actOrder.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const _ = require('lodash');
  5. const assert = require('assert');
  6. const { ObjectId } = require('mongoose').Types;
  7. const Transaction = require('mongoose-transactions');
  8. const moment = require('moment');
  9. //
  10. class ActOrderService extends CrudService {
  11. constructor(ctx) {
  12. super(ctx, 'actorder');
  13. this.model = this.ctx.model.Trade.ActOrder;
  14. this.orderDetailModel = this.ctx.model.Trade.OrderDetail;
  15. this.orderModel = this.ctx.model.Trade.Order;
  16. this.platformActModel = this.ctx.model.System.PlatformAct;
  17. this.gjaModel = this.ctx.model.Shop.GoodsJoinAct;
  18. this.tran = new Transaction();
  19. }
  20. /**
  21. * 创建订单与活动的关系
  22. * @param {String} id 订单id
  23. * @param {Object} data 订单数据
  24. * @param {Transaction} tran 数据库事务实例
  25. */
  26. async create(id, data, tran) {
  27. // 检查活动类型为1的 活动,再查下面的商品
  28. const actList = await this.platformActModel.find({ is_use: '0', type: { $ne: '0' } }, { _id: 1 });
  29. if (actList.length <= 0) return;
  30. // 整理出订单内的商品id
  31. const odGoods = data.goods.map(i => {
  32. const { _id: spec_id, goods } = i;
  33. const goods_id = _.get(goods, '_id');
  34. return { spec_id, goods_id };
  35. });
  36. const actOrderList = [];
  37. // 循环所有活动,找当前购买的商品,是否出现在这些活动之中,如果出现,就需要添加关系
  38. for (const act of actList) {
  39. const { _id: platformAct } = act;
  40. const query = { goods: odGoods.map(i => i.goods_id), platformAct };
  41. const goodsList = await this.gjaModel.find(query);
  42. // 获取受影响的商品
  43. const influenceGoods = goodsList.map(i => i.goods);
  44. if (influenceGoods.length <= 0) continue;
  45. const actOrder = { platform_act: platformAct, order_detail: id };
  46. // 用受影响的 商品id, 在 odGoods 中 找到对应的数据作为 actOrder的goods
  47. actOrder.goods = odGoods.filter(f => influenceGoods.includes(f.goods_id));
  48. actOrderList.push(actOrder);
  49. }
  50. for (const data of actOrderList) {
  51. tran.insert('ActOrder', data);
  52. }
  53. }
  54. // 改为聚合查询
  55. async query(filter, { skip = 0, limit, sort, desc, projection } = {}) {
  56. const { platform_act, order_detail, is_deal, goods } = filter;
  57. const pipeline = [];
  58. const modelMatch = {};
  59. if (platform_act) modelMatch.platform_act = platform_act;
  60. if (order_detail) modelMatch.order_detail = order_detail;
  61. if (is_deal) modelMatch.is_deal = is_deal;
  62. if (Object.keys(modelMatch).length > 0) pipeline.push({ $match: modelMatch });
  63. // 表关联
  64. pipeline.push({ $addFields: { order_detail_id: { $toObjectId: '$order_detail' } } });
  65. const odLookUp = {
  66. from: 'orderDetail',
  67. localField: 'order_detail_id',
  68. foreignField: '_id',
  69. as: 'odi',
  70. };
  71. // 加入查询条件, 限制下订单状态
  72. const odPipeline = [{ $match: { status: { $in: [ '1', '2-', '2', '3' ] } } }];
  73. if (goods) {
  74. odPipeline.push({ $match: { 'goods.goods.name': new RegExp(goods) } });
  75. }
  76. odPipeline.push({ $project: { name: 1, pay_time: 1, no: 1, customer: 1, shop: 1, status: 1, address: { name: 1, phone: 1, address: 1 } } });
  77. odPipeline.push({ $addFields: { shop_id: { $toObjectId: '$shop' } } });
  78. odPipeline.push({
  79. $lookup: {
  80. from: 'shop',
  81. localField: 'shop_id',
  82. foreignField: '_id',
  83. pipeline: [{ $project: { name: 1 } }],
  84. as: 'shopInfo',
  85. },
  86. });
  87. odPipeline.push({ $addFields: { customer_id: { $toObjectId: '$customer' } } });
  88. odPipeline.push({
  89. $lookup: {
  90. from: 'user',
  91. localField: 'customer_id',
  92. foreignField: '_id',
  93. pipeline: [{ $project: { name: 1 } }],
  94. as: 'cInfo',
  95. },
  96. });
  97. odPipeline.push({
  98. $project: {
  99. _id: 0,
  100. order_detail: '$_id',
  101. name: 1,
  102. pay_time: 1,
  103. no: 1,
  104. custumer_name: { $first: '$cInfo.name' },
  105. shop_name: { $first: '$shopInfo.name' },
  106. status: 1,
  107. address: { name: 1, phone: 1, address: 1 },
  108. },
  109. });
  110. odLookUp.pipeline = odPipeline;
  111. pipeline.push({ $lookup: odLookUp });
  112. pipeline.push({ $unwind: '$odi' });
  113. pipeline.push({ $replaceRoot: { newRoot: { $mergeObjects: [ '$$ROOT', '$$ROOT.odi' ] } } });
  114. pipeline.push({ $project: { odi: 0 } });
  115. // 查询管道
  116. const fp = _.cloneDeep(pipeline);
  117. pipeline.push({ $sort: { pay_time: 1 } });
  118. if (_.isNumber(skip) && skip > 0) pipeline.push(skip);
  119. if (_.isNumber(limit) && limit > 0) pipeline.push(limit);
  120. const data = await this.model.aggregate(fp);
  121. // 总数管道
  122. const tp = _.cloneDeep(pipeline);
  123. tp.push({ $count: 'total' });
  124. const tr = await this.model.aggregate(tp);
  125. return { data, total: _.get(_.head(tr), 'total', 0) };
  126. }
  127. /**
  128. * 获取本单最大退款金额
  129. * @param {String} id 数据id
  130. */
  131. async getRefundMaxMoney({ id }) {
  132. const data = await this.model.findById(id);
  133. if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到活动订单');
  134. const { order_detail, goods } = data;
  135. const orderDetail = await this.orderDetailModel.findById(order_detail);
  136. if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息');
  137. const moneyDetail = this.ctx.service.util.orderDetail.moneyDetail(orderDetail);
  138. let maxMoney = 0;
  139. // 判断订单类型,取出不同的实付价格
  140. let priceKey;
  141. if (_.get(orderDetail, 'type', '0') === '1') priceKey = 'ggrp';
  142. else priceKey = 'grp';
  143. for (const g of goods) {
  144. // 该规格总价明细
  145. const detail = _.get(moneyDetail, g.spec_id);
  146. if (!detail) continue;
  147. // 改规格实际支付价格
  148. const rp = _.get(detail, priceKey, 0);
  149. maxMoney = this.ctx.plus(maxMoney, rp);
  150. }
  151. // 找total_detail中的act字段,减掉 活动相关 已经退款的金额;
  152. const act = _.get(orderDetail, 'total_detail.act', []);
  153. const isRefundParts = act.reduce((p, n) => this.ctx.plus(p, n.money), 0);
  154. const canRefundParts = this.ctx.minus(maxMoney, isRefundParts);
  155. return canRefundParts;
  156. }
  157. /**
  158. * 批量返现
  159. * @param {Object} body 参数体
  160. * @param {Array} body.data 返现数组数据
  161. * @property {Number} money 退款金额
  162. * @property {String} id 数据id
  163. */
  164. async toRefund({ data }) {
  165. for (const i of data) {
  166. const { id, money, no } = i;
  167. const max = await this.getRefundMaxMoney({ id });
  168. if (this.ctx.minus(max, money) < 0) throw new BusinessError(ErrorCode.DATA_INVALID, `订单号:${no} 超过最大退款金额`);
  169. }
  170. try {
  171. const failed = [];
  172. for (const i of data) {
  173. const { id: act_id, money } = i;
  174. const obj = { act_id, money, time: moment().format('YYYY-MM-DD HH:mm:ss') };
  175. const actOrder = await this.model.findById(act_id, 'order_detail');
  176. const { order_detail } = actOrder;
  177. const orderDetail = await this.orderDetailModel.findById(order_detail, 'total_detail');
  178. const total_detail = _.get(orderDetail, 'total_detail', {});
  179. // 组织退款信息 {order_no,out_refund_no,money,reson}
  180. const refundData = await this.toMakeRefundData(i);
  181. obj.data = refundData;
  182. total_detail.act = [ ..._.get(total_detail, 'act', []), obj ];
  183. try {
  184. // 退款
  185. await this.ctx.service.trade.pay.refund(refundData);
  186. this.tran.update('OrderDetail', order_detail, { total_detail });
  187. } catch (error) {
  188. // next
  189. failed.push(obj);
  190. console.log(error);
  191. }
  192. }
  193. await this.tran.run();
  194. } catch (error) {
  195. await this.tran.rollback();
  196. } finally {
  197. this.tran.clean();
  198. }
  199. }
  200. /**
  201. * 根据促销返现数据生成退款数据
  202. * @param {Object} data 前端退款数据
  203. */
  204. async toMakeRefundData(data) {
  205. const { id, money } = data;
  206. const obj = { money, reason: '促销返现' };
  207. const actOrder = await this.model.findById(id);
  208. if (!actOrder) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到活动订单');
  209. const orderDetail = await this.orderDetailModel.findById(actOrder.order_detail, 'order');
  210. if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息');
  211. const order = await this.orderModel.findById(orderDetail.order, 'pay');
  212. if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付订单信息');
  213. const order_no = _.get(order, 'pay.pay_no');
  214. if (!order_no) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付单号');
  215. obj.order_no = order_no;
  216. const out_refund_no = `${order_no}-r-${_.random(10000, 99999)}`;
  217. obj.out_refund_no = out_refund_no;
  218. return obj;
  219. }
  220. }
  221. module.exports = ActOrderService;