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:
|
||||
- /api/v1/customer-service-contacts
|
||||
strip_path: false
|
||||
- name: admin-tree-pricing-public
|
||||
paths:
|
||||
- /api/v1/tree-pricing
|
||||
strip_path: false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Presence Service - 在线状态服务
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export class AdminTreePricingController {
|
|||
* 不需要管理员认证
|
||||
*/
|
||||
@ApiTags('认种定价配置-公开API')
|
||||
@Controller('api/v1/tree-pricing')
|
||||
@Controller('tree-pricing')
|
||||
export class PublicTreePricingController {
|
||||
constructor(
|
||||
private readonly pricingService: TreePricingService,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
|
@ -47,6 +48,12 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
|||
/// 是否显示超出警告
|
||||
bool _showExceedWarning = false;
|
||||
|
||||
/// 涨价倒计时 timer
|
||||
Timer? _priceCountdownTimer;
|
||||
|
||||
/// 距下次涨价的剩余时间
|
||||
Duration _timeUntilIncrease = Duration.zero;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -65,6 +72,7 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
|||
_pricingConfig = config;
|
||||
_pricePerTree = config.totalPrice.toDouble();
|
||||
});
|
||||
_startPriceCountdown();
|
||||
debugPrint('[PlantingQuantity] 动态定价: ${config.totalPrice} USDT/棵 (supplement=${config.currentSupplement})');
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -75,12 +83,40 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_priceCountdownTimer?.cancel();
|
||||
_quantityController.removeListener(_onQuantityChanged);
|
||||
_quantityController.dispose();
|
||||
_quantityFocusNode.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() {
|
||||
final text = _quantityController.text;
|
||||
|
|
@ -707,35 +743,68 @@ class _PlantingQuantityPageState extends ConsumerState<PlantingQuantityPage> {
|
|||
color: Color(0xFF000000),
|
||||
),
|
||||
),
|
||||
// 下次涨价预告
|
||||
// 下次涨价倒计时预告
|
||||
if (_pricingConfig != null &&
|
||||
_pricingConfig!.autoIncreaseEnabled &&
|
||||
_pricingConfig!.nextAutoIncreaseAt != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF3E0),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: const Color(0xFFFFCC80), width: 0.5),
|
||||
),
|
||||
child: Row(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.trending_up,
|
||||
color: Color(0xFFE65100),
|
||||
size: 18,
|
||||
),
|
||||
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,
|
||||
// 倒计时行
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.timer_outlined,
|
||||
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