| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- 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<OrderMt> {
- private orderGoodsRepository: Repository<OrderGoodsMt>;
- private goodsRepository: Repository<GoodsMt>;
- private deliveryAddressRepository: Repository<DeliveryAddressMt>;
- private orderRefundRepository: Repository<OrderRefundMt>;
- private paymentMtService: PaymentMtService;
- private creditBalanceService: CreditBalanceService;
- private logger: ReturnType<typeof createServiceLogger>;
- 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<number>();
- for (const info of goodsInfo) {
- if (info.goods.spuId > 0) {
- parentGoodsIds.add(info.goods.spuId);
- }
- }
- // 批量查询父商品信息
- const parentGoodsMap = new Map<number, GoodsMt>();
- 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<void>
- */
- async cancelOrder(tenantId: number, orderId: number, reason: string, userId: number): Promise<void> {
- 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<void> {
- 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<PaymentMtEntity | null> {
- 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 : '未知错误'}`
- };
- }
- }
- }
|