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:
hailin 2026-03-01 20:06:31 -08:00
parent 0e34896d0b
commit 6efa74aded
2 changed files with 12 additions and 88 deletions

View File

@ -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,
},
);

View File

@ -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',
),
),
],
),
);
}
}