瀏覽代碼

促销;订单物流查询修改

lrf 2 年之前
父節點
當前提交
5bd8a52c95

+ 8 - 0
app/controller/trade/config/.actOrder.js

@@ -24,6 +24,7 @@ module.exports = {
         platform_act: 'platform_act',
         order_detail: 'order_detail',
         is_deal: 'is_deal',
+        goods: 'goods',
       },
       // options: {
       //   "meta.state": 0 // 默认条件
@@ -37,4 +38,11 @@ module.exports = {
       count: true,
     },
   },
+  grmm: {
+    params: ['!id'],
+    service: 'getRefundMaxMoney',
+  },
+  toRefund: {
+    requestBody: ['data'],
+  },
 };

+ 20 - 1
app/model/trade/orderDetail.js

@@ -3,6 +3,25 @@ const transport = {
   shop_transport_no: '运单号',
   shop_transport_type: '快递公司类型',
 };
+const discount_detail = {
+  // key:value形式
+  key: '领取的优惠券id',
+  value: 'key:value形式:key:规格id ; value:分配的价格',
+};
+
+const act = {
+  // [object]形式
+  act_id: { type: String, zh: '活动id' },
+  money: { type: Number, zh: '返现金额' },
+  time: { type: String, zh: '返现时间' },
+  data: { type: Object, zh: '退款数据' },
+};
+const total_detail = {
+  goods_total: { type: Number, zh: '商品总价' },
+  freight_total: { type: Number, zh: '运费总价' },
+  discount_detail: { type: Object, zh: '优惠券明细' },
+  act: { type: Array, zh: '活动相关' },
+};
 
 const Schema = require('mongoose').Schema;
 const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
@@ -18,7 +37,7 @@ const orderDetail = {
   customer: { type: String, required: false, zh: '顾客', ref: 'User.User' }, //
   address: { type: Object, required: false, zh: '邮寄地址' }, //
   no: { type: String, required: false, zh: '订单号' }, //
-  transport: { type: Object, required: false, zh: '快递' }, // {no:运单号,type:快递公司编码}
+  transport: { type: Array, required: false, zh: '快递' }, // {no:运单号,type:快递公司编码}
   goods: { type: Array, required: false, zh: '商品快照清单' }, // 下单时,商品的属性设置:[{商品规格,商品信息}]
   total_detail: { type: Object, required: false, zh: '金额明细' }, // 本单的:{货物总价,运费总价,优惠详情:{优惠券id:{规格id:优惠金额}}}
   buy_time: { type: String, required: false, zh: '下单时间' }, //

+ 180 - 1
app/service/trade/actOrder.js

@@ -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;

+ 10 - 5
app/service/trade/orderDetail.js

@@ -20,11 +20,16 @@ class OrderDetailService extends CrudService {
   async searchOrderTransport({ id }) {
     const orderDetail = await this.model.findById(id);
     if (!id) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息');
-    const { transport = {} } = orderDetail;
-    const { shop_transport_no: no, shop_transport_type: type } = transport;
-    if (!no || !type) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '缺少快递信息');
-    const res = await this.ctx.service.util.kd100.search({ no, type });
-    return res;
+    const { transport = [] } = orderDetail;
+    const result = [];
+    for (const t of transport) {
+      const { shop_transport_no: no, shop_transport_type: type } = t;
+      if (!no || !type) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '缺少快递信息');
+      const res = await this.ctx.service.util.kd100.search({ no, type });
+      result.push(res);
+    }
+
+    return result;
   }
 
   /**

+ 3 - 1
app/z_router/trade/actOrder.js

@@ -7,7 +7,9 @@ const rkey = 'actOrder';
 const ckey = 'trade.actOrder';
 const keyZh = '平台活动-订单';
 const routes = [
-  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, middleware: [ 'dealQuery' ], name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'post', path: `${rkey}/toRefund`, controller: `${ckey}.toRefund`, name: `${ckey}toRefund`, zh: `${keyZh}批量返现` },
+  { method: 'get', path: `${rkey}/grmm/:id`, controller: `${ckey}.grmm`, name: `${ckey}grmm`, zh: `${keyZh}计算本单最大可以返现的金额` },
+  { method: 'get', path: `${rkey}`, middleware: [ 'dealQuery' ], controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
   { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
   // { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, name: `${ckey}Create`, zh: `创建${keyZh}` },
   { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, name: `${ckey}Update`, zh: `修改${keyZh}` },