import { GenericCrudService } from '@d8d/shared-crud'; import { DataSource, Repository, In } from 'typeorm'; import { OrderMt } from '../entities/order.mt.entity'; import { OrderGoodsMt } from '../entities/order-goods.mt.entity'; import { OrderRefundMt } from '../entities/order-refund.mt.entity'; import { GoodsMt } from '@d8d/goods-module-mt'; import { DeliveryAddressMt } from '@d8d/delivery-address-module-mt'; import { PaymentMtService } from '@d8d/mini-payment-mt'; import { CreditBalanceService } from '@d8d/credit-balance-module-mt'; import { PaymentMtEntity, PaymentStatus } from '@d8d/mini-payment-mt'; import type { CreateOrderRequest } from '../schemas/create-order.schema'; import { createServiceLogger } from '@d8d/shared-utils'; export class OrderMtService extends GenericCrudService { private orderGoodsRepository: Repository; private goodsRepository: Repository; private deliveryAddressRepository: Repository; private orderRefundRepository: Repository; private paymentMtService: PaymentMtService; private creditBalanceService: CreditBalanceService; private logger: ReturnType; constructor(dataSource: DataSource) { super(dataSource, OrderMt, { tenantOptions: { enabled: true, tenantIdField: 'tenantId' } }); this.orderGoodsRepository = dataSource.getRepository(OrderGoodsMt); this.goodsRepository = dataSource.getRepository(GoodsMt); this.deliveryAddressRepository = dataSource.getRepository(DeliveryAddressMt); this.orderRefundRepository = dataSource.getRepository(OrderRefundMt); this.paymentMtService = new PaymentMtService(dataSource); this.creditBalanceService = new CreditBalanceService(dataSource); this.logger = createServiceLogger('order-mt-service'); } /** * 创建订单 * @param data 创建订单请求数据 * @param userId 用户ID * @param tenantId 租户ID * @returns 创建的订单信息 */ async createOrder(data: CreateOrderRequest, userId: number, tenantId: number): Promise<{ success: boolean; orderId: number; orderNo: string; amount: number; payAmount: number; message: string; }> { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const { addressId, productOwn, consumeFrom, products, remark } = data; // 记录开始创建订单 this.logger.info('开始创建订单', { tenantId, userId, productCount: products.length, addressId }); // 验证商品信息并计算总价 let totalAmount = 0; let totalCostAmount = 0; const goodsInfo = []; for (const item of products) { const goods = await this.goodsRepository.findOne({ where: { id: item.id, tenantId } }); if (!goods) { throw new Error(`商品ID ${item.id} 不存在`); } if (goods.stock < item.num) { throw new Error(`商品 ${goods.name} 库存不足`); } const itemAmount = goods.price * item.num; const itemCostAmount = goods.costPrice * item.num; totalAmount += itemAmount; totalCostAmount += itemCostAmount; goodsInfo.push({ goods, quantity: item.num, amount: itemAmount, costAmount: itemCostAmount }); } // 收集需要查询的父商品ID const parentGoodsIds = new Set(); for (const info of goodsInfo) { if (info.goods.spuId > 0) { parentGoodsIds.add(info.goods.spuId); } } // 批量查询父商品信息 const parentGoodsMap = new Map(); if (parentGoodsIds.size > 0) { const parentGoods = await this.goodsRepository.find({ where: { id: In([...parentGoodsIds]), tenantId } }); parentGoods.forEach(g => parentGoodsMap.set(g.id, g)); } // 获取收货地址信息 let deliveryAddress = null; if (addressId) { deliveryAddress = await this.deliveryAddressRepository.findOne({ where: { id: addressId, userId, tenantId } }); if (!deliveryAddress) { throw new Error('收货地址不存在'); } } // 生成订单号 const orderNo = this.generateOrderNo(); // 创建订单 // 计算订单过期时间(24小时后) const expireTime = new Date(); expireTime.setHours(expireTime.getHours() + 24); const createData ={ tenantId, orderNo, userId, amount: totalAmount, costAmount: totalCostAmount, payAmount: totalAmount, // 这里可以根据优惠规则计算实际支付金额 orderType: 1, // 实物订单 payType: consumeFrom === '积分兑换' ? 1 : 2, // 根据消费来源设置支付类型 payState: 0, // 未支付 state: 0, // 未发货 addressId: addressId || 0, merchantId: goodsInfo[0]?.goods.merchantId || 0, supplierId: goodsInfo[0]?.goods.supplierId || 0, remark: remark || '', createdBy: userId, updatedBy: userId, expireTime, // 设置订单过期时间 // 设置收货地址信息(如果提供) ...(deliveryAddress && { receiverMobile: deliveryAddress.phone, recevierName: deliveryAddress.name, recevierProvince: deliveryAddress.receiverProvince, recevierCity: deliveryAddress.receiverCity, recevierDistrict: deliveryAddress.receiverDistrict, recevierTown: deliveryAddress.receiverTown, address: deliveryAddress.address }) }; this.logger.info('createData:',createData); const order = this.repository.create(createData); this.logger.info('order:',order); this.logger.info('订单创建成功', { tenantId, userId, orderNo: order.orderNo, amount: order.amount, payAmount: order.payAmount }); const savedOrder = await queryRunner.manager.save(order); // 创建订单商品明细 const orderGoodsList = goodsInfo.map(info => { let goodsName = info.goods.name; if (info.goods.spuId > 0) { const parentGoods = parentGoodsMap.get(info.goods.spuId); if (parentGoods) { goodsName = `${parentGoods.name} ${info.goods.name}`; } } return { tenantId, orderId: savedOrder.id, orderNo, goodsId: info.goods.id, goodsName, imageFileId: info.goods.imageFileId, goodsType: info.goods.goodsType, supplierId: info.goods.supplierId, costPrice: info.goods.costPrice, price: info.goods.price, num: info.quantity, freightAmount: 0, state: 0, createdBy: userId, updatedBy: userId, expressName: null, expressNo: null }; }); this.logger.info('orderGoodsList:',orderGoodsList); this.logger.info('订单商品明细创建成功', { tenantId, orderId: savedOrder.id, goodsCount: orderGoodsList.length }); await queryRunner.manager.save(OrderGoodsMt, orderGoodsList); // 更新商品库存 for (const item of goodsInfo) { await queryRunner.manager.update(GoodsMt, item.goods.id, { stock: () => `stock - ${item.quantity}`, salesNum: () => `sales_num + ${item.quantity}` }); } await queryRunner.commitTransaction(); this.logger.info('订单创建流程完成', { tenantId, userId, orderId: savedOrder.id, orderNo: savedOrder.orderNo, totalAmount: savedOrder.amount }); return { success: true, orderId: savedOrder.id, orderNo: savedOrder.orderNo, amount: savedOrder.amount, payAmount: savedOrder.payAmount, message: '订单创建成功' }; } catch (error) { await queryRunner.rollbackTransaction(); this.logger.error('订单创建失败', { tenantId, userId, error: error instanceof Error ? error.message : String(error) }); throw error; } finally { await queryRunner.release(); } } /** * 取消订单 * @param tenantId 租户ID * @param orderId 订单ID * @param reason 取消原因 * @param userId 用户ID * @returns Promise */ async cancelOrder(tenantId: number, orderId: number, reason: string, userId: number): Promise { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // 查询订单信息 const order = await this.repository.findOne({ where: { id: orderId, tenantId, userId } }); if (!order) { throw new Error('订单不存在'); } // 验证订单状态(仅允许取消未发货且支付状态为0或2的订单) if (order.state !== 0) { throw new Error('当前订单状态不允许取消'); } // 验证支付状态 if (order.payState !== 0 && order.payState !== 2) { throw new Error('当前订单状态不允许取消'); } // 对于已支付订单(支付状态=2),需要触发退款流程 if (order.payState === 2) { this.logger.debug('订单已支付,开始触发退款流程', { tenantId, orderId, orderNo: order.orderNo }); try { // 调用支付模块的退款功能 const refundResult = await this.paymentMtService.refund( tenantId, order.orderNo, order.payAmount, `订单取消:${reason}` ); this.logger.debug('退款处理成功', { tenantId, orderId, orderNo: order.orderNo, refundId: refundResult.refundId }); // 创建退款记录 await this.createRefundRecord(tenantId, order, refundResult, userId); } catch (error) { this.logger.warn('退款处理失败', { tenantId, orderId, orderNo: order.orderNo, error: error instanceof Error ? error.message : String(error) }); // 退款失败不影响订单取消,但记录错误信息 // 可以在这里添加重试机制或通知管理员 } } // 更新订单状态为5(订单关闭),记录取消时间和原因 await queryRunner.manager.update(OrderMt, { id: orderId, tenantId }, { payState: 5, // 订单关闭 cancelReason: reason, cancelTime: new Date(), updatedBy: userId, updatedAt: new Date() }); // 如果是额度支付订单(payType === 3),需要恢复额度 if (order.payType === 3) { this.logger.debug('额度支付订单取消,开始恢复额度', { tenantId, orderId, orderNo: order.orderNo }); try { await this.creditBalanceService.restoreBalanceForCancelOrder( tenantId, userId, order.orderNo, order.payAmount, userId ); this.logger.debug('额度恢复成功', { tenantId, orderId, orderNo: order.orderNo }); } catch (error) { this.logger.warn('额度恢复失败', { tenantId, orderId, orderNo: order.orderNo, error: error instanceof Error ? error.message : String(error) }); // 额度恢复失败不影响订单取消,但记录错误信息 } } // 如果订单是未支付状态,需要恢复商品库存 if (order.payState === 0) { // 查询订单商品明细 const orderGoods = await this.orderGoodsRepository.find({ where: { orderId, tenantId } }); // 恢复商品库存 for (const item of orderGoods) { await queryRunner.manager.update(GoodsMt, { id: item.goodsId, tenantId }, { stock: () => `stock + ${item.num}`, salesNum: () => `sales_num - ${item.num}` }); } } await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } /** * 确认收货 * @param tenantId 租户ID * @param orderId 订单ID * @param userId 用户ID * @returns 更新后的订单信息 */ async confirmReceipt(tenantId: number, orderId: number, userId: number): Promise<{ orderId: number; state: number; updatedAt: Date; }> { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // 查询订单信息 const order = await this.repository.findOne({ where: { id: orderId, tenantId, userId } }); if (!order) { throw new Error('订单不存在'); } // 验证订单状态:只有已发货的订单才能确认收货 if (order.state !== 1) { throw new Error('只有已发货的订单才能确认收货'); } // 验证支付状态:只有已支付的订单才能确认收货 if (order.payState !== 2) { throw new Error('只有已支付的订单才能确认收货'); } // 更新订单状态为收货成功 (state = 2) await queryRunner.manager.update(OrderMt, { id: orderId, tenantId }, { state: 2, // 收货成功 updatedBy: userId, updatedAt: new Date() }); // 提交事务 await queryRunner.commitTransaction(); // 返回更新后的订单信息 const updatedOrder = await this.repository.findOne({ where: { id: orderId, tenantId } }); if (!updatedOrder) { throw new Error('订单更新后查询失败'); } return { orderId: updatedOrder.id, state: updatedOrder.state, updatedAt: updatedOrder.updatedAt }; } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } /** * 生成订单号 * 格式:年月日时分秒 + 6位随机数 */ private generateOrderNo(): string { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hour = String(now.getHours()).padStart(2, '0'); const minute = String(now.getMinutes()).padStart(2, '0'); const second = String(now.getSeconds()).padStart(2, '0'); const random = String(Math.floor(Math.random() * 1000000)).padStart(6, '0'); return `ORD${year}${month}${day}${hour}${minute}${second}${random}`; } /** * 创建退款记录 */ private async createRefundRecord( tenantId: number, order: OrderMt, refundResult: any, userId: number ): Promise { const refundRecord = new OrderRefundMt(); refundRecord.tenantId = tenantId; refundRecord.orderNo = order.orderNo; refundRecord.refundOrderNo = refundResult.outRefundNo; refundRecord.refundAmount = refundResult.refundAmount; refundRecord.state = 2; // 退款成功 refundRecord.remark = `退款成功,微信退款流水号: ${refundResult.refundId}`; refundRecord.createdBy = userId; refundRecord.updatedBy = userId; await this.orderRefundRepository.save(refundRecord); this.logger.debug('退款记录创建成功', { tenantId, orderNo: order.orderNo, refundOrderNo: refundResult.outRefundNo }); } /** * 根据订单ID查询支付记录 * @param tenantId 租户ID * @param orderId 订单ID * @param userId 用户ID(可选,用于权限验证) * @returns 支付记录信息,如果不存在则返回null */ async getPaymentRecordByOrderId( tenantId: number, orderId: number, userId?: number ): Promise { try { // 获取支付实体仓库 const paymentRepository = this.dataSource.getRepository(PaymentMtEntity); // 构建查询条件 const whereCondition: any = { externalOrderId: orderId, tenantId }; // 如果提供了用户ID,则添加用户ID过滤(确保用户只能查询自己的支付记录) if (userId !== undefined) { whereCondition.userId = userId; } // 查询支付记录 const paymentRecord = await paymentRepository.findOne({ where: whereCondition, order: { createdAt: 'DESC' } // 按创建时间倒序,获取最新的支付记录 }); if (!paymentRecord) { this.logger.debug('未找到订单的支付记录', { tenantId, orderId, userId }); return null; } this.logger.debug('找到订单的支付记录', { tenantId, orderId, paymentId: paymentRecord.id, outTradeNo: paymentRecord.outTradeNo, wechatTransactionId: paymentRecord.wechatTransactionId, paymentStatus: paymentRecord.paymentStatus, totalAmount: paymentRecord.totalAmount }); return paymentRecord; } catch (error) { this.logger.error('查询订单支付记录失败', { tenantId, orderId, error: error instanceof Error ? error.message : String(error) }); throw new Error(`查询支付记录失败: ${error instanceof Error ? error.message : '未知错误'}`); } } /** * 根据订单ID查询支付记录(简化版,返回必要信息) * @param tenantId 租户ID * @param orderId 订单ID * @param userId 用户ID(可选) * @returns 支付记录的关键信息 */ async getPaymentInfoByOrderId( tenantId: number, orderId: number, userId?: number ): Promise<{ outTradeNo: string | null; wechatTransactionId: string | null; paymentStatus: string; exists: boolean; paymentId?: number; totalAmount?: number; createdAt?: Date; hasWechatTransactionId: boolean; paymentStatusDetail: string; }> { try { const paymentRecord = await this.getPaymentRecordByOrderId(tenantId, orderId, userId); if (!paymentRecord) { return { outTradeNo: null, wechatTransactionId: null, paymentStatus: '未找到支付记录', exists: false, hasWechatTransactionId: false, paymentStatusDetail: '支付记录不存在' }; } const hasWechatTransactionId = !!paymentRecord.wechatTransactionId; let paymentStatusDetail: string = paymentRecord.paymentStatus || '未知状态'; // 添加详细的支付状态说明 if (!hasWechatTransactionId) { if (paymentRecord.paymentStatus === PaymentStatus.PAID) { paymentStatusDetail = '已支付(等待微信回调更新交易ID)'; } else if (paymentRecord.paymentStatus === PaymentStatus.PROCESSING) { paymentStatusDetail = '支付处理中(微信交易ID将在支付成功后生成)'; } else if (paymentRecord.paymentStatus === PaymentStatus.PENDING) { paymentStatusDetail = '待支付(尚未完成支付)'; } } return { outTradeNo: paymentRecord.outTradeNo || null, wechatTransactionId: paymentRecord.wechatTransactionId || null, paymentStatus: paymentRecord.paymentStatus || '未知状态', exists: true, paymentId: paymentRecord.id, totalAmount: paymentRecord.totalAmount, createdAt: paymentRecord.createdAt, hasWechatTransactionId, paymentStatusDetail }; } catch (error) { this.logger.error('获取订单支付信息失败', { tenantId, orderId, error: error instanceof Error ? error.message : String(error) }); return { outTradeNo: null, wechatTransactionId: null, paymentStatus: '查询失败', exists: false, hasWechatTransactionId: false, paymentStatusDetail: `查询失败: ${error instanceof Error ? error.message : '未知错误'}` }; } } }