afterSale.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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 moment = require('moment');
  8. const Transaction = require('mongoose-transactions');
  9. //
  10. class AfterSaleService extends CrudService {
  11. constructor(ctx) {
  12. super(ctx, 'aftersale');
  13. this.model = this.ctx.model.Trade.AfterSale;
  14. this.orderDetailModel = this.ctx.model.Trade.OrderDetail;
  15. this.tran = new Transaction();
  16. }
  17. async create({ order_id, goods_id, ...others }) {
  18. const orderDetail = await this.orderDetailModel.findById(order_id);
  19. if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息');
  20. // 查询该商品是不是申请退款,如果是申请退款,查看是否有记录,如果有记录,是否已经退款
  21. const type = _.get(others, 'type');
  22. if (type === '0') {
  23. const record = await this.model.find({ order_detail: order_id, 'goods._id': goods_id, type: '0' });
  24. if (record) {
  25. const rs = _.get(record, 'status');
  26. if (rs === '0' || rs === '1') throw new BusinessError(ErrorCode.DATA_EXISTED, '已申请退款,正在等待处理');
  27. else if (rs === '-1') throw new BusinessError(ErrorCode.DATA_EXISTED, '该商品已退款');
  28. }
  29. } else {
  30. // 如果是换货/维修,那么可以重复多次,也就是需要查当前该货物是否有未结束的售后,如果有,则不能添加
  31. const num = await this.model.count({ order_detail: order_id, 'goods._id': goods_id, type, status: [ '2', '3' ] });
  32. if (num) throw new BusinessError(ErrorCode.DATA_INVALID, '商品正在售后中');
  33. }
  34. const { goods: goodsList } = orderDetail;
  35. const goods = goodsList.find(f => ObjectId(f._id).equals(goods_id));
  36. if (!goods) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未在当前订单中搜索到要售后的商品');
  37. const { shop, customer } = orderDetail;
  38. const apply_time = moment().format('YYYY-MM-DD HH:mm:ss');
  39. const obj = { order_detail: order_id, customer, shop, goods, ...others, apply_time, status: '0' };
  40. await this.model.create(obj);
  41. }
  42. async update(filter, update, { projection } = {}) {
  43. assert(filter);
  44. assert(update);
  45. const beforeUpdateResult = await this.beforeUpdate(filter, update);
  46. filter = beforeUpdateResult.filter;
  47. update = beforeUpdateResult.update;
  48. const { _id, id } = filter;
  49. if (_id || id) filter = { _id: ObjectId(_id || id) };
  50. // 检查数据是否存在
  51. const entity = await this.model.findOne(filter).exec();
  52. if (!entity) throw new BusinessError(ErrorCode.DATA_NOT_EXIST);
  53. // 修改数据
  54. try {
  55. this.tran.update('AfterSale', entity._id, update);
  56. await this.tran.run();
  57. const type = _.get(entity, 'type');
  58. const status = _.get(update, 'status');
  59. // 同意退款,则直接进行退款,然后再将状态修改为已退款
  60. if (type === '0' && status === '1') {
  61. await this.toRefund({ afterSale_id: entity._id, goods_id: _.get(entity, 'goods._id') }, this.tran);
  62. this.tran.update('AfterSale', entity._id, { status: '-1' });
  63. }
  64. await this.tran.run();
  65. } catch (error) {
  66. console.error(error);
  67. await this.tran.rollback();
  68. throw new BusinessError(ErrorCode.SERVICE_FAULT, '售后:修改失败');
  69. }
  70. const reSearchData = await this.model.findOne(filter, projection).exec();
  71. return reSearchData;
  72. }
  73. /**
  74. * 退款
  75. * @param {Object} param 参数
  76. * @param param.afterSale_id 售后申请id
  77. * @param param.goods_id 商品规格id
  78. * @param {Transaction} tran 事务的实例
  79. */
  80. async toRefund({ afterSale_id, goods_id }, tran) {
  81. const data = await this.model.findById(afterSale_id);
  82. if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到售后信息');
  83. const { populate } = this.ctx.service.trade.orderDetail.getRefMods();
  84. const orderDetail = await this.orderDetailModel.findById(data.order_detail).populate(populate);
  85. if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到售后信息的订单');
  86. // 计算商品原价
  87. let money = this.ctx.multiply(_.get(data, 'goods.sell_money'), _.get(data, 'goods.buy_num'));
  88. const reason = _.get(data, 'desc');
  89. const order_no = _.get(orderDetail, 'order.pay.pay_no');
  90. if (!order_no) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付订单号');
  91. // 查查优惠中,是否有这个商品:将这个商品的每一个优惠券的优惠部分都加一起,然后用原价-优惠价=实付价
  92. const discount_detail = _.get(orderDetail, 'total_detail.discount_detail');
  93. if (discount_detail) {
  94. let discountMoney = 0;
  95. for (const uc_id in discount_detail) {
  96. const detail = discount_detail[uc_id];
  97. const gd = detail[goods_id];
  98. if (gd && !_.get(gd, 'refund', false)) {
  99. discountMoney = this.ctx.plus(discountMoney, gd.discountMoney);
  100. gd.refund = true;
  101. }
  102. }
  103. money = this.ctx.minus(money, discountMoney);
  104. }
  105. // 找下当前这个订单有多少次售后记录
  106. let num = await this.model.count({ order_detail: data.order_detail });
  107. num += 1;
  108. // 组成退款单号
  109. const out_refund_no = `${order_no}-r${num}`;
  110. const obj = { reason, money, order_no, out_refund_no };
  111. // 退款请求
  112. await this.ctx.service.trade.pay.refund(obj);
  113. // 检查优惠券是否都退了
  114. let allRefund = true;
  115. for (const dd of discount_detail) {
  116. for (const d in dd) {
  117. if (!_.get(d, 'refund')) {
  118. allRefund = false;
  119. break;
  120. }
  121. }
  122. }
  123. if (allRefund) {
  124. // 优惠券部分全都退了,那就把优惠券退了
  125. const couponIds = Object.keys(discount_detail);
  126. for (const id of couponIds) {
  127. tran.update('UserCoupon', id, { status: '0' });
  128. }
  129. }
  130. // 修改订单详情的优惠券标记
  131. tran.update('OrderDetail', orderDetail._id, { 'total_detail.discount_detail': discount_detail });
  132. }
  133. }
  134. module.exports = AfterSaleService;