cart.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. //
  8. class CartService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'cart');
  11. this.model = this.ctx.model.Trade.Cart;
  12. this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
  13. this.platformActModel = this.ctx.model.System.PlatformAct;
  14. this.gjaModel = this.ctx.model.Shop.GoodsJoinAct;
  15. this.setModel = this.ctx.model.Shop.GoodsSet;
  16. this.shopModel = this.ctx.model.Shop.Shop;
  17. this.goodsModel = this.ctx.model.Shop.Goods;
  18. }
  19. /**
  20. * 返回购物车需要的数据格式
  21. ** 添加促销部分:
  22. ** 买赠,特价,加价购: 需要查询每个商品是否参与了这三项活动,需要给出对应数据
  23. ** 满减/折:不需要在购物车出现
  24. ** 套装:最后下单时组合,购物车中不需要额外修改
  25. * @param {Object} query 查询条件
  26. * @param query.customer 顾客id
  27. * @return {Array}
  28. */
  29. async selfCart({ customer }) {
  30. assert(customer, '缺少顾客信息');
  31. let data = await this.model.find({ customer });
  32. if (data.length > 0) {
  33. data = JSON.parse(JSON.stringify(data));
  34. data = data.map(i => ({ ...i, cart_id: i._id }));
  35. }
  36. data = await this.getCartGoodsAct(data);
  37. const actList = await this.ctx.service.trade.order.getActList(data);
  38. const pageData = await this.ctx.service.trade.order.getPageData(data, actList);
  39. return pageData;
  40. }
  41. /**
  42. * 实时获取购物车内的商品参与的活动
  43. * @param {Array} data 购物车商品
  44. */
  45. async getCartGoodsAct(data) {
  46. data = data.map(i => ({ ...i, act: [] }));
  47. // 实时匹配活动问题
  48. const platformActList = await this.platformActModel.find({ is_use: '0' });
  49. const platform_act = platformActList.map(i => ObjectId(i._id).toString());
  50. for (const cart of data) {
  51. const { goodsSpec: spec_id, goods: goods_id, act = [], is_set = '1', set_id } = cart;
  52. if (is_set === '1' || !set_id) {
  53. // 非套装处理, 套装不参与其他活动
  54. const gjaList = await this.gjaModel.find({ platform_act, 'goods._id': ObjectId(goods_id).toString(), 'spec._id': ObjectId(spec_id).toString() });
  55. for (const gja of gjaList) {
  56. const { platform_act_type: type, platform_act } = gja;
  57. // 加价购需要额外判断是否是基础商品
  58. if (type === '4') {
  59. const goods_type = _.get(gja, 'config.goods_type');
  60. if (goods_type === 'basic') act.push(platform_act);
  61. } else act.push(platform_act);
  62. }
  63. cart.act = _.uniq(act);
  64. }
  65. }
  66. return data;
  67. }
  68. /**
  69. ** 创建购物车信息,若找到该店的该商品规格.进行库存校验
  70. ** 数量进行合并;
  71. ** 反之创建
  72. ** 添加促销活动部分:
  73. ** 买赠,特价,加价购: 不需要在创建时处理,而是在查询时实时处理,显示相关信息
  74. ** 满减/折:不需要在购物车出现
  75. ** 套装:只有套装需要记录在购物车中,以同一商品形式出现
  76. * @param {Object} body 请求参数
  77. * @param body.num 数量
  78. */
  79. async create({ num, ...body }) {
  80. const { customer, shop, goods, goodsSpec, is_set = '1', set_id } = body;
  81. assert(customer, '缺少顾客信息');
  82. if (is_set === '1') {
  83. assert(shop, '缺少店铺信息');
  84. assert(goods, '缺少商品信息');
  85. assert(goodsSpec, '缺少商品规格信息');
  86. } else assert(set_id, '缺少套装id');
  87. if (is_set === '1') {
  88. // 非套装 添加购物车
  89. const query = _.pick(body, [ 'customer', 'shop', 'goods', 'goodsSpec' ]);
  90. const data = await this.model.findOne(query);
  91. if (data) {
  92. const buyNum = this.ctx.plus(data.num, num);
  93. const { enough, msg } = await this.checkGoodsNum({ goodsSpecId: goodsSpec, num: buyNum });
  94. if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg);
  95. data.num = buyNum;
  96. await data.save();
  97. } else {
  98. const { enough, msg } = await this.checkGoodsNum({ goodsSpecId: goodsSpec, num });
  99. if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg);
  100. await this.model.create({ num, ...body });
  101. }
  102. } else {
  103. // 套装添加购物车
  104. const query = _.pick(body, [ 'customer', 'set_id' ]);
  105. const data = await this.model.findOne(query);
  106. if (data) {
  107. // 计算套装购买数量
  108. const buyNum = this.ctx.plus(data.num, num);
  109. const { enough, msg } = await this.checkSetGoodsNum(data, buyNum);
  110. if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg);
  111. data.num = buyNum;
  112. await data.save();
  113. } else {
  114. const { enough, msg, shop } = await this.checkSetGoodsNum(body, num);
  115. if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg);
  116. await this.model.create({ shop, num, ...body });
  117. }
  118. }
  119. }
  120. /**
  121. * 检查套装购物车库存是否足够
  122. * @param {Object} data 套装的购物车数据
  123. * @param {Number} buyNum 套装购买数量
  124. */
  125. async checkSetGoodsNum(data, buyNum) {
  126. const { set_id } = data;
  127. const setData = await this.setModel.findById(set_id).lean();
  128. const { shop } = setData;
  129. const shopData = await this.shopModel.findById(shop);
  130. const shopRes = this.ctx.service.util.trade.checkShop(shopData);
  131. if (shopRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, shopRes.msg);
  132. const { set } = setData;
  133. const stockList = [];
  134. for (const sd of set) {
  135. const { goods, spec, set_num } = sd;
  136. const goodsData = await this.goodsModel.findById(goods);
  137. const goodsRes = this.ctx.service.util.trade.checkGoods(goodsData);
  138. if (goodsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, goodsRes.msg);
  139. const goodsSpecData = await this.goodsSpecModel.findById(spec);
  140. const setGoodsNum = this.ctx.multiply(buyNum, set_num);
  141. const gsRes = this.ctx.service.util.trade.checkGoodsSpec(goodsSpecData, setGoodsNum, '0');
  142. if (gsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, gsRes.msg);
  143. const { num: gsnum = 0 } = goodsSpecData;
  144. // 应该用 库存/每套的件数 向下取整作为库存量
  145. const stock_num = _.floor(this.ctx.divide(gsnum, set_num));
  146. stockList.push(stock_num);
  147. }
  148. const total = _.min(stockList);
  149. return { enough: true, total, shop };
  150. }
  151. /**
  152. * 检查商品规格的库存
  153. * @param {String} goodsSpecId 商品规格id
  154. * @param {String} cartId 购物车id
  155. * @param {Number} num 需要的库存数量
  156. * @param {Boolean} update 修改购物车
  157. * @param {String} set_id 套装id
  158. * @return {Boolean} true:库存满足购物车条件;false:库存不满足条件
  159. */
  160. async checkGoodsNum({ cartId, goodsSpecId, num, set_id }, update = true) {
  161. if (set_id) {
  162. // 套装查询库存
  163. const cartData = await this.model.findById(cartId);
  164. const res = await this.checkSetGoodsNum(cartData, num);
  165. if (update && cartId) {
  166. await this.model.updateOne({ _id: cartId }, { num });
  167. }
  168. return res;
  169. }
  170. const { populate } = this.ctx.service.shop.goodsSpec.getRefMods();
  171. const goodsSpec = await this.goodsSpecModel.findById(goodsSpecId).populate(populate);
  172. if (!goodsSpec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到商品的规格数据');
  173. const goods = _.get(goodsSpec, 'goods');
  174. const shop = _.get(goods, 'shop');
  175. const shopRes = this.ctx.service.util.trade.checkShop(shop);
  176. if (shopRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, shopRes.msg);
  177. const goodsRes = this.ctx.service.util.trade.checkGoods(goods);
  178. if (goodsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, goodsRes.msg);
  179. const gsRes = this.ctx.service.util.trade.checkGoodsSpec(goodsSpec, num);
  180. const { num: gsnum = 0 } = goodsSpec;
  181. if (gsRes.result !== true) {
  182. return { enough: false, total: gsnum, msg: gsRes.msg };
  183. }
  184. if (update && cartId) {
  185. await this.model.updateOne({ _id: cartId }, { num });
  186. }
  187. return { enough: true, total: gsnum };
  188. }
  189. }
  190. module.exports = CartService;