|
@@ -3,14 +3,19 @@ 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();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -47,6 +52,180 @@ class ActOrderService extends CrudService {
|
|
|
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;
|