fix(c2c): MATCHED/PAID订单过期后自动恢复为新PENDING订单

当部分成交的订单过期时,将过期数量恢复到市场:
- BUY单过期:直接为maker创建新PENDING BUY单
- SELL单过期:检查maker余额,重新冻结后创建新PENDING SELL单
- 恢复失败不影响过期流程(仅记录日志)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-01 01:37:00 -08:00
parent 73a617b88c
commit 4e4a876341
1 changed files with 56 additions and 0 deletions

View File

@ -747,8 +747,64 @@ export class C2cService {
});
this.logger.log(`C2C订单已过期: ${freshOrder.orderNo}, 原状态: ${freshOrder.status}`);
// MATCHED/PAID 订单过期后,将数量退还为新的 PENDING 订单(恢复到市场)
if (freshOrder.status === C2C_ORDER_STATUS.MATCHED || freshOrder.status === C2C_ORDER_STATUS.PAID) {
await this.restoreExpiredOrder(freshOrder, quantityDecimal);
}
} finally {
await this.redis.releaseLock(lockKey, lockValue);
}
}
/**
* maker PENDING
* 退
*/
private async restoreExpiredOrder(expiredOrder: C2cOrderEntity, quantityDecimal: Decimal): Promise<void> {
try {
const priceDecimal = new Decimal(expiredOrder.price);
const totalAmountDecimal = priceDecimal.mul(quantityDecimal);
// SELL 订单需要重新冻结 maker 的积分值
if (expiredOrder.type === C2C_ORDER_TYPE.SELL) {
const makerAccount = await this.tradingAccountRepository.findByAccountSequence(expiredOrder.makerAccountSequence);
if (!makerAccount) {
this.logger.warn(`过期恢复: maker ${expiredOrder.makerAccountSequence} 账户不存在,跳过恢复`);
return;
}
const quantityMoney = new Money(quantityDecimal);
if (makerAccount.availableCash.isLessThan(quantityMoney)) {
this.logger.warn(`过期恢复: maker ${expiredOrder.makerAccountSequence} 可用余额不足 (需要 ${quantityDecimal}, 可用 ${makerAccount.availableCash}),跳过恢复`);
return;
}
await this.tradingAccountRepository.freezeCash(expiredOrder.makerAccountSequence, quantityDecimal);
}
// 创建新的 PENDING 订单
const restoreOrderNo = this.generateOrderNo();
await this.c2cOrderRepository.create({
orderNo: restoreOrderNo,
type: expiredOrder.type as any,
makerAccountSequence: expiredOrder.makerAccountSequence,
makerUserId: expiredOrder.makerUserId ?? undefined,
makerPhone: expiredOrder.makerPhone ?? undefined,
makerNickname: expiredOrder.makerNickname ?? undefined,
price: expiredOrder.price,
quantity: quantityDecimal.toString(),
totalAmount: totalAmountDecimal.toString(),
paymentMethod: expiredOrder.type === C2C_ORDER_TYPE.SELL ? (expiredOrder.paymentMethod ?? undefined) : undefined,
paymentAccount: expiredOrder.type === C2C_ORDER_TYPE.SELL ? (expiredOrder.paymentAccount ?? undefined) : undefined,
paymentQrCode: expiredOrder.type === C2C_ORDER_TYPE.SELL ? (expiredOrder.paymentQrCode ?? undefined) : undefined,
paymentRealName: expiredOrder.type === C2C_ORDER_TYPE.SELL ? (expiredOrder.paymentRealName ?? undefined) : undefined,
sellerKavaAddress: expiredOrder.sellerKavaAddress,
remark: expiredOrder.remark ?? undefined,
});
this.logger.log(`过期恢复: 订单 ${expiredOrder.orderNo} 已恢复为新 PENDING 订单 ${restoreOrderNo}, 数量: ${quantityDecimal}`);
} catch (error) {
this.logger.error(`过期恢复失败: ${expiredOrder.orderNo}`, error);
// 恢复失败不影响过期流程,只记录日志
}
}
}