trade.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. //
  7. class TradeService extends CrudService {
  8. constructor(ctx) {
  9. super(ctx, 'trade');
  10. this.redis = this.app.redis;
  11. this.redisKey = this.app.config.redisKey;
  12. this.redisTimeout = this.app.config.redisTimeout;
  13. this.shopModel = this.ctx.model.Shop.Shop;
  14. this.goodsModel = this.ctx.model.Shop.Goods;
  15. this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
  16. this.cartModel = this.ctx.model.Trade.Cart;
  17. }
  18. /**
  19. * 检查选中的购物车是否符合购买的条件
  20. * @param {Object} body 请求体
  21. * @param body.cartIds 选中的购物车id列表
  22. * @param {Boolean} makeCache 生成缓存
  23. */
  24. async checkCartBuy({ cartIds }, makeCache = true) {
  25. const list = await this.cartModel.find({ _id: cartIds });
  26. let result = { result: true };
  27. for (const i of list) {
  28. const res = await this.checkCanBuy(i, false);
  29. if (res.result !== true) {
  30. result = res;
  31. break;
  32. }
  33. }
  34. if (makeCache) {
  35. const key = await this.makeOrderKey(cartIds);
  36. result.key = key;
  37. }
  38. return result;
  39. }
  40. /**
  41. * 检测是否可以购买该物品
  42. * @param {Object} data 查询条件
  43. * @param param.shop 商店id
  44. * @param param.goods 商品id
  45. * @param param.goodsSpec 商品规格id
  46. * @param param.num 购买数量
  47. * @param param.type 订单类型: 0:常规单 1:团购单,需要按团购价计算
  48. * @param param.group 团id
  49. * @param param.inviter 邀请用户id 返现部分
  50. * @param makeCache 生成缓存
  51. */
  52. async checkCanBuy(data, makeCache = true) {
  53. if (!_.isArray(data)) data = [ data ];
  54. let result = { result: true };
  55. for (const i of data) {
  56. const { shop, goods, goodsSpec, num } = i;
  57. if (!shop) {
  58. result.result = false;
  59. result.msg = '缺少店铺信息';
  60. break;
  61. }
  62. if (!goods) {
  63. result.result = false;
  64. result.msg = '缺少商品信息';
  65. break;
  66. }
  67. if (!goodsSpec) {
  68. result.result = false;
  69. result.msg = '缺少商品规格信息';
  70. break;
  71. }
  72. if (!num) {
  73. result.result = false;
  74. result.msg = '缺少购买数量';
  75. break;
  76. }
  77. // 1.检查商店是否正常运行
  78. const shopData = await this.shopModel.findById(shop);
  79. const shopRes = this.checkShop(shopData);
  80. if (shopRes.result !== true) {
  81. result = shopRes;
  82. break;
  83. }
  84. // 2.检查商品是否可以购买
  85. const goodsData = await this.goodsModel.findById(goods);
  86. const goodsRes = this.checkGoods(goodsData);
  87. if (goodsRes.result !== true) {
  88. result = goodsRes;
  89. break;
  90. }
  91. // 3.检验该规格是否可以购买
  92. const goodsSpecData = await this.goodsSpecModel.findById(goodsSpec);
  93. const gsRes = this.checkGoodsSpec(goodsSpecData, num);
  94. if (gsRes.result !== true) {
  95. result = gsRes;
  96. break;
  97. }
  98. }
  99. // #endregion
  100. if (result.result && makeCache) {
  101. const key = await this.makeOrderKey(data);
  102. result.key = key;
  103. }
  104. return result;
  105. }
  106. /**
  107. * 检查商店是否满足条件
  108. * @param {Object} shopData 店铺数据
  109. */
  110. checkShop(shopData) {
  111. const result = { result: true };
  112. if (!shopData) {
  113. result.msg = '未找到店铺';
  114. result.result = false;
  115. return result;
  116. }
  117. if (shopData.status !== '1') {
  118. result.msg = '店铺不处于营业状态';
  119. result.result = false;
  120. return result;
  121. }
  122. return result;
  123. }
  124. /**
  125. * 检查商品是否满足条件
  126. * @param {Object} goodsData 商品数据
  127. */
  128. checkGoods(goodsData) {
  129. const result = { result: true };
  130. if (!goodsData) {
  131. result.msg = '未找到商品';
  132. result.result = false;
  133. return result;
  134. }
  135. if (goodsData.status === '0') {
  136. result.msg = '该商品已下架';
  137. result.result = false;
  138. return result;
  139. }
  140. return result;
  141. }
  142. /**
  143. * 检查商品的规格是否满足购买条件
  144. * @param {Object} goodsSpecData 商品规格的数据
  145. * @param {Number} num 购买数量
  146. */
  147. checkGoodsSpec(goodsSpecData, num) {
  148. const result = { result: true };
  149. if (!goodsSpecData) {
  150. result.msg = '未找到商品的指定规格';
  151. result.result = false;
  152. return result;
  153. }
  154. if (goodsSpecData.status !== '0') {
  155. result.msg = '该规格的商品已下架';
  156. result.result = false;
  157. return result;
  158. }
  159. if (goodsSpecData.num < num) {
  160. result.msg = '库存量不足';
  161. result.result = false;
  162. return result;
  163. }
  164. // 判断是否有购买限制
  165. if (_.get(goodsSpecData, 'buy_limit', '0') !== '0') {
  166. // 有购买限制,需要检测购买限制
  167. const buy_limit = _.get(goodsSpecData, 'buy_limit');
  168. const limit_num = _.get(goodsSpecData, 'limit_num', 0);
  169. if (buy_limit === '1') {
  170. if (num < limit_num) {
  171. result.msg = `改规格至少购买 ${limit_num} 份, 购买数量不足`;
  172. result.result = false;
  173. return result;
  174. }
  175. } else if (buy_limit === '2') {
  176. if (num > limit_num) {
  177. result.msg = `改规格至多购买 ${limit_num} 份, 购买数量超过上限`;
  178. result.result = false;
  179. return result;
  180. }
  181. } else if (buy_limit === '3') {
  182. if (num !== limit_num) {
  183. result.msg = `改规格只能购买 ${limit_num} 份`;
  184. result.result = false;
  185. return result;
  186. }
  187. }
  188. }
  189. return result;
  190. }
  191. // 随机字符串产生函数
  192. createNonceStr() {
  193. return Math.random().toString(36).substr(2, 15);
  194. }
  195. // 生成key
  196. async makeOrderKey(data) {
  197. const str = this.createNonceStr();
  198. const key = `${this.redisKey.orderKeyPrefix}${str}`;
  199. const value = JSON.stringify(data);
  200. await this.redis.set(key, value, 'EX', this.redisTimeout);
  201. return str;
  202. }
  203. }
  204. module.exports = TradeService;