'use strict'; const { CrudService } = require('naf-framework-mongoose-free/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; const _ = require('lodash'); const assert = require('assert'); const { ObjectId } = require('mongoose').Types; const Transaction = require('mongoose-transactions'); const moment = require('moment'); // class ActOrderService extends CrudService { constructor(ctx) { super(ctx, 'actorder'); this.model = this.ctx.model.Trade.ActOrder; this.orderDetailModel = this.ctx.model.Trade.OrderDetail; this.orderModel = this.ctx.model.Trade.Order; this.platformActModel = this.ctx.model.System.PlatformAct; this.gjaModel = this.ctx.model.Shop.GoodsJoinAct; this.tran = new Transaction(); } /** * 创建订单与活动的关系 * @param {String} id 订单id * @param {Object} data 订单数据 * @param {Transaction} tran 数据库事务实例 */ async create(id, data, tran) { // 检查活动类型为1的 活动,再查下面的商品 const actList = await this.platformActModel.find({ is_use: '0', type: { $ne: '0' } }, { _id: 1 }); if (actList.length <= 0) return; // 整理出订单内的商品id const odGoods = data.goods.map(i => { const { _id: spec_id, goods } = i; const goods_id = _.get(goods, '_id'); return { spec_id, goods_id }; }); const actOrderList = []; // 循环所有活动,找当前购买的商品,是否出现在这些活动之中,如果出现,就需要添加关系 for (const act of actList) { const { _id: platformAct } = act; const query = { goods: odGoods.map(i => i.goods_id), platformAct }; const goodsList = await this.gjaModel.find(query); // 获取受影响的商品 const influenceGoods = goodsList.map(i => i.goods); if (influenceGoods.length <= 0) continue; const actOrder = { platform_act: platformAct, order_detail: id }; // 用受影响的 商品id, 在 odGoods 中 找到对应的数据作为 actOrder的goods actOrder.goods = odGoods.filter(f => influenceGoods.includes(f.goods_id)); actOrderList.push(actOrder); } for (const data of actOrderList) { tran.insert('ActOrder', data); } } // 改为聚合查询 async query(filter, { skip = 0, limit, sort, desc, projection } = {}) { const { platform_act, order_detail, is_deal, goods } = filter; const pipeline = []; const modelMatch = {}; if (platform_act) modelMatch.platform_act = platform_act; if (order_detail) modelMatch.order_detail = order_detail; if (is_deal) modelMatch.is_deal = is_deal; if (Object.keys(modelMatch).length > 0) pipeline.push({ $match: modelMatch }); // 表关联 pipeline.push({ $addFields: { order_detail_id: { $toObjectId: '$order_detail' } } }); const odLookUp = { from: 'orderDetail', localField: 'order_detail_id', foreignField: '_id', as: 'odi', }; // 加入查询条件, 限制下订单状态 const odPipeline = [{ $match: { status: { $in: [ '1', '2-', '2', '3' ] } } }]; if (goods) { odPipeline.push({ $match: { 'goods.goods.name': new RegExp(goods) } }); } odPipeline.push({ $project: { name: 1, pay_time: 1, no: 1, customer: 1, shop: 1, status: 1, address: { name: 1, phone: 1, address: 1 } } }); odPipeline.push({ $addFields: { shop_id: { $toObjectId: '$shop' } } }); odPipeline.push({ $lookup: { from: 'shop', localField: 'shop_id', foreignField: '_id', pipeline: [{ $project: { name: 1 } }], as: 'shopInfo', }, }); odPipeline.push({ $addFields: { customer_id: { $toObjectId: '$customer' } } }); odPipeline.push({ $lookup: { from: 'user', localField: 'customer_id', foreignField: '_id', pipeline: [{ $project: { name: 1 } }], as: 'cInfo', }, }); odPipeline.push({ $project: { _id: 0, order_detail: '$_id', name: 1, pay_time: 1, no: 1, custumer_name: { $first: '$cInfo.name' }, shop_name: { $first: '$shopInfo.name' }, status: 1, address: { name: 1, phone: 1, address: 1 }, }, }); odLookUp.pipeline = odPipeline; pipeline.push({ $lookup: odLookUp }); pipeline.push({ $unwind: '$odi' }); pipeline.push({ $replaceRoot: { newRoot: { $mergeObjects: [ '$$ROOT', '$$ROOT.odi' ] } } }); pipeline.push({ $project: { odi: 0 } }); // 查询管道 const fp = _.cloneDeep(pipeline); pipeline.push({ $sort: { pay_time: 1 } }); if (_.isNumber(skip) && skip > 0) pipeline.push(skip); if (_.isNumber(limit) && limit > 0) pipeline.push(limit); const data = await this.model.aggregate(fp); // 总数管道 const tp = _.cloneDeep(pipeline); tp.push({ $count: 'total' }); const tr = await this.model.aggregate(tp); return { data, total: _.get(_.head(tr), 'total', 0) }; } /** * 获取本单最大退款金额 * @param {String} id 数据id */ async getRefundMaxMoney({ id }) { const data = await this.model.findById(id); if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到活动订单'); const { order_detail, goods } = data; const orderDetail = await this.orderDetailModel.findById(order_detail); if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息'); const moneyDetail = this.ctx.service.util.orderDetail.moneyDetail(orderDetail); let maxMoney = 0; // 判断订单类型,取出不同的实付价格 let priceKey; if (_.get(orderDetail, 'type', '0') === '1') priceKey = 'ggrp'; else priceKey = 'grp'; for (const g of goods) { // 该规格总价明细 const detail = _.get(moneyDetail, g.spec_id); if (!detail) continue; // 改规格实际支付价格 const rp = _.get(detail, priceKey, 0); maxMoney = this.ctx.plus(maxMoney, rp); } // 找total_detail中的act字段,减掉 活动相关 已经退款的金额; const act = _.get(orderDetail, 'total_detail.act', []); const isRefundParts = act.reduce((p, n) => this.ctx.plus(p, n.money), 0); const canRefundParts = this.ctx.minus(maxMoney, isRefundParts); return canRefundParts; } /** * 批量返现 * @param {Object} body 参数体 * @param {Array} body.data 返现数组数据 * @property {Number} money 退款金额 * @property {String} id 数据id */ async toRefund({ data }) { for (const i of data) { const { id, money, no } = i; const max = await this.getRefundMaxMoney({ id }); if (this.ctx.minus(max, money) < 0) throw new BusinessError(ErrorCode.DATA_INVALID, `订单号:${no} 超过最大退款金额`); } try { const failed = []; for (const i of data) { const { id: act_id, money } = i; const obj = { act_id, money, time: moment().format('YYYY-MM-DD HH:mm:ss') }; const actOrder = await this.model.findById(act_id, 'order_detail'); const { order_detail } = actOrder; const orderDetail = await this.orderDetailModel.findById(order_detail, 'total_detail'); const total_detail = _.get(orderDetail, 'total_detail', {}); // 组织退款信息 {order_no,out_refund_no,money,reson} const refundData = await this.toMakeRefundData(i); obj.data = refundData; total_detail.act = [ ..._.get(total_detail, 'act', []), obj ]; try { // 退款 await this.ctx.service.trade.pay.refund(refundData); this.tran.update('OrderDetail', order_detail, { total_detail }); } catch (error) { // next failed.push(obj); console.log(error); } } await this.tran.run(); } catch (error) { await this.tran.rollback(); } finally { this.tran.clean(); } } /** * 根据促销返现数据生成退款数据 * @param {Object} data 前端退款数据 */ async toMakeRefundData(data) { const { id, money } = data; const obj = { money, reason: '促销返现' }; const actOrder = await this.model.findById(id); if (!actOrder) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到活动订单'); const orderDetail = await this.orderDetailModel.findById(actOrder.order_detail, 'order'); if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息'); const order = await this.orderModel.findById(orderDetail.order, 'pay'); if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付订单信息'); const order_no = _.get(order, 'pay.pay_no'); if (!order_no) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付单号'); obj.order_no = order_no; const out_refund_no = `${order_no}-r-${_.random(10000, 99999)}`; obj.out_refund_no = out_refund_no; return obj; } } module.exports = ActOrderService;