import { Injectable, Logger } from '@nestjs/common'; import { buildOrderStatusEvent } from '../../common/event/event.helpers'; import { PrismaService } from '../../common/prisma/prisma.module'; import { WechatOrderShippingService } from './wechat-order-shipping.service'; import type { WechatTradeManageEvent } from './wechat-msg-crypto.util'; /** * 小程序发货信息管理相关消息推送处理。 * @see https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html */ @Injectable() export class WechatTradeManageService { private readonly logger = new Logger(WechatTradeManageService.name); constructor( private readonly prisma: PrismaService, private readonly wechatOrderShipping: WechatOrderShippingService, ) {} async handleEvent(evt: WechatTradeManageEvent): Promise<{ handled: string }> { switch (evt.event) { case 'trade_manage_order_settlement': return this.handleOrderSettlement(evt); case 'trade_manage_remind_shipping': return this.handleRemindShipping(evt); case 'trade_manage_remind_access_api': case 'wxa_trade_controlled': await this.logEvent(evt, null, 'SUCCESS', evt.msg || evt.event); return { handled: evt.event }; default: await this.logEvent(evt, null, 'SUCCESS', `ignored:${evt.event || 'empty'}`); return { handled: 'ignored' }; } } private async handleOrderSettlement(evt: WechatTradeManageEvent) { const order = await this.findOrder(evt); const isConfirmOrSettle = evt.confirmReceiveTime != null || evt.settlementTime != null || evt.confirmReceiveMethod != null; if (!isConfirmOrSettle) { // 发货时推送:仅有 shipped_time / estimated_settlement_time await this.logEvent(evt, order?.id ?? null, 'SUCCESS', 'shipped_notify'); return { handled: 'settlement_shipped_notify' }; } if (!order) { await this.logEvent(evt, null, 'FAILED', 'ORDER_NOT_FOUND'); this.logger.warn( `trade_manage_order_settlement order not found tradeNo=${evt.merchantTradeNo} tx=${evt.transactionId}`, ); return { handled: 'settlement_order_missing' }; } if (order.status === 'COMPLETED') { await this.logEvent(evt, order.id, 'SUCCESS', 'already_completed'); return { handled: 'settlement_already_completed' }; } if (order.payStatus !== 'PAID') { await this.logEvent(evt, order.id, 'FAILED', 'NOT_PAID'); return { handled: 'settlement_not_paid' }; } const methodLabel = evt.confirmReceiveMethod === 2 ? '微信自动确认收货' : evt.confirmReceiveMethod === 1 ? '微信手动确认收货' : '微信订单结算'; const now = new Date(); const remark = [ methodLabel, evt.confirmReceiveTime ? `confirmAt=${evt.confirmReceiveTime}` : null, evt.settlementTime ? `settleAt=${evt.settlementTime}` : null, ] .filter(Boolean) .join(' | ') .slice(0, 512); await this.prisma.$transaction(async (tx) => { await tx.order.update({ where: { id: order.id }, data: { status: 'COMPLETED', completedAt: order.completedAt ?? now, }, }); await tx.orderDelivery.updateMany({ where: { orderId: order.id, deliveredAt: null }, data: { deliveredAt: now }, }); await tx.commonEvent.create({ data: buildOrderStatusEvent({ orderId: order.id, fromStatus: order.status, toStatus: 'COMPLETED', operator: 'WECHAT_TRADE_MANAGE', remark, }), }); }); await this.logEvent(evt, order.id, 'SUCCESS', methodLabel); this.logger.log( `WeChat confirm/settle → COMPLETED order=${order.orderNo} method=${evt.confirmReceiveMethod ?? '-'}`, ); return { handled: 'settlement_completed' }; } private async handleRemindShipping(evt: WechatTradeManageEvent) { const order = await this.findOrder(evt); await this.logEvent(evt, order?.id ?? null, order ? 'SUCCESS' : 'FAILED', evt.msg || 'remind_shipping'); if (order) { this.wechatOrderShipping.uploadForOrderSafe(order.id); } return { handled: 'remind_shipping' }; } private async findOrder(evt: WechatTradeManageEvent) { if (evt.merchantTradeNo) { const byNo = await this.prisma.order.findUnique({ where: { orderNo: evt.merchantTradeNo } }); if (byNo) return byNo; } if (evt.transactionId) { return this.prisma.order.findFirst({ where: { payExternalNo: evt.transactionId }, orderBy: { id: 'desc' }, }); } return null; } private async logEvent( evt: WechatTradeManageEvent, orderId: bigint | null, status: 'SUCCESS' | 'FAILED', note: string, ) { await this.prisma.logThirdParty.create({ data: { provider: 'WECHAT_PAY', scene: 'TRADE_MANAGE_PUSH', refType: orderId ? 'ORDER' : 'SYSTEM', refId: orderId ?? undefined, requestUrl: 'callbacks/wechat/message', requestBody: { event: evt.event, merchantTradeNo: evt.merchantTradeNo, transactionId: evt.transactionId, confirmReceiveMethod: evt.confirmReceiveMethod, confirmReceiveTime: evt.confirmReceiveTime, settlementTime: evt.settlementTime, shippedTime: evt.shippedTime, estimatedSettlementTime: evt.estimatedSettlementTime, msg: evt.msg, }, responseBody: { note }, externalNo: evt.transactionId || evt.merchantTradeNo, status, errorMessage: status === 'FAILED' ? note.slice(0, 512) : undefined, }, }); } }