feat(mobile): integrate PlantingService with real API
- Add PlantingService for planting order management - createOrder: Create new planting order - selectProvinceCity: Select province and city - confirmProvinceCity: Confirm province/city selection - payOrder: Pay for order - getOrder/getMyOrders: Query orders - cancelOrder: Cancel unpaid orders - Register PlantingService in DI container - Update planting_quantity_page to create order before navigation - Update planting_location_page to call real API endpoints - Fix referral-service event-ack.publisher to use KafkaService 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ba5b6141a3
commit
dfa21c0280
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, Logger, Inject } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { ClientKafka } from '@nestjs/microservices';
|
import { KafkaService } from '../messaging/kafka.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件确认消息结构
|
* 事件确认消息结构
|
||||||
|
|
@ -30,10 +30,7 @@ export class EventAckPublisher {
|
||||||
private readonly logger = new Logger(EventAckPublisher.name);
|
private readonly logger = new Logger(EventAckPublisher.name);
|
||||||
private readonly serviceName = 'referral-service';
|
private readonly serviceName = 'referral-service';
|
||||||
|
|
||||||
constructor(
|
constructor(private readonly kafkaService: KafkaService) {}
|
||||||
@Inject('KAFKA_SERVICE')
|
|
||||||
private readonly kafkaClient: ClientKafka,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送处理成功确认
|
* 发送处理成功确认
|
||||||
|
|
@ -48,9 +45,10 @@ export class EventAckPublisher {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.kafkaClient.emit('planting.events.ack', {
|
await this.kafkaService.publish({
|
||||||
|
topic: 'planting.events.ack',
|
||||||
key: eventId,
|
key: eventId,
|
||||||
value: JSON.stringify(ackMessage),
|
value: ackMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.log(`[ACK] ✓ Sent success confirmation for event ${eventId} (${eventType})`);
|
this.logger.log(`[ACK] ✓ Sent success confirmation for event ${eventId} (${eventType})`);
|
||||||
|
|
@ -73,9 +71,10 @@ export class EventAckPublisher {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.kafkaClient.emit('planting.events.ack', {
|
await this.kafkaService.publish({
|
||||||
|
topic: 'planting.events.ack',
|
||||||
key: eventId,
|
key: eventId,
|
||||||
value: JSON.stringify(ackMessage),
|
value: ackMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.warn(`[ACK] ✗ Sent failure confirmation for event ${eventId}: ${errorMessage}`);
|
this.logger.warn(`[ACK] ✗ Sent failure confirmation for event ${eventId}: ${errorMessage}`);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import '../services/referral_service.dart';
|
||||||
import '../services/authorization_service.dart';
|
import '../services/authorization_service.dart';
|
||||||
import '../services/deposit_service.dart';
|
import '../services/deposit_service.dart';
|
||||||
import '../services/wallet_service.dart';
|
import '../services/wallet_service.dart';
|
||||||
|
import '../services/planting_service.dart';
|
||||||
|
|
||||||
// Storage Providers
|
// Storage Providers
|
||||||
final secureStorageProvider = Provider<SecureStorage>((ref) {
|
final secureStorageProvider = Provider<SecureStorage>((ref) {
|
||||||
|
|
@ -58,6 +59,12 @@ final walletServiceProvider = Provider<WalletService>((ref) {
|
||||||
return WalletService(apiClient: apiClient);
|
return WalletService(apiClient: apiClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Planting Service Provider
|
||||||
|
final plantingServiceProvider = Provider<PlantingService>((ref) {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
return PlantingService(apiClient: apiClient);
|
||||||
|
});
|
||||||
|
|
||||||
// Override provider with initialized instance
|
// Override provider with initialized instance
|
||||||
ProviderContainer createProviderContainer(LocalStorage localStorage) {
|
ProviderContainer createProviderContainer(LocalStorage localStorage) {
|
||||||
return ProviderContainer(
|
return ProviderContainer(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,299 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import '../network/api_client.dart';
|
||||||
|
|
||||||
|
/// 认种订单状态
|
||||||
|
enum PlantingOrderStatus {
|
||||||
|
created, // 已创建
|
||||||
|
provinceCitySelected, // 已选择省市
|
||||||
|
provinceCityConfirmed, // 已确认省市
|
||||||
|
paid, // 已支付
|
||||||
|
fundAllocated, // 资金已分配
|
||||||
|
cancelled, // 已取消
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 认种订单响应
|
||||||
|
class PlantingOrder {
|
||||||
|
final String orderNo;
|
||||||
|
final int treeCount;
|
||||||
|
final double totalAmount;
|
||||||
|
final PlantingOrderStatus status;
|
||||||
|
final String? selectedProvince;
|
||||||
|
final String? selectedCity;
|
||||||
|
final DateTime? provinceCitySelectedAt;
|
||||||
|
final DateTime? provinceCityConfirmedAt;
|
||||||
|
final DateTime? paidAt;
|
||||||
|
final bool isMiningEnabled;
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
PlantingOrder({
|
||||||
|
required this.orderNo,
|
||||||
|
required this.treeCount,
|
||||||
|
required this.totalAmount,
|
||||||
|
required this.status,
|
||||||
|
this.selectedProvince,
|
||||||
|
this.selectedCity,
|
||||||
|
this.provinceCitySelectedAt,
|
||||||
|
this.provinceCityConfirmedAt,
|
||||||
|
this.paidAt,
|
||||||
|
required this.isMiningEnabled,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PlantingOrder.fromJson(Map<String, dynamic> json) {
|
||||||
|
return PlantingOrder(
|
||||||
|
orderNo: json['orderNo'] ?? '',
|
||||||
|
treeCount: json['treeCount'] ?? 0,
|
||||||
|
totalAmount: (json['totalAmount'] ?? 0).toDouble(),
|
||||||
|
status: _parseStatus(json['status']),
|
||||||
|
selectedProvince: json['selectedProvince'],
|
||||||
|
selectedCity: json['selectedCity'],
|
||||||
|
provinceCitySelectedAt: json['provinceCitySelectedAt'] != null
|
||||||
|
? DateTime.parse(json['provinceCitySelectedAt'])
|
||||||
|
: null,
|
||||||
|
provinceCityConfirmedAt: json['provinceCityConfirmedAt'] != null
|
||||||
|
? DateTime.parse(json['provinceCityConfirmedAt'])
|
||||||
|
: null,
|
||||||
|
paidAt: json['paidAt'] != null ? DateTime.parse(json['paidAt']) : null,
|
||||||
|
isMiningEnabled: json['isMiningEnabled'] ?? false,
|
||||||
|
createdAt: json['createdAt'] != null
|
||||||
|
? DateTime.parse(json['createdAt'])
|
||||||
|
: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PlantingOrderStatus _parseStatus(String? status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'CREATED':
|
||||||
|
return PlantingOrderStatus.created;
|
||||||
|
case 'PROVINCE_CITY_SELECTED':
|
||||||
|
return PlantingOrderStatus.provinceCitySelected;
|
||||||
|
case 'PROVINCE_CITY_CONFIRMED':
|
||||||
|
return PlantingOrderStatus.provinceCityConfirmed;
|
||||||
|
case 'PAID':
|
||||||
|
return PlantingOrderStatus.paid;
|
||||||
|
case 'FUND_ALLOCATED':
|
||||||
|
return PlantingOrderStatus.fundAllocated;
|
||||||
|
case 'CANCELLED':
|
||||||
|
return PlantingOrderStatus.cancelled;
|
||||||
|
default:
|
||||||
|
return PlantingOrderStatus.created;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建订单响应
|
||||||
|
class CreateOrderResponse {
|
||||||
|
final String orderNo;
|
||||||
|
final int treeCount;
|
||||||
|
final double totalAmount;
|
||||||
|
final double pricePerTree;
|
||||||
|
|
||||||
|
CreateOrderResponse({
|
||||||
|
required this.orderNo,
|
||||||
|
required this.treeCount,
|
||||||
|
required this.totalAmount,
|
||||||
|
required this.pricePerTree,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CreateOrderResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return CreateOrderResponse(
|
||||||
|
orderNo: json['orderNo'] ?? '',
|
||||||
|
treeCount: json['treeCount'] ?? 0,
|
||||||
|
totalAmount: (json['totalAmount'] ?? 0).toDouble(),
|
||||||
|
pricePerTree: (json['pricePerTree'] ?? 2199).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 认种服务
|
||||||
|
///
|
||||||
|
/// 提供认种订单创建、省市选择、支付等功能
|
||||||
|
class PlantingService {
|
||||||
|
final ApiClient _apiClient;
|
||||||
|
|
||||||
|
PlantingService({required ApiClient apiClient}) : _apiClient = apiClient;
|
||||||
|
|
||||||
|
/// 创建认种订单
|
||||||
|
///
|
||||||
|
/// [treeCount] 认种数量
|
||||||
|
/// 返回订单号和总金额
|
||||||
|
Future<CreateOrderResponse> createOrder(int treeCount) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 创建认种订单: treeCount=$treeCount');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/orders',
|
||||||
|
data: {'treeCount': treeCount},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
debugPrint('[PlantingService] 订单创建成功: ${data['orderNo']}');
|
||||||
|
return CreateOrderResponse.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('创建订单失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 创建订单失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 选择省市
|
||||||
|
///
|
||||||
|
/// [orderNo] 订单号
|
||||||
|
/// [provinceCode] 省份代码
|
||||||
|
/// [cityCode] 城市代码
|
||||||
|
Future<PlantingOrder> selectProvinceCity(
|
||||||
|
String orderNo,
|
||||||
|
String provinceCode,
|
||||||
|
String cityCode,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 选择省市: orderNo=$orderNo, province=$provinceCode, city=$cityCode');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/orders/$orderNo/select-province-city',
|
||||||
|
data: {
|
||||||
|
'provinceCode': provinceCode,
|
||||||
|
'cityCode': cityCode,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
debugPrint('[PlantingService] 省市选择成功');
|
||||||
|
return PlantingOrder.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('选择省市失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 选择省市失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 确认省市选择
|
||||||
|
///
|
||||||
|
/// [orderNo] 订单号
|
||||||
|
/// 确认后不可修改省市
|
||||||
|
Future<PlantingOrder> confirmProvinceCity(String orderNo) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 确认省市选择: orderNo=$orderNo');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/orders/$orderNo/confirm-province-city',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
debugPrint('[PlantingService] 省市确认成功');
|
||||||
|
return PlantingOrder.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('确认省市失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 确认省市失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 支付订单
|
||||||
|
///
|
||||||
|
/// [orderNo] 订单号
|
||||||
|
/// 支付成功后开始挖矿
|
||||||
|
Future<PlantingOrder> payOrder(String orderNo) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 支付订单: orderNo=$orderNo');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/orders/$orderNo/pay',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
debugPrint('[PlantingService] 支付成功');
|
||||||
|
return PlantingOrder.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('支付失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 支付失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取订单详情
|
||||||
|
///
|
||||||
|
/// [orderNo] 订单号
|
||||||
|
Future<PlantingOrder> getOrder(String orderNo) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 获取订单详情: orderNo=$orderNo');
|
||||||
|
|
||||||
|
final response = await _apiClient.get('/planting/orders/$orderNo');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
return PlantingOrder.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('获取订单失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 获取订单失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取我的订单列表
|
||||||
|
///
|
||||||
|
/// [page] 页码
|
||||||
|
/// [pageSize] 每页数量
|
||||||
|
Future<List<PlantingOrder>> getMyOrders({int page = 1, int pageSize = 20}) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 获取我的订单列表: page=$page, pageSize=$pageSize');
|
||||||
|
|
||||||
|
final response = await _apiClient.get(
|
||||||
|
'/planting/orders',
|
||||||
|
queryParameters: {
|
||||||
|
'page': page,
|
||||||
|
'pageSize': pageSize,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
final items = data['items'] as List<dynamic>? ?? [];
|
||||||
|
return items.map((e) => PlantingOrder.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('获取订单列表失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 获取订单列表失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 取消订单
|
||||||
|
///
|
||||||
|
/// [orderNo] 订单号
|
||||||
|
/// 只有未支付的订单可以取消
|
||||||
|
Future<void> cancelOrder(String orderNo) async {
|
||||||
|
try {
|
||||||
|
debugPrint('[PlantingService] 取消订单: orderNo=$orderNo');
|
||||||
|
|
||||||
|
final response = await _apiClient.post(
|
||||||
|
'/planting/orders/$orderNo/cancel',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
debugPrint('[PlantingService] 订单取消成功');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('取消订单失败: ${response.statusCode}');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingService] 取消订单失败: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,15 +3,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:city_pickers/city_pickers.dart';
|
import 'package:city_pickers/city_pickers.dart';
|
||||||
import '../widgets/planting_confirm_dialog.dart';
|
import '../widgets/planting_confirm_dialog.dart';
|
||||||
|
import '../../../../core/di/injection_container.dart';
|
||||||
|
|
||||||
/// 认种省市选择页面参数
|
/// 认种省市选择页面参数
|
||||||
class PlantingLocationParams {
|
class PlantingLocationParams {
|
||||||
final int quantity;
|
final int quantity;
|
||||||
final double totalPrice;
|
final double totalPrice;
|
||||||
|
final String orderNo; // 订单号
|
||||||
|
|
||||||
PlantingLocationParams({
|
PlantingLocationParams({
|
||||||
required this.quantity,
|
required this.quantity,
|
||||||
required this.totalPrice,
|
required this.totalPrice,
|
||||||
|
required this.orderNo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,11 +23,13 @@ class PlantingLocationParams {
|
||||||
class PlantingLocationPage extends ConsumerStatefulWidget {
|
class PlantingLocationPage extends ConsumerStatefulWidget {
|
||||||
final int quantity;
|
final int quantity;
|
||||||
final double totalPrice;
|
final double totalPrice;
|
||||||
|
final String orderNo;
|
||||||
|
|
||||||
const PlantingLocationPage({
|
const PlantingLocationPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.quantity,
|
required this.quantity,
|
||||||
required this.totalPrice,
|
required this.totalPrice,
|
||||||
|
required this.orderNo,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -33,11 +38,17 @@ class PlantingLocationPage extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
/// 选中的省份
|
/// 选中的省份名称
|
||||||
String? _selectedProvince;
|
String? _selectedProvinceName;
|
||||||
|
|
||||||
/// 选中的城市
|
/// 选中的城市名称
|
||||||
String? _selectedCity;
|
String? _selectedCityName;
|
||||||
|
|
||||||
|
/// 选中的省份代码
|
||||||
|
String? _selectedProvinceCode;
|
||||||
|
|
||||||
|
/// 选中的城市代码
|
||||||
|
String? _selectedCityCode;
|
||||||
|
|
||||||
/// 是否正在提交
|
/// 是否正在提交
|
||||||
bool _isSubmitting = false;
|
bool _isSubmitting = false;
|
||||||
|
|
@ -66,8 +77,10 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedProvince = result.provinceName;
|
_selectedProvinceName = result.provinceName;
|
||||||
_selectedCity = result.cityName;
|
_selectedCityName = result.cityName;
|
||||||
|
_selectedProvinceCode = result.provinceId;
|
||||||
|
_selectedCityCode = result.cityId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +97,7 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
|
|
||||||
/// 确认选择
|
/// 确认选择
|
||||||
void _confirmSelection() async {
|
void _confirmSelection() async {
|
||||||
if (_selectedProvince == null || _selectedCity == null) {
|
if (_selectedProvinceName == null || _selectedCityName == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('请选择省份和城市'),
|
content: Text('请选择省份和城市'),
|
||||||
|
|
@ -97,8 +110,8 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
// 显示确认弹窗(带5秒倒计时)
|
// 显示确认弹窗(带5秒倒计时)
|
||||||
await PlantingConfirmDialog.show(
|
await PlantingConfirmDialog.show(
|
||||||
context: context,
|
context: context,
|
||||||
province: _selectedProvince!,
|
province: _selectedProvinceName!,
|
||||||
city: _selectedCity!,
|
city: _selectedCityName!,
|
||||||
onConfirm: _submitPlanting,
|
onConfirm: _submitPlanting,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -108,8 +121,20 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
setState(() => _isSubmitting = true);
|
setState(() => _isSubmitting = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: 调用 API 提交认种请求
|
final plantingService = ref.read(plantingServiceProvider);
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
|
// 1. 选择省市
|
||||||
|
await plantingService.selectProvinceCity(
|
||||||
|
widget.orderNo,
|
||||||
|
_selectedProvinceCode!,
|
||||||
|
_selectedCityCode!,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 确认省市选择
|
||||||
|
await plantingService.confirmProvinceCity(widget.orderNo);
|
||||||
|
|
||||||
|
// 3. 支付订单
|
||||||
|
await plantingService.payOrder(widget.orderNo);
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
// 显示成功提示
|
// 显示成功提示
|
||||||
|
|
@ -142,7 +167,7 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
|
|
||||||
/// 是否可以提交
|
/// 是否可以提交
|
||||||
bool get _canSubmit =>
|
bool get _canSubmit =>
|
||||||
_selectedProvince != null && _selectedCity != null && !_isSubmitting;
|
_selectedProvinceName != null && _selectedCityName != null && !_isSubmitting;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -305,12 +330,12 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_selectedProvince ?? '选择省份',
|
_selectedProvinceName ?? '选择省份',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
color: _selectedProvince != null
|
color: _selectedProvinceName != null
|
||||||
? const Color(0xFF5D4037)
|
? const Color(0xFF5D4037)
|
||||||
: const Color(0xFF5D4037).withValues(alpha: 0.5),
|
: const Color(0xFF5D4037).withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
|
|
@ -363,12 +388,12 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_selectedCity ?? '选择市级',
|
_selectedCityName ?? '选择市级',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
color: _selectedCity != null
|
color: _selectedCityName != null
|
||||||
? const Color(0xFF5D4037)
|
? const Color(0xFF5D4037)
|
||||||
: const Color(0xFF5D4037).withValues(alpha: 0.5),
|
: const Color(0xFF5D4037).withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
/// 是否正在加载
|
/// 是否正在加载
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
/// 是否正在创建订单
|
||||||
|
bool _isCreatingOrder = false;
|
||||||
|
|
||||||
/// 加载错误信息
|
/// 加载错误信息
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
|
|
||||||
|
|
@ -108,16 +111,44 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
context.pop();
|
context.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 下一步:选择省市
|
/// 下一步:创建订单并选择省市
|
||||||
void _goToNextStep() {
|
Future<void> _goToNextStep() async {
|
||||||
if (_quantity > 0 && _quantity <= _maxQuantity) {
|
if (_quantity <= 0 || _quantity > _maxQuantity || _isCreatingOrder) {
|
||||||
context.push(
|
return;
|
||||||
RoutePaths.plantingLocation,
|
}
|
||||||
extra: PlantingLocationParams(
|
|
||||||
quantity: _quantity,
|
setState(() => _isCreatingOrder = true);
|
||||||
totalPrice: _quantity * _pricePerTree,
|
|
||||||
),
|
try {
|
||||||
);
|
// 1. 调用 API 创建订单
|
||||||
|
final plantingService = ref.read(plantingServiceProvider);
|
||||||
|
final response = await plantingService.createOrder(_quantity);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
// 2. 跳转到省市选择页面,传递订单号
|
||||||
|
context.push(
|
||||||
|
RoutePaths.plantingLocation,
|
||||||
|
extra: PlantingLocationParams(
|
||||||
|
quantity: _quantity,
|
||||||
|
totalPrice: _quantity * _pricePerTree,
|
||||||
|
orderNo: response.orderNo,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('创建订单失败: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('创建订单失败: $e'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isCreatingOrder = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -518,7 +549,7 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
|
|
||||||
/// 构建底部按钮
|
/// 构建底部按钮
|
||||||
Widget _buildBottomButton() {
|
Widget _buildBottomButton() {
|
||||||
final bool canProceed = _quantity > 0 && _quantity <= _maxQuantity && !_isLoading;
|
final bool canProceed = _quantity > 0 && _quantity <= _maxQuantity && !_isLoading && !_isCreatingOrder;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
|
||||||
|
|
@ -547,17 +578,26 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: Text(
|
child: _isCreatingOrder
|
||||||
'下一步:选择省市',
|
? const SizedBox(
|
||||||
style: TextStyle(
|
width: 24,
|
||||||
fontSize: 18,
|
height: 24,
|
||||||
fontFamily: 'Inter',
|
child: CircularProgressIndicator(
|
||||||
fontWeight: FontWeight.w500,
|
strokeWidth: 2,
|
||||||
height: 1.56,
|
color: Colors.white,
|
||||||
color: Colors.white,
|
),
|
||||||
),
|
)
|
||||||
),
|
: const Text(
|
||||||
|
'下一步:选择省市',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 1.56,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue