fix(c2c): 积分股→积分值,C2C交易对象修正为积分值(cash)
C2C交易的产品是积分值,支付方式是绿积分(外部1.0系统),与积分股无关。 前端: - c2c_publish_page: 余额卡、数量输入、提示文案、确认弹窗全部改为积分值 - c2c_market_page: 余额概览(availableShares→availableCash)、订单数量标签、接单弹窗 - c2c_order_detail_page: 订单详情、划转提示、确认收款弹窗 后端 c2c.service.ts: - createOrder SELL: freezeShares→freezeCash - takeOrder BUY(taker=卖方): freezeShares→freezeCash - takeOrder SELL(taker=买方): 移除冻结(买方通过外部支付绿积分) - cancelOrder SELL: unfreezeShares→unfreezeCash - expireOrder: unfreezeShares→unfreezeCash,SELL不再解冻taker - executeTransfer: 统一BUY/SELL为单向积分值划转,assetType改为CASH Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
49bcb96c4c
commit
94d4524ee3
|
|
@ -38,15 +38,16 @@ const DEFAULT_CONFIRM_TIMEOUT_MINUTES = 60;
|
||||||
/**
|
/**
|
||||||
* C2C 场外交易服务
|
* C2C 场外交易服务
|
||||||
*
|
*
|
||||||
* 业务流程:
|
* 业务流程(C2C交易的是积分值,支付方式是绿积分/外部1.0系统):
|
||||||
* 1. 用户发布广告(createOrder)-> 状态: PENDING
|
* 1. 用户发布广告(createOrder)-> 状态: PENDING
|
||||||
* - BUY 类型: 冻结用户的积分值(cashBalance)
|
* - BUY 类型: 验证积分值余额,不冻结(买方通过外部支付绿积分)
|
||||||
* - SELL 类型: 冻结用户的积分股(shareBalance)
|
* - SELL 类型: 冻结卖方的积分值(cashBalance)
|
||||||
* 2. 其他用户接单(takeOrder)-> 状态: MATCHED
|
* 2. 其他用户接单(takeOrder)-> 状态: MATCHED
|
||||||
* - 对方冻结对应资产
|
* - 接BUY单(taker=卖方): 冻结taker的积分值
|
||||||
|
* - 接SELL单(taker=买方): 不冻结(买方通过外部支付绿积分)
|
||||||
* 3. 买方付款后点击确认付款(confirmPayment)-> 状态: PAID
|
* 3. 买方付款后点击确认付款(confirmPayment)-> 状态: PAID
|
||||||
* 4. 卖方确认收款(confirmReceived)-> 状态: COMPLETED
|
* 4. 卖方确认收款(confirmReceived)-> 状态: COMPLETED
|
||||||
* - 解冻双方资产并完成转账
|
* - 解冻卖方积分值并划转给买方
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class C2cService {
|
export class C2cService {
|
||||||
|
|
@ -144,13 +145,13 @@ export class C2cService {
|
||||||
}
|
}
|
||||||
// #12: 不冻结(买方通过外部1.0系统支付绿积分,系统无法冻结外部资产)
|
// #12: 不冻结(买方通过外部1.0系统支付绿积分,系统无法冻结外部资产)
|
||||||
} else {
|
} else {
|
||||||
// 卖出订单:需要冻结积分股
|
// 卖出订单:需要冻结积分值(C2C交易的是积分值)
|
||||||
const quantityMoney = new Money(quantityDecimal);
|
const quantityMoney = new Money(quantityDecimal);
|
||||||
if (account.availableShares.isLessThan(quantityMoney)) {
|
if (account.availableCash.isLessThan(quantityMoney)) {
|
||||||
throw new BadRequestException(`积分股余额不足,需要 ${quantity},可用 ${account.availableShares.toString()}`);
|
throw new BadRequestException(`积分值余额不足,需要 ${quantity},可用 ${account.availableCash.toString()}`);
|
||||||
}
|
}
|
||||||
// 冻结积分股
|
// 冻结积分值
|
||||||
await this.tradingAccountRepository.freezeShares(accountSequence, quantityDecimal);
|
await this.tradingAccountRepository.freezeCash(accountSequence, quantityDecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建订单
|
// 创建订单
|
||||||
|
|
@ -288,23 +289,17 @@ export class C2cService {
|
||||||
this.logger.log(`C2C部分成交: 原订单 ${orderNo} 拆分,剩余 ${remainingQuantity} 创建新订单 ${remainOrderNo}`);
|
this.logger.log(`C2C部分成交: 原订单 ${orderNo} 拆分,剩余 ${remainingQuantity} 创建新订单 ${remainOrderNo}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接单方需要冻结对应资产
|
// 接单方需要冻结对应资产(C2C交易的是积分值)
|
||||||
if (order.type === C2C_ORDER_TYPE.BUY) {
|
if (order.type === C2C_ORDER_TYPE.BUY) {
|
||||||
// 挂单方要买入积分股,接单方需要有积分股来卖出
|
// 挂单方要买入积分值,接单方(卖方)需要有积分值来卖出
|
||||||
const quantityMoney = new Money(quantityDecimal);
|
const quantityMoney = new Money(quantityDecimal);
|
||||||
if (takerAccount.availableShares.isLessThan(quantityMoney)) {
|
if (takerAccount.availableCash.isLessThan(quantityMoney)) {
|
||||||
throw new BadRequestException(`积分股余额不足,需要 ${quantityDecimal.toString()},可用 ${takerAccount.availableShares.toString()}`);
|
throw new BadRequestException(`积分值余额不足,需要 ${quantityDecimal.toString()},可用 ${takerAccount.availableCash.toString()}`);
|
||||||
}
|
|
||||||
// 冻结接单方的积分股
|
|
||||||
await this.tradingAccountRepository.freezeShares(takerAccountSequence, quantityDecimal);
|
|
||||||
} else {
|
|
||||||
// 挂单方要卖出积分股,接单方需要有积分值来买入
|
|
||||||
const totalAmountMoney = new Money(totalAmountDecimal);
|
|
||||||
if (takerAccount.availableCash.isLessThan(totalAmountMoney)) {
|
|
||||||
throw new BadRequestException(`积分值余额不足,需要 ${totalAmountDecimal.toString()},可用 ${takerAccount.availableCash.toString()}`);
|
|
||||||
}
|
}
|
||||||
// 冻结接单方的积分值
|
// 冻结接单方的积分值
|
||||||
await this.tradingAccountRepository.freezeCash(takerAccountSequence, totalAmountDecimal);
|
await this.tradingAccountRepository.freezeCash(takerAccountSequence, quantityDecimal);
|
||||||
|
} else {
|
||||||
|
// 挂单方要卖出积分值,接单方(买方)通过外部支付绿积分,不冻结
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算超时时间
|
// 计算超时时间
|
||||||
|
|
@ -369,7 +364,8 @@ export class C2cService {
|
||||||
if (order.type === C2C_ORDER_TYPE.BUY) {
|
if (order.type === C2C_ORDER_TYPE.BUY) {
|
||||||
// BUY 订单未冻结买方资产(买方通过外部支付绿积分),无需解冻
|
// BUY 订单未冻结买方资产(买方通过外部支付绿积分),无需解冻
|
||||||
} else {
|
} else {
|
||||||
await this.tradingAccountRepository.unfreezeShares(accountSequence, quantityDecimal);
|
// SELL 订单:解冻卖方的积分值
|
||||||
|
await this.tradingAccountRepository.unfreezeCash(accountSequence, quantityDecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新订单状态
|
// 更新订单状态
|
||||||
|
|
@ -494,146 +490,66 @@ export class C2cService {
|
||||||
sellerAccountSequence = order.makerAccountSequence;
|
sellerAccountSequence = order.makerAccountSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用事务执行转账
|
// 使用事务执行转账(C2C交易的是积分值,BUY和SELL都是单向划转积分值)
|
||||||
await this.prisma.$transaction(async (tx) => {
|
await this.prisma.$transaction(async (tx) => {
|
||||||
if (order.type === C2C_ORDER_TYPE.BUY) {
|
// 1. 解冻卖方积分值并扣除
|
||||||
// BUY订单:买方通过外部支付绿积分,系统只转移卖方积分股给买方(单向)
|
await tx.tradingAccount.update({
|
||||||
// 1. 解冻卖方积分股并扣除
|
where: { accountSequence: sellerAccountSequence },
|
||||||
await tx.tradingAccount.update({
|
data: {
|
||||||
where: { accountSequence: sellerAccountSequence },
|
frozenCash: { decrement: quantityDecimal.toNumber() },
|
||||||
data: {
|
cashBalance: { decrement: quantityDecimal.toNumber() },
|
||||||
frozenShares: { decrement: quantityDecimal.toNumber() },
|
totalSold: { increment: quantityDecimal.toNumber() },
|
||||||
shareBalance: { decrement: quantityDecimal.toNumber() },
|
},
|
||||||
totalSold: { increment: quantityDecimal.toNumber() },
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 买方获得积分股
|
// 2. 买方获得积分值
|
||||||
await tx.tradingAccount.update({
|
await tx.tradingAccount.update({
|
||||||
where: { accountSequence: buyerAccountSequence },
|
where: { accountSequence: buyerAccountSequence },
|
||||||
data: {
|
data: {
|
||||||
shareBalance: { increment: quantityDecimal.toNumber() },
|
cashBalance: { increment: quantityDecimal.toNumber() },
|
||||||
totalBought: { increment: quantityDecimal.toNumber() },
|
totalBought: { increment: quantityDecimal.toNumber() },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 记录交易流水(买方 — 积分股入账)
|
// 3. 记录交易流水(买方 — 积分值入账)
|
||||||
const buyerAccount = await tx.tradingAccount.findUnique({
|
const buyerAccount = await tx.tradingAccount.findUnique({
|
||||||
where: { accountSequence: buyerAccountSequence },
|
where: { accountSequence: buyerAccountSequence },
|
||||||
});
|
});
|
||||||
await tx.tradingTransaction.create({
|
await tx.tradingTransaction.create({
|
||||||
data: {
|
data: {
|
||||||
accountSequence: buyerAccountSequence,
|
accountSequence: buyerAccountSequence,
|
||||||
type: 'C2C_BUY',
|
type: 'C2C_BUY',
|
||||||
assetType: 'SHARE',
|
assetType: 'CASH',
|
||||||
amount: quantityDecimal.toNumber(),
|
amount: quantityDecimal.toNumber(),
|
||||||
balanceBefore: new Decimal(buyerAccount!.shareBalance).minus(quantityDecimal).toNumber(),
|
balanceBefore: new Decimal(buyerAccount!.cashBalance).minus(quantityDecimal).toNumber(),
|
||||||
balanceAfter: buyerAccount!.shareBalance,
|
balanceAfter: buyerAccount!.cashBalance,
|
||||||
referenceId: order.orderNo,
|
referenceId: order.orderNo,
|
||||||
referenceType: 'C2C_ORDER',
|
referenceType: 'C2C_ORDER',
|
||||||
counterpartyType: 'USER',
|
counterpartyType: 'USER',
|
||||||
counterpartyAccountSeq: sellerAccountSequence,
|
counterpartyAccountSeq: sellerAccountSequence,
|
||||||
memo: `C2C买入 ${order.quantity} 积分股(绿积分支付)`,
|
memo: `C2C买入 ${order.quantity} 积分值(绿积分支付)`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. 记录交易流水(卖方 — 积分股扣除)
|
// 4. 记录交易流水(卖方 — 积分值扣除)
|
||||||
const sellerAccount = await tx.tradingAccount.findUnique({
|
const sellerAccount = await tx.tradingAccount.findUnique({
|
||||||
where: { accountSequence: sellerAccountSequence },
|
where: { accountSequence: sellerAccountSequence },
|
||||||
});
|
});
|
||||||
await tx.tradingTransaction.create({
|
await tx.tradingTransaction.create({
|
||||||
data: {
|
data: {
|
||||||
accountSequence: sellerAccountSequence,
|
accountSequence: sellerAccountSequence,
|
||||||
type: 'C2C_SELL',
|
type: 'C2C_SELL',
|
||||||
assetType: 'SHARE',
|
assetType: 'CASH',
|
||||||
amount: quantityDecimal.toNumber(),
|
amount: quantityDecimal.toNumber(),
|
||||||
balanceBefore: new Decimal(sellerAccount!.shareBalance).plus(quantityDecimal).toNumber(),
|
balanceBefore: new Decimal(sellerAccount!.cashBalance).plus(quantityDecimal).toNumber(),
|
||||||
balanceAfter: sellerAccount!.shareBalance,
|
balanceAfter: sellerAccount!.cashBalance,
|
||||||
referenceId: order.orderNo,
|
referenceId: order.orderNo,
|
||||||
referenceType: 'C2C_ORDER',
|
referenceType: 'C2C_ORDER',
|
||||||
counterpartyType: 'USER',
|
counterpartyType: 'USER',
|
||||||
counterpartyAccountSeq: buyerAccountSequence,
|
counterpartyAccountSeq: buyerAccountSequence,
|
||||||
memo: `C2C卖出 ${order.quantity} 积分股(收到绿积分)`,
|
memo: `C2C卖出 ${order.quantity} 积分值(收到绿积分)`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// SELL订单:双向交换(积分股 ↔ 积分值),保持现有逻辑
|
|
||||||
// 1. 解冻买方的积分值并扣除
|
|
||||||
await tx.tradingAccount.update({
|
|
||||||
where: { accountSequence: buyerAccountSequence },
|
|
||||||
data: {
|
|
||||||
frozenCash: { decrement: totalAmountDecimal.toNumber() },
|
|
||||||
cashBalance: { decrement: totalAmountDecimal.toNumber() },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 解冻卖方的积分股并扣除
|
|
||||||
await tx.tradingAccount.update({
|
|
||||||
where: { accountSequence: sellerAccountSequence },
|
|
||||||
data: {
|
|
||||||
frozenShares: { decrement: quantityDecimal.toNumber() },
|
|
||||||
shareBalance: { decrement: quantityDecimal.toNumber() },
|
|
||||||
totalSold: { increment: quantityDecimal.toNumber() },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 3. 买方获得积分股
|
|
||||||
await tx.tradingAccount.update({
|
|
||||||
where: { accountSequence: buyerAccountSequence },
|
|
||||||
data: {
|
|
||||||
shareBalance: { increment: quantityDecimal.toNumber() },
|
|
||||||
totalBought: { increment: quantityDecimal.toNumber() },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. 卖方获得积分值
|
|
||||||
await tx.tradingAccount.update({
|
|
||||||
where: { accountSequence: sellerAccountSequence },
|
|
||||||
data: {
|
|
||||||
cashBalance: { increment: totalAmountDecimal.toNumber() },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 5. 记录交易流水(买方)
|
|
||||||
const buyerAccount = await tx.tradingAccount.findUnique({
|
|
||||||
where: { accountSequence: buyerAccountSequence },
|
|
||||||
});
|
|
||||||
await tx.tradingTransaction.create({
|
|
||||||
data: {
|
|
||||||
accountSequence: buyerAccountSequence,
|
|
||||||
type: 'C2C_BUY',
|
|
||||||
assetType: 'SHARE',
|
|
||||||
amount: quantityDecimal.toNumber(),
|
|
||||||
balanceBefore: new Decimal(buyerAccount!.shareBalance).minus(quantityDecimal).toNumber(),
|
|
||||||
balanceAfter: buyerAccount!.shareBalance,
|
|
||||||
referenceId: order.orderNo,
|
|
||||||
referenceType: 'C2C_ORDER',
|
|
||||||
counterpartyType: 'USER',
|
|
||||||
counterpartyAccountSeq: sellerAccountSequence,
|
|
||||||
memo: `C2C买入 ${order.quantity} 积分股,单价 ${order.price}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 6. 记录交易流水(卖方)
|
|
||||||
const sellerAccount = await tx.tradingAccount.findUnique({
|
|
||||||
where: { accountSequence: sellerAccountSequence },
|
|
||||||
});
|
|
||||||
await tx.tradingTransaction.create({
|
|
||||||
data: {
|
|
||||||
accountSequence: sellerAccountSequence,
|
|
||||||
type: 'C2C_SELL',
|
|
||||||
assetType: 'CASH',
|
|
||||||
amount: totalAmountDecimal.toNumber(),
|
|
||||||
balanceBefore: new Decimal(sellerAccount!.cashBalance).minus(totalAmountDecimal).toNumber(),
|
|
||||||
balanceAfter: sellerAccount!.cashBalance,
|
|
||||||
referenceId: order.orderNo,
|
|
||||||
referenceType: 'C2C_ORDER',
|
|
||||||
counterpartyType: 'USER',
|
|
||||||
counterpartyAccountSeq: buyerAccountSequence,
|
|
||||||
memo: `C2C卖出 ${order.quantity} 积分股,单价 ${order.price}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.log(`C2C交易转账完成: ${order.orderNo}, 买方: ${buyerAccountSequence}, 卖方: ${sellerAccountSequence}`);
|
this.logger.log(`C2C交易转账完成: ${order.orderNo}, 买方: ${buyerAccountSequence}, 卖方: ${sellerAccountSequence}`);
|
||||||
|
|
@ -740,18 +656,15 @@ export class C2cService {
|
||||||
const quantityDecimal = new Decimal(freshOrder.quantity);
|
const quantityDecimal = new Decimal(freshOrder.quantity);
|
||||||
const totalAmountDecimal = new Decimal(freshOrder.totalAmount);
|
const totalAmountDecimal = new Decimal(freshOrder.totalAmount);
|
||||||
|
|
||||||
// 解冻双方资产
|
// 解冻卖方的积分值(C2C交易的是积分值,买方不冻结)
|
||||||
if (freshOrder.type === C2C_ORDER_TYPE.BUY) {
|
if (freshOrder.type === C2C_ORDER_TYPE.BUY) {
|
||||||
// BUY订单:maker未冻结(买方通过外部支付绿积分),只解冻taker的积分股
|
// BUY订单:maker(买方)未冻结,只解冻taker(卖方)的积分值
|
||||||
if (freshOrder.takerAccountSequence) {
|
if (freshOrder.takerAccountSequence) {
|
||||||
await this.tradingAccountRepository.unfreezeShares(freshOrder.takerAccountSequence, quantityDecimal);
|
await this.tradingAccountRepository.unfreezeCash(freshOrder.takerAccountSequence, quantityDecimal);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SELL订单:maker冻结了积分股,taker冻结了积分值
|
// SELL订单:maker(卖方)冻结了积分值,taker(买方)未冻结
|
||||||
await this.tradingAccountRepository.unfreezeShares(freshOrder.makerAccountSequence, quantityDecimal);
|
await this.tradingAccountRepository.unfreezeCash(freshOrder.makerAccountSequence, quantityDecimal);
|
||||||
if (freshOrder.takerAccountSequence) {
|
|
||||||
await this.tradingAccountRepository.unfreezeCash(freshOrder.takerAccountSequence, totalAmountDecimal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新订单状态为过期
|
// 更新订单状态为过期
|
||||||
|
|
|
||||||
|
|
@ -127,12 +127,12 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'可用积分股',
|
'可用积分值',
|
||||||
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
formatAmount(availableShares),
|
formatAmount(availableCash),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
@ -389,7 +389,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
Text('数量', style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context))),
|
Text('数量', style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context))),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'${formatAmount(order.quantity)} 积分股',
|
'${formatAmount(order.quantity)} 积分值',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
@ -490,7 +490,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${formatAmount(order.quantity)} 积分股',
|
'${formatAmount(order.quantity)} 积分值',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
@ -649,7 +649,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text('可用数量', style: TextStyle(fontSize: 13, color: AppColors.textSecondaryOf(context))),
|
Text('可用数量', style: TextStyle(fontSize: 13, color: AppColors.textSecondaryOf(context))),
|
||||||
Text('${formatAmount(order.quantity)} 积分股', style: TextStyle(fontSize: 13, color: AppColors.textPrimaryOf(context))),
|
Text('${formatAmount(order.quantity)} 积分值', style: TextStyle(fontSize: 13, color: AppColors.textPrimaryOf(context))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -683,7 +683,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: const BorderSide(color: _orange, width: 2),
|
borderSide: const BorderSide(color: _orange, width: 2),
|
||||||
),
|
),
|
||||||
suffixText: '积分股',
|
suffixText: '积分值',
|
||||||
suffixStyle: TextStyle(color: AppColors.textSecondaryOf(context), fontSize: 14),
|
suffixStyle: TextStyle(color: AppColors.textSecondaryOf(context), fontSize: 14),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -830,8 +830,8 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
isBuyAction
|
isBuyAction
|
||||||
? '您将使用积分值购买对方的积分股'
|
? '您将使用绿积分购买对方的积分值'
|
||||||
: '您将出售积分股,买方将向您支付绿积分',
|
: '您将出售积分值,买方将向您支付绿积分',
|
||||||
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -338,8 +338,8 @@ class _C2cOrderDetailPageState extends ConsumerState<C2cOrderDetailPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildInfoRow('订单编号', order.orderNo, canCopy: true),
|
_buildInfoRow('订单编号', order.orderNo, canCopy: true),
|
||||||
_buildInfoRow('单价', '${formatPrice(order.price)} 积分值/股'),
|
_buildInfoRow('单价', '${formatPrice(order.price)} 积分值'),
|
||||||
_buildInfoRow('数量', '${formatAmount(order.quantity)} 积分股'),
|
_buildInfoRow('数量', '${formatAmount(order.quantity)} 积分值'),
|
||||||
_buildInfoRow('总金额', '${formatAmount(order.totalAmount)} 积分值'),
|
_buildInfoRow('总金额', '${formatAmount(order.totalAmount)} 积分值'),
|
||||||
_buildInfoRow('创建时间', _formatDateTime(order.createdAt)),
|
_buildInfoRow('创建时间', _formatDateTime(order.createdAt)),
|
||||||
if (order.remark != null && order.remark!.isNotEmpty)
|
if (order.remark != null && order.remark!.isNotEmpty)
|
||||||
|
|
@ -880,12 +880,12 @@ class _C2cOrderDetailPageState extends ConsumerState<C2cOrderDetailPage> {
|
||||||
? [
|
? [
|
||||||
'您已确认付款',
|
'您已确认付款',
|
||||||
'请等待卖方确认收款',
|
'请等待卖方确认收款',
|
||||||
'卖方确认后积分股将自动划转到您的账户',
|
'卖方确认后积分值将自动划转到您的账户',
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
'买方已确认付款',
|
'买方已确认付款',
|
||||||
'请检查您是否已收到转账',
|
'请检查您是否已收到转账',
|
||||||
'确认收款后积分股将自动划转给买方',
|
'确认收款后积分值将自动划转给买方',
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1215,7 +1215,7 @@ class _C2cOrderDetailPageState extends ConsumerState<C2cOrderDetailPage> {
|
||||||
Text('金额: ${formatAmount(order.totalAmount)} 积分值'),
|
Text('金额: ${formatAmount(order.totalAmount)} 积分值'),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const Text(
|
const Text(
|
||||||
'确认后,您的积分股将自动划转给买方,此操作不可撤销。',
|
'确认后,您的积分值将自动划转给买方,此操作不可撤销。',
|
||||||
style: TextStyle(fontSize: 12, color: _red),
|
style: TextStyle(fontSize: 12, color: _red),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -192,11 +192,9 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBalanceCard(String availableShares, String availableCash) {
|
Widget _buildBalanceCard(String availableShares, String availableCash) {
|
||||||
final isBuy = _selectedType == 0;
|
// C2C交易对象是积分值,BUY和SELL都显示可用积分值
|
||||||
// BUY: 显示可用积分值(用于验证 #16)
|
const label = '可用积分值';
|
||||||
// SELL: 显示可用积分股(卖方需要有足够积分股)
|
final balance = availableCash;
|
||||||
final label = isBuy ? '可用积分值' : '可用积分股';
|
|
||||||
final balance = isBuy ? availableCash : availableShares;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
|
@ -271,16 +269,14 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #14: 双向输入 — 积分股数量 ↔ 积分值金额(1:1固定价格,两者相等)
|
// #14: 双向输入 — 积分值数量 ↔ 积分值金额(1:1固定价格,两者相等)
|
||||||
Widget _buildQuantityInput(
|
Widget _buildQuantityInput(
|
||||||
String availableShares,
|
String availableShares,
|
||||||
String availableCash,
|
String availableCash,
|
||||||
String currentPrice,
|
String currentPrice,
|
||||||
) {
|
) {
|
||||||
final isBuy = _selectedType == 0;
|
// C2C交易的是积分值,BUY和SELL上限都是可用积分值
|
||||||
// BUY: 买入积分股,用积分值支付 → 上限是可用积分值
|
final maxBalance = availableCash;
|
||||||
// SELL: 卖出积分股 → 上限是可用积分股
|
|
||||||
final maxBalance = isBuy ? availableCash : availableShares;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
|
@ -302,12 +298,12 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'价格1:1,积分股数量 = 积分值金额',
|
'价格1:1,积分值数量 = 积分值金额',
|
||||||
style: const TextStyle(fontSize: 12, color: _grayText),
|
style: const TextStyle(fontSize: 12, color: _grayText),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
// 输入框1: 积分股数量
|
// 输入框1: 积分值数量
|
||||||
TextField(
|
TextField(
|
||||||
controller: _quantityController,
|
controller: _quantityController,
|
||||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||||
|
|
@ -315,7 +311,7 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}')),
|
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}')),
|
||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '请输入积分股数量',
|
hintText: '请输入积分值数量',
|
||||||
hintStyle: const TextStyle(color: _grayText),
|
hintStyle: const TextStyle(color: _grayText),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: _bgGray,
|
fillColor: _bgGray,
|
||||||
|
|
@ -343,7 +339,7 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
suffixText: '积分股',
|
suffixText: '积分值',
|
||||||
suffixStyle: const TextStyle(color: _grayText, fontSize: 14),
|
suffixStyle: const TextStyle(color: _grayText, fontSize: 14),
|
||||||
),
|
),
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
|
|
@ -679,9 +675,9 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text('积分股数量', style: TextStyle(fontSize: 14, color: _grayText)),
|
const Text('积分值数量', style: TextStyle(fontSize: 14, color: _grayText)),
|
||||||
Text(
|
Text(
|
||||||
'${formatAmount(quantity.toString())} 积分股',
|
'${formatAmount(quantity.toString())} 积分值',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
@ -783,11 +779,11 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
const Text(
|
const Text(
|
||||||
'1. 发布卖出广告后,您的积分股将被冻结直到交易完成或取消\n'
|
'1. 发布卖出广告后,您的积分值将被冻结直到交易完成或取消\n'
|
||||||
'2. 发布买入广告不会冻结您的资产\n'
|
'2. 发布买入广告不会冻结您的资产\n'
|
||||||
'3. 其他用户接单后,需在规定时间内完成交易\n'
|
'3. 其他用户接单后,需在规定时间内完成交易\n'
|
||||||
'4. 买方需在1.0系统中向卖方转账绿积分\n'
|
'4. 买方需在1.0系统中向卖方转账绿积分\n'
|
||||||
'5. 卖方确认收到绿积分后,积分股自动划转给买方\n'
|
'5. 卖方确认收到绿积分后,积分值自动划转给买方\n'
|
||||||
'6. 如遇问题,请联系客服处理',
|
'6. 如遇问题,请联系客服处理',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -871,7 +867,7 @@ class _C2cPublishPageState extends ConsumerState<C2cPublishPage> {
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
_selectedType == 1
|
_selectedType == 1
|
||||||
? '发布后,您的积分股将被冻结'
|
? '发布后,您的积分值将被冻结'
|
||||||
: '发布后不会冻结您的资产',
|
: '发布后不会冻结您的资产',
|
||||||
style: const TextStyle(fontSize: 12, color: _grayText),
|
style: const TextStyle(fontSize: 12, color: _grayText),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue