refactor(transfer): 移除发起转让页的售价字段 — 线下柜台签合同交易
APP 端树转让仅完成系统层面的所有权变更 + 算力调整(撤回旧用户算力、 新增至新用户及其团队),价格由用户线下到柜台签署合同确定。 变更详情: - transfer_initiate_page.dart: · 移除 _priceController、_feeRate、费用相关 getter · 移除"每棵售价"输入框和费用计算卡片 UI · 移除 _feeRow 辅助方法 · 更新说明文案:强调线下柜台签合同 · 更新确认弹窗:仅显示买方账号和转让棵数 · API 调用改用 treeCount 替代 pricePerTree - transfer_service.dart: · createTransfer() 参数从 pricePerTree(double) 改为 treeCount(int) · 请求体字段同步调整 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0e34896d0b
commit
6efa74aded
|
|
@ -236,21 +236,21 @@ class TransferService {
|
|||
/// 发起转让
|
||||
///
|
||||
/// [buyerAccountSequence] 买方账号
|
||||
/// [plantingOrderNos] 要转让的认种订单号列表
|
||||
/// [pricePerTree] 每棵售价
|
||||
/// [plantingOrderNos] 要转让的认种订单号列表(空列表时由后端自动选择)
|
||||
/// [treeCount] 转让棵数
|
||||
Future<TransferOrder> createTransfer({
|
||||
required String buyerAccountSequence,
|
||||
required List<String> plantingOrderNos,
|
||||
required double pricePerTree,
|
||||
required int treeCount,
|
||||
}) async {
|
||||
try {
|
||||
debugPrint('[TransferService] 发起转让: buyer=$buyerAccountSequence, trees=${plantingOrderNos.length}');
|
||||
debugPrint('[TransferService] 发起转让: buyer=$buyerAccountSequence, treeCount=$treeCount');
|
||||
final response = await _apiClient.post(
|
||||
'/transfers',
|
||||
data: {
|
||||
'buyerAccountSequence': buyerAccountSequence,
|
||||
'plantingOrderNos': plantingOrderNos,
|
||||
'pricePerTree': pricePerTree.toString(),
|
||||
'treeCount': treeCount,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import '../../../../routes/route_paths.dart';
|
|||
//
|
||||
// 用户发起树转让的表单页面:
|
||||
// - 输入买方账号
|
||||
// - 输入每棵售价
|
||||
// - 自动计算手续费(5%)和卖方到账金额
|
||||
// - 输入转让棵数
|
||||
// - 确认发起转让
|
||||
// 注:价格线下柜台签合同交易,APP 仅完成系统层面所有权 + 算力转移
|
||||
//
|
||||
// === 回滚方式 ===
|
||||
// 删除 features/transfer/ 目录
|
||||
|
|
@ -28,27 +28,18 @@ class TransferInitiatePage extends ConsumerStatefulWidget {
|
|||
|
||||
class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
||||
final _buyerController = TextEditingController();
|
||||
final _priceController = TextEditingController();
|
||||
final _treeCountController = TextEditingController(text: '1');
|
||||
bool _isSubmitting = false;
|
||||
String? _errorMessage;
|
||||
|
||||
// 平台手续费率
|
||||
static const double _feeRate = 0.05;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_buyerController.dispose();
|
||||
_priceController.dispose();
|
||||
_treeCountController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
double get _pricePerTree => double.tryParse(_priceController.text) ?? 0;
|
||||
int get _treeCount => int.tryParse(_treeCountController.text) ?? 1;
|
||||
double get _totalPrice => _pricePerTree * _treeCount;
|
||||
double get _feeAmount => _totalPrice * _feeRate;
|
||||
double get _sellerReceive => _totalPrice - _feeAmount;
|
||||
|
||||
Future<void> _handleSubmit() async {
|
||||
final buyer = _buyerController.text.trim();
|
||||
|
|
@ -56,10 +47,6 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
setState(() => _errorMessage = '请输入买方账号');
|
||||
return;
|
||||
}
|
||||
if (_pricePerTree <= 0) {
|
||||
setState(() => _errorMessage = '请输入有效的每棵售价');
|
||||
return;
|
||||
}
|
||||
if (_treeCount <= 0) {
|
||||
setState(() => _errorMessage = '请输入有效的转让棵数');
|
||||
return;
|
||||
|
|
@ -77,12 +64,9 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
),
|
||||
content: Text(
|
||||
'买方账号: $buyer\n'
|
||||
'转让棵数: $_treeCount 棵\n'
|
||||
'每棵售价: ${_pricePerTree.toStringAsFixed(0)} USDT\n'
|
||||
'总价: ${_totalPrice.toStringAsFixed(0)} USDT\n'
|
||||
'手续费: ${_feeAmount.toStringAsFixed(2)} USDT (${(_feeRate * 100).toStringAsFixed(0)}%)\n'
|
||||
'您将收到: ${_sellerReceive.toStringAsFixed(2)} USDT\n\n'
|
||||
'转让发起后需要卖方确认,确认后 Saga 流程将自动执行。',
|
||||
'转让棵数: $_treeCount 棵\n\n'
|
||||
'确认后系统将完成树所有权变更及算力调整。\n'
|
||||
'交易价格请线下柜台签署合同确定。',
|
||||
style: const TextStyle(color: Color(0xFF5D4037), fontSize: 14, height: 1.6),
|
||||
),
|
||||
actions: [
|
||||
|
|
@ -115,7 +99,7 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
final order = await service.createTransfer(
|
||||
buyerAccountSequence: buyer,
|
||||
plantingOrderNos: [], // 由后端根据棵数自动选择
|
||||
pricePerTree: _pricePerTree,
|
||||
treeCount: _treeCount,
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
|
|
@ -178,7 +162,7 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'转让已认种的果树所有权。发起后需卖方确认,系统将自动完成资金冻结、所有权变更、算力调整等流程。',
|
||||
'转让已认种的果树所有权。系统将自动完成所有权变更及算力调整,交易价格请线下柜台签署合同确定。',
|
||||
style: TextStyle(fontSize: 13, color: Color(0xFF8B5A2B)),
|
||||
),
|
||||
),
|
||||
|
|
@ -207,48 +191,8 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// 每棵售价
|
||||
_buildLabel('每棵售价 (USDT)'),
|
||||
const SizedBox(height: 8),
|
||||
_buildTextField(
|
||||
controller: _priceController,
|
||||
hintText: '输入每棵售价',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[\d.]'))],
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 费用计算卡片
|
||||
if (_pricePerTree > 0) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x99FFFFFF),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x1A000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_feeRow('总价', '${_totalPrice.toStringAsFixed(0)} USDT'),
|
||||
_feeRow('手续费 (${(_feeRate * 100).toStringAsFixed(0)}%)', '${_feeAmount.toStringAsFixed(2)} USDT'),
|
||||
const Divider(color: Color(0xFFEAE0C8)),
|
||||
_feeRow('您将收到', '${_sellerReceive.toStringAsFixed(2)} USDT',
|
||||
valueColor: const Color(0xFFD4AF37)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
|
||||
// 错误信息
|
||||
if (_errorMessage != null) ...[
|
||||
Container(
|
||||
|
|
@ -429,24 +373,4 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _feeRow(String label, String value, {Color? valueColor}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF745D43))),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: valueColor ?? const Color(0xFF5D4037),
|
||||
fontFamily: 'Inter',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue