fix(planting): 修复认种页面动态定价不生效 + 添加涨价倒计时
- 修复 admin-service PublicTreePricingController 路由双重前缀问题
(@Controller('api/v1/tree-pricing') → @Controller('tree-pricing'))
- Kong 网关新增 /api/v1/tree-pricing 路由到 admin-service
- mobile-app 认种页面添加涨价倒计时功能:
显示"距下次涨价还有 X天 X小时 X分钟"及涨价后价格
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
19f350b8e3
commit
3a307b5db7
|
|
@ -240,6 +240,10 @@ services:
|
||||||
paths:
|
paths:
|
||||||
- /api/v1/customer-service-contacts
|
- /api/v1/customer-service-contacts
|
||||||
strip_path: false
|
strip_path: false
|
||||||
|
- name: admin-tree-pricing-public
|
||||||
|
paths:
|
||||||
|
- /api/v1/tree-pricing
|
||||||
|
strip_path: false
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Presence Service - 在线状态服务
|
# Presence Service - 在线状态服务
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export class AdminTreePricingController {
|
||||||
* 不需要管理员认证
|
* 不需要管理员认证
|
||||||
*/
|
*/
|
||||||
@ApiTags('认种定价配置-公开API')
|
@ApiTags('认种定价配置-公开API')
|
||||||
@Controller('api/v1/tree-pricing')
|
@Controller('tree-pricing')
|
||||||
export class PublicTreePricingController {
|
export class PublicTreePricingController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly pricingService: TreePricingService,
|
private readonly pricingService: TreePricingService,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
@ -47,6 +48,12 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
/// 是否显示超出警告
|
/// 是否显示超出警告
|
||||||
bool _showExceedWarning = false;
|
bool _showExceedWarning = false;
|
||||||
|
|
||||||
|
/// 涨价倒计时 timer
|
||||||
|
Timer? _priceCountdownTimer;
|
||||||
|
|
||||||
|
/// 距下次涨价的剩余时间
|
||||||
|
Duration _timeUntilIncrease = Duration.zero;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -65,6 +72,7 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
_pricingConfig = config;
|
_pricingConfig = config;
|
||||||
_pricePerTree = config.totalPrice.toDouble();
|
_pricePerTree = config.totalPrice.toDouble();
|
||||||
});
|
});
|
||||||
|
_startPriceCountdown();
|
||||||
debugPrint('[PlantingQuantity] 动态定价: ${config.totalPrice} USDT/棵 (supplement=${config.currentSupplement})');
|
debugPrint('[PlantingQuantity] 动态定价: ${config.totalPrice} USDT/棵 (supplement=${config.currentSupplement})');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -75,12 +83,40 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_priceCountdownTimer?.cancel();
|
||||||
_quantityController.removeListener(_onQuantityChanged);
|
_quantityController.removeListener(_onQuantityChanged);
|
||||||
_quantityController.dispose();
|
_quantityController.dispose();
|
||||||
_quantityFocusNode.dispose();
|
_quantityFocusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 启动涨价倒计时
|
||||||
|
void _startPriceCountdown() {
|
||||||
|
_priceCountdownTimer?.cancel();
|
||||||
|
final config = _pricingConfig;
|
||||||
|
if (config == null || !config.autoIncreaseEnabled || config.nextAutoIncreaseAt == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_updateCountdown();
|
||||||
|
_priceCountdownTimer = Timer.periodic(const Duration(minutes: 1), (_) {
|
||||||
|
_updateCountdown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateCountdown() {
|
||||||
|
final nextAt = _pricingConfig?.nextAutoIncreaseAt;
|
||||||
|
if (nextAt == null) return;
|
||||||
|
final remaining = nextAt.difference(DateTime.now());
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_timeUntilIncrease = remaining.isNegative ? Duration.zero : remaining;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (remaining.isNegative) {
|
||||||
|
_priceCountdownTimer?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 输入框数量变化监听
|
/// 输入框数量变化监听
|
||||||
void _onQuantityChanged() {
|
void _onQuantityChanged() {
|
||||||
final text = _quantityController.text;
|
final text = _quantityController.text;
|
||||||
|
|
@ -707,35 +743,68 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
||||||
color: Color(0xFF000000),
|
color: Color(0xFF000000),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 下次涨价预告
|
// 下次涨价倒计时预告
|
||||||
if (_pricingConfig != null &&
|
if (_pricingConfig != null &&
|
||||||
_pricingConfig!.autoIncreaseEnabled &&
|
_pricingConfig!.autoIncreaseEnabled &&
|
||||||
_pricingConfig!.nextAutoIncreaseAt != null) ...[
|
_pricingConfig!.nextAutoIncreaseAt != null) ...[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFFF3E0),
|
color: const Color(0xFFFFF3E0),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: const Color(0xFFFFCC80), width: 0.5),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
// 倒计时行
|
||||||
Icons.trending_up,
|
Row(
|
||||||
color: Color(0xFFE65100),
|
children: [
|
||||||
size: 18,
|
const Icon(
|
||||||
),
|
Icons.timer_outlined,
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
'预计涨价: ${_pricingConfig!.nextAutoIncreaseAt!.month}月${_pricingConfig!.nextAutoIncreaseAt!.day}日后每棵 +${_pricingConfig!.autoIncreaseAmount} 绿积分',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontFamily: 'Inter',
|
|
||||||
height: 1.4,
|
|
||||||
color: Color(0xFFE65100),
|
color: Color(0xFFE65100),
|
||||||
|
size: 18,
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_timeUntilIncrease == Duration.zero
|
||||||
|
? '涨价时间已到,价格即将更新'
|
||||||
|
: '距下次涨价还有 ${_timeUntilIncrease.inDays}天 ${_timeUntilIncrease.inHours % 24}小时 ${_timeUntilIncrease.inMinutes % 60}分钟',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
height: 1.4,
|
||||||
|
color: Color(0xFFE65100),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
// 涨价后价格
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.trending_up,
|
||||||
|
color: Color(0xFFE65100),
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'涨价后每棵: ${(_pricingConfig!.totalPrice + _pricingConfig!.autoIncreaseAmount).toInt()} 绿积分 (+${_pricingConfig!.autoIncreaseAmount.toInt()})',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
height: 1.4,
|
||||||
|
color: Color(0xFFBF360C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue