feat(c2c): 添加expireOrder事务流程的详细调试日志

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-01 03:09:50 -08:00
parent 627c3c943c
commit 49ba2fcb19
1 changed files with 53 additions and 17 deletions

View File

@ -682,7 +682,15 @@ export class C2cService {
async processExpiredOrders(): Promise<number> {
// 计算 PENDING 订单的过期截止时间
const pendingCutoff = new Date(Date.now() - DEFAULT_PENDING_TIMEOUT_HOURS * 60 * 60 * 1000);
this.logger.debug(`[EXPIRY] 开始扫描超时订单, PENDING截止时间: ${pendingCutoff.toISOString()}`);
const expiredOrders = await this.c2cOrderRepository.findExpiredOrders(pendingCutoff);
if (expiredOrders.length === 0) {
return 0;
}
this.logger.log(`[EXPIRY] 发现 ${expiredOrders.length} 个超时订单: ${expiredOrders.map(o => `${o.orderNo}(${o.type}/${o.status})`).join(', ')}`);
let processedCount = 0;
for (const order of expiredOrders) {
@ -690,14 +698,11 @@ export class C2cService {
await this.expireOrder(order);
processedCount++;
} catch (error) {
this.logger.error(`处理超时订单失败: ${order.orderNo}`, error);
this.logger.error(`[EXPIRY] 处理超时订单失败: ${order.orderNo}`, error);
}
}
if (processedCount > 0) {
this.logger.log(`处理了 ${processedCount} 个超时订单`);
}
this.logger.log(`[EXPIRY] 本轮处理完成: 成功 ${processedCount}/${expiredOrders.length}`);
return processedCount;
}
@ -709,17 +714,23 @@ export class C2cService {
const lockKey = `c2c:expire:${order.orderNo}`;
const lockValue = await this.redis.acquireLock(lockKey, 30);
if (!lockValue) {
return; // 其他进程正在处理
this.logger.debug(`[EXPIRY] 订单 ${order.orderNo} 正被其他进程处理,跳过`);
return;
}
try {
// 重新获取订单,确保状态一致
const freshOrder = await this.c2cOrderRepository.findByOrderNo(order.orderNo);
if (!freshOrder || (
if (!freshOrder) {
this.logger.warn(`[EXPIRY] 订单 ${order.orderNo} 不存在,跳过`);
return;
}
if (
freshOrder.status !== C2C_ORDER_STATUS.PENDING &&
freshOrder.status !== C2C_ORDER_STATUS.MATCHED &&
freshOrder.status !== C2C_ORDER_STATUS.PAID
)) {
) {
this.logger.debug(`[EXPIRY] 订单 ${order.orderNo} 状态已变为 ${freshOrder.status},无需处理`);
return;
}
@ -728,27 +739,39 @@ export class C2cService {
const isSell = freshOrder.type === C2C_ORDER_TYPE.SELL;
let restoreOrderNo: string | null = null;
this.logger.log(
`[EXPIRY] 开始处理订单 ${freshOrder.orderNo}: ` +
`类型=${freshOrder.type}, 状态=${freshOrder.status}, ` +
`数量=${freshOrder.quantity}, 价格=${freshOrder.price}, ` +
`maker=${freshOrder.makerAccountSequence}, taker=${freshOrder.takerAccountSequence || '无'}, ` +
`需恢复=${shouldRestore}`,
);
await this.prisma.$transaction(async (tx) => {
// 1. 解冻卖方的积分值(如果需要)
if (freshOrder.status === C2C_ORDER_STATUS.PENDING) {
// PENDING SELL解冻 maker
if (isSell) {
this.logger.log(`[EXPIRY][TX] 步骤1: 解冻 PENDING SELL maker ${freshOrder.makerAccountSequence} 的 frozenCash -= ${quantityDecimal}`);
await tx.tradingAccount.update({
where: { accountSequence: freshOrder.makerAccountSequence },
data: { frozenCash: { decrement: quantityDecimal.toNumber() } },
});
} else {
this.logger.debug(`[EXPIRY][TX] 步骤1: PENDING BUY 无冻结资产,跳过解冻`);
}
// PENDING BUY无冻结
} else if (!isSell && freshOrder.takerAccountSequence) {
// MATCHED/PAID BUY解冻 taker卖方
this.logger.log(`[EXPIRY][TX] 步骤1: 解冻 ${freshOrder.status} BUY taker ${freshOrder.takerAccountSequence} 的 frozenCash -= ${quantityDecimal}`);
await tx.tradingAccount.update({
where: { accountSequence: freshOrder.takerAccountSequence },
data: { frozenCash: { decrement: quantityDecimal.toNumber() } },
});
} else if (isSell && shouldRestore) {
this.logger.log(`[EXPIRY][TX] 步骤1: ${freshOrder.status} SELL 保持冻结不变,冻结量将转给恢复的 PENDING 订单`);
}
// MATCHED/PAID SELL + shouldRestore不解冻冻结直接转给新 PENDING 订单
// 2. 标记订单为过期
this.logger.log(`[EXPIRY][TX] 步骤2: 标记订单 ${freshOrder.orderNo} 状态 ${freshOrder.status} -> EXPIRED`);
await tx.c2cOrder.update({
where: { orderNo: freshOrder.orderNo },
data: {
@ -761,6 +784,12 @@ export class C2cService {
if (shouldRestore) {
const priceDecimal = new Decimal(freshOrder.price);
restoreOrderNo = this.generateOrderNo();
const totalAmount = priceDecimal.mul(quantityDecimal).toString();
this.logger.log(
`[EXPIRY][TX] 步骤3: 恢复为新 PENDING 订单 ${restoreOrderNo}, ` +
`类型=${freshOrder.type}, maker=${freshOrder.makerAccountSequence}, ` +
`数量=${quantityDecimal}, 总金额=${totalAmount}`,
);
await tx.c2cOrder.create({
data: {
orderNo: restoreOrderNo,
@ -772,7 +801,7 @@ export class C2cService {
makerNickname: freshOrder.makerNickname,
price: freshOrder.price,
quantity: quantityDecimal.toString(),
totalAmount: priceDecimal.mul(quantityDecimal).toString(),
totalAmount,
minAmount: '0',
maxAmount: '0',
paymentMethod: isSell ? freshOrder.paymentMethod : null,
@ -783,17 +812,24 @@ export class C2cService {
remark: freshOrder.remark,
},
});
} else {
this.logger.debug(`[EXPIRY][TX] 步骤3: PENDING 订单无需恢复`);
}
});
// 事务成功后记日志
this.logger.log(`C2C订单已过期: ${freshOrder.orderNo}, 原状态: ${freshOrder.status}`);
if (restoreOrderNo) {
this.logger.log(`过期恢复: 订单 ${freshOrder.orderNo} 已恢复为新 PENDING 订单 ${restoreOrderNo}, 数量: ${quantityDecimal}`);
}
this.logger.log(
`[EXPIRY] 事务提交成功: 订单 ${freshOrder.orderNo} (${freshOrder.type}/${freshOrder.status}) -> EXPIRED` +
(restoreOrderNo ? `, 恢复为 ${restoreOrderNo} (PENDING, 数量=${quantityDecimal})` : ''),
);
} catch (error: any) {
// 事务失败 = 全部回滚,不会出现不一致状态
this.logger.error(`expireOrder 事务失败: ${order.orderNo}: ${error.message}`);
this.logger.error(
`[EXPIRY] 事务失败,已回滚: 订单=${order.orderNo}, ` +
`错误=${error.message}`,
error.stack,
);
throw error;
} finally {
await this.redis.releaseLock(lockKey, lockValue);
}