feat(c2c): 卖单自动获取账户ID和Kava地址,移除手动输入

后端创建卖单时自动从 identity-service 获取卖家 Kava 地址并存入订单,
前端发布页面自动展示 accountSequence(只读),不再需要手动输入1.0系统ID。

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-29 11:08:56 -08:00
parent 2b7a30983e
commit 263f1ecf8e
7 changed files with 31 additions and 45 deletions

View File

@ -57,6 +57,8 @@ export class C2cController {
paymentAccount: order.paymentAccount || undefined,
paymentQrCode: order.paymentQrCode || undefined,
paymentRealName: order.paymentRealName || undefined,
// 卖家 Kava 地址
sellerKavaAddress: order.sellerKavaAddress || undefined,
// 超时信息
paymentTimeoutMinutes: order.paymentTimeoutMinutes,
confirmTimeoutMinutes: order.confirmTimeoutMinutes,

View File

@ -168,6 +168,8 @@ export class C2cOrderResponseDto {
paymentAccount?: string;
paymentQrCode?: string;
paymentRealName?: string;
// 卖家 Kava 地址(绿积分转账地址)
sellerKavaAddress?: string;
// 超时信息
paymentTimeoutMinutes: number;
confirmTimeoutMinutes: number;

View File

@ -3,6 +3,7 @@ import { C2cOrderRepository, C2cOrderEntity } from '../../infrastructure/persist
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
import { RedisService } from '../../infrastructure/redis/redis.service';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { IdentityClient } from '../../infrastructure/identity/identity.client';
import { Money } from '../../domain/value-objects/money.vo';
import Decimal from 'decimal.js';
@ -57,6 +58,7 @@ export class C2cService {
private readonly tradingAccountRepository: TradingAccountRepository,
private readonly redis: RedisService,
private readonly prisma: PrismaService,
private readonly identityClient: IdentityClient,
) {}
/**
@ -109,16 +111,12 @@ export class C2cService {
throw new BadRequestException('数量必须大于0');
}
// 卖单必须提供收款信息
// 卖单:自动获取卖家 Kava 地址(用于绿积分转账)
let sellerKavaAddress: string | null = null;
if (type === C2C_ORDER_TYPE.SELL) {
if (!options?.paymentMethod) {
throw new BadRequestException('卖单必须提供收款方式');
}
if (!options?.paymentAccount) {
throw new BadRequestException('卖单必须提供收款账号');
}
if (!options?.paymentRealName) {
throw new BadRequestException('卖单必须提供收款人实名');
sellerKavaAddress = await this.identityClient.getUserKavaAddress(accountSequence);
if (!sellerKavaAddress) {
throw new BadRequestException('未找到您的 Kava 钱包地址,请先绑定钱包');
}
}
@ -169,6 +167,8 @@ export class C2cService {
paymentAccount: options?.paymentAccount,
paymentQrCode: options?.paymentQrCode,
paymentRealName: options?.paymentRealName,
// 卖家 Kava 地址(卖单时自动获取)
sellerKavaAddress,
remark: options?.remark,
});

View File

@ -111,6 +111,8 @@ export class C2cOrderRepository {
paymentAccount?: string;
paymentQrCode?: string;
paymentRealName?: string;
// 卖家 Kava 地址
sellerKavaAddress?: string | null;
remark?: string;
}): Promise<C2cOrderEntity> {
const record = await this.prisma.c2cOrder.create({
@ -132,6 +134,8 @@ export class C2cOrderRepository {
paymentAccount: data.paymentAccount,
paymentQrCode: data.paymentQrCode,
paymentRealName: data.paymentRealName,
// 卖家 Kava 地址
sellerKavaAddress: data.sellerKavaAddress,
remark: data.remark,
},
});

View File

@ -108,7 +108,6 @@ abstract class TradingRemoteDataSource {
String? paymentAccount,
String? paymentQrCode,
String? paymentRealName,
String? systemId, // 1.0ID绿
String? remark,
});
@ -467,7 +466,6 @@ class TradingRemoteDataSourceImpl implements TradingRemoteDataSource {
String? paymentAccount,
String? paymentQrCode,
String? paymentRealName,
String? systemId, // 1.0ID绿
String? remark,
}) async {
try {
@ -483,7 +481,6 @@ class TradingRemoteDataSourceImpl implements TradingRemoteDataSource {
if (paymentAccount != null) data['paymentAccount'] = paymentAccount;
if (paymentQrCode != null) data['paymentQrCode'] = paymentQrCode;
if (paymentRealName != null) data['paymentRealName'] = paymentRealName;
if (systemId != null && systemId.isNotEmpty) data['systemId'] = systemId;
if (remark != null && remark.isNotEmpty) data['remark'] = remark;
final response = await client.post(

View File

@ -32,7 +32,6 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
Set<String> _selectedPaymentMethods = {'GREEN_POINTS'}; // 绿
final _paymentAccountController = TextEditingController();
final _paymentRealNameController = TextEditingController();
final _systemIdController = TextEditingController(); // 1.0ID
@override
void dispose() {
@ -41,7 +40,6 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
_remarkController.dispose();
_paymentAccountController.dispose();
_paymentRealNameController.dispose();
_systemIdController.dispose();
super.dispose();
}
@ -400,32 +398,27 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
),
const SizedBox(height: 16),
// 1.0ID绿
// ID
const Text(
'1.0系统ID',
'账户ID',
style: TextStyle(fontSize: 14, color: _grayText),
),
const SizedBox(height: 4),
const Text(
'买家将通过此ID向您转账绿积分',
'买家将通过此账户向您转账绿积分',
style: TextStyle(fontSize: 12, color: _grayText),
),
const SizedBox(height: 8),
TextField(
controller: _systemIdController,
decoration: InputDecoration(
hintText: '请输入您的1.0系统ID',
hintStyle: const TextStyle(color: _grayText),
filled: true,
fillColor: _bgGray,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _orange, width: 2),
),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: _bgGray,
borderRadius: BorderRadius.circular(12),
),
child: Text(
ref.watch(userNotifierProvider).accountSequence ?? '',
style: const TextStyle(fontSize: 15, color: _darkText),
),
),
@ -675,8 +668,6 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
//
bool isValid = price > 0 && quantity > 0;
if (isSell) {
// 1.0ID绿
isValid = isValid && _systemIdController.text.trim().isNotEmpty;
//
if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) {
isValid = isValid &&
@ -774,13 +765,6 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
//
if (isSell) {
// 1.0ID绿
if (_systemIdController.text.trim().isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请填写1.0系统ID'), backgroundColor: _red),
);
return;
}
//
if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) {
if (_paymentAccountController.text.trim().isEmpty) {
@ -832,7 +816,7 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
const SizedBox(height: 8),
Text('收款方式: $paymentMethodTexts'),
const SizedBox(height: 4),
Text('1.0系统ID: ${_systemIdController.text.trim()}'),
Text('账户ID: ${ref.read(userNotifierProvider).accountSequence ?? ''}'),
if (_selectedPaymentMethods.any((m) => m != 'GREEN_POINTS')) ...[
const SizedBox(height: 4),
Text('收款账号: ${_paymentAccountController.text.trim()}'),
@ -874,7 +858,6 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
paymentMethod: isSell ? _selectedPaymentMethods.join(',') : null,
paymentAccount: isSell ? _paymentAccountController.text.trim() : null,
paymentRealName: isSell ? _paymentRealNameController.text.trim() : null,
systemId: isSell ? _systemIdController.text.trim() : null,
remark: remark.isEmpty ? null : remark,
);

View File

@ -87,7 +87,6 @@ class C2cTradingNotifier extends StateNotifier<C2cTradingState> {
String? paymentAccount,
String? paymentQrCode,
String? paymentRealName,
String? systemId, // 1.0ID绿
String? remark,
}) async {
state = state.copyWith(isLoading: true, clearError: true);
@ -102,7 +101,6 @@ class C2cTradingNotifier extends StateNotifier<C2cTradingState> {
paymentAccount: paymentAccount,
paymentQrCode: paymentQrCode,
paymentRealName: paymentRealName,
systemId: systemId,
remark: remark,
);
state = state.copyWith(isLoading: false, lastOrder: order);