123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- 'use strict';
- const { CrudService } = require('naf-framework-mongoose-free/lib/service');
- const { BusinessError, ErrorCode } = require('naf-core').Error;
- const _ = require('lodash');
- const assert = require('assert');
- //
- class TradeService extends CrudService {
- constructor(ctx) {
- super(ctx, 'trade');
- this.redis = this.app.redis;
- this.redisKey = this.app.config.redisKey;
- this.redisTimeout = this.app.config.redisTimeout;
- this.shopModel = this.ctx.model.Shop.Shop;
- this.goodsModel = this.ctx.model.Shop.Goods;
- this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
- this.cartModel = this.ctx.model.Trade.Cart;
- this.setModel = this.ctx.model.Shop.GoodsSet;
- }
- /**
- * 检查选中的购物车是否符合购买的条件
- * @param {Object} body 请求体
- * @param body.cartIds 选中的购物车id列表
- * @param {Boolean} makeCache 生成缓存
- */
- async checkCartBuy({ cartIds }, makeCache = true) {
- const list = await this.cartModel.find({ _id: cartIds });
- let result = { result: true };
- for (const i of list) {
- const res = await this.checkCanBuy(i, false);
- if (res.result !== true) {
- result = res;
- break;
- }
- }
- if (makeCache) {
- const key = await this.makeOrderKey(cartIds);
- result.key = key;
- }
- return result;
- }
- /**
- * 检测是否可以购买该物品
- * @param {Object} data 查询条件
- * @param param.shop 商店id
- * @param param.goods 商品id
- * @param param.goodsSpec 商品规格id
- * @param param.num 购买数量
- * @param param.type 订单类型: 0:常规单 1:团购单,需要按团购价计算
- * @param param.group 团id
- * @param param.inviter 邀请用户id 返现部分
- * @param param.is_set 是否是套装,默认 1 不是套装
- * @param params.set_id 套装id
- * @param makeCache 生成缓存
- */
- async checkCanBuy(data, makeCache = true) {
- if (!_.isArray(data)) data = [ data ];
- let result = { result: true };
- for (const i of data) {
- const { is_set = '1' } = i;
- if (is_set === '1') {
- // 非套装,使用正常的判断
- result = await this.checkGoodsCanBuy(i);
- } else {
- // 套装,使用套装判断
- result = await this.checkSetCanBuy(i);
- }
- if (result.result !== true) break;
- }
- // #endregion
- if (result.result && makeCache) {
- const key = await this.makeOrderKey(data);
- result.key = key;
- }
- return result;
- }
- /**
- * 套装判断是否可以购买
- * @param {Object} data 购物车/直接购买 数据对象
- */
- async checkSetCanBuy(data) {
- const { num, shop } = data;
- // 取set_id是在购物车检查是否可以购买; data._id是下单时使用
- // 已经将购物车那边的数据加上了set_id,不过先这么写,兼容
- const set_id = _.get(data, 'set_id', data._id);
- let result = { result: true };
- if (!shop) {
- result.result = false;
- result.msg = '缺少店铺信息';
- return result;
- }
- // 1.检查商店是否正常运行
- const shopData = await this.shopModel.findById(shop);
- const shopRes = this.checkShop(shopData);
- if (shopRes.result !== true) {
- result = shopRes;
- return result;
- }
- const setData = await this.setModel.findById(set_id).lean();
- const { set, is_use } = setData;
- if (is_use === '1') return { result: false, msg: '套装已下架' };
- for (const sd of set) {
- const { goods, spec, set_num } = sd;
- if (!goods) {
- result.result = false;
- result.msg = '缺少商品信息';
- break;
- }
- if (!spec) {
- result.result = false;
- result.msg = '缺少商品规格信息';
- break;
- }
- if (!num) {
- result.result = false;
- result.msg = '缺少购买数量';
- break;
- }
- // 2.检查商品是否可以购买
- const goodsData = await this.goodsModel.findById(goods);
- const goodsRes = this.checkGoods(goodsData);
- if (goodsRes.result !== true) {
- result = goodsRes;
- break;
- }
- // 3.根据套装库存设置判断是走各个商品库存还是走套装库存
- // 走各个商品库存
- const goodsSpecData = await this.goodsSpecModel.findById(spec);
- // 单独计算下商品数量 = 购买数量 * 组成套装的数量
- const setGoodsNum = this.ctx.multiply(num, set_num);
- const gsRes = this.checkGoodsSpec(goodsSpecData, setGoodsNum, '0');
- if (gsRes.result !== true) {
- result = gsRes;
- break;
- }
- }
- return result;
- }
- /**
- * 常规商品判断是否可以购买
- * @param {Object} data 购物车/直接购买 数据对象
- */
- async checkGoodsCanBuy(data) {
- let result = { result: true };
- const { shop, goods, goodsSpec, num } = data;
- if (!shop) {
- result.result = false;
- result.msg = '缺少店铺信息';
- return result;
- }
- if (!goods) {
- result.result = false;
- result.msg = '缺少商品信息';
- return result;
- }
- if (!goodsSpec) {
- result.result = false;
- result.msg = '缺少商品规格信息';
- return result;
- }
- if (!num) {
- result.result = false;
- result.msg = '缺少购买数量';
- return result;
- }
- // 1.检查商店是否正常运行
- const shopData = await this.shopModel.findById(shop);
- const shopRes = this.checkShop(shopData);
- if (shopRes.result !== true) {
- result = shopRes;
- return result;
- }
- // 2.检查商品是否可以购买
- const goodsData = await this.goodsModel.findById(goods);
- const goodsRes = this.checkGoods(goodsData);
- if (goodsRes.result !== true) {
- result = goodsRes;
- return result;
- }
- // 3.检验该规格是否可以购买
- const goodsSpecData = await this.goodsSpecModel.findById(goodsSpec);
- const gsRes = this.checkGoodsSpec(goodsSpecData, num);
- if (gsRes.result !== true) {
- result = gsRes;
- return result;
- }
- return result;
- }
- /**
- * 检查商店是否满足条件
- * @param {Object} shopData 店铺数据
- */
- checkShop(shopData) {
- const result = { result: true };
- if (!shopData) {
- result.msg = '未找到店铺';
- result.result = false;
- return result;
- }
- if (shopData.status !== '1') {
- result.msg = '店铺不处于营业状态';
- result.result = false;
- return result;
- }
- return result;
- }
- /**
- * 检查商品是否满足条件
- * @param {Object} goodsData 商品数据
- */
- checkGoods(goodsData) {
- const result = { result: true };
- if (!goodsData) {
- result.msg = '未找到商品';
- result.result = false;
- return result;
- }
- if (goodsData.status === '0') {
- result.msg = '该商品已下架';
- result.result = false;
- return result;
- }
- return result;
- }
- /**
- * 检查商品的规格是否满足购买条件
- * @param {Object} goodsSpecData 商品规格的数据
- * @param {Number} num 购买数量
- * @param {String} is_set 是否是套装;套装不需要检查购买限制
- */
- checkGoodsSpec(goodsSpecData, num, is_set = '1') {
- const result = { result: true };
- if (!goodsSpecData) {
- result.msg = '未找到商品的指定规格';
- result.result = false;
- return result;
- }
- if (goodsSpecData.status !== '0') {
- result.msg = '该规格的商品已下架';
- result.result = false;
- return result;
- }
- if (goodsSpecData.num < num) {
- result.msg = '库存量不足';
- result.result = false;
- return result;
- }
- if (is_set !== '1') return result;
- // 判断是否有购买限制
- if (_.get(goodsSpecData, 'buy_limit', '0') !== '0') {
- // 有购买限制,需要检测购买限制
- const buy_limit = _.get(goodsSpecData, 'buy_limit');
- const limit_num = _.get(goodsSpecData, 'limit_num', 0);
- if (buy_limit === '1') {
- if (num < limit_num) {
- result.msg = `该规格至少购买 ${limit_num} 份, 购买数量不足`;
- result.result = false;
- return result;
- }
- } else if (buy_limit === '2') {
- if (num > limit_num) {
- result.msg = `该规格至多购买 ${limit_num} 份, 购买数量超过上限`;
- result.result = false;
- return result;
- }
- } else if (buy_limit === '3') {
- if (num !== limit_num) {
- result.msg = `该规格只能购买 ${limit_num} 份`;
- result.result = false;
- return result;
- }
- }
- }
- return result;
- }
- // 随机字符串产生函数
- createNonceStr() {
- return Math.random().toString(36).substr(2, 15);
- }
- // 生成key
- async makeOrderKey(data) {
- const str = this.createNonceStr();
- const key = `${this.redisKey.orderKeyPrefix}${str}`;
- const value = JSON.stringify(data);
- await this.redis.set(key, value, 'EX', this.redisTimeout);
- return str;
- }
- }
- module.exports = TradeService;
|