'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'); const { ObjectId } = require('mongoose').Types; // class CartService extends CrudService { constructor(ctx) { super(ctx, 'cart'); this.model = this.ctx.model.Trade.Cart; this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec; this.platformActModel = this.ctx.model.System.PlatformAct; this.gjaModel = this.ctx.model.Shop.GoodsJoinAct; this.setModel = this.ctx.model.Shop.GoodsSet; this.shopModel = this.ctx.model.Shop.Shop; this.goodsModel = this.ctx.model.Shop.Goods; } /** * 返回购物车需要的数据格式 ** 添加促销部分: ** 买赠,特价,加价购: 需要查询每个商品是否参与了这三项活动,需要给出对应数据 ** 满减/折:不需要在购物车出现 ** 套装:最后下单时组合,购物车中不需要额外修改 * @param {Object} query 查询条件 * @param query.customer 顾客id * @return {Array} */ async selfCart({ customer }) { assert(customer, '缺少顾客信息'); let data = await this.model.find({ customer }); if (data.length > 0) { data = JSON.parse(JSON.stringify(data)); data = data.map(i => ({ ...i, cart_id: i._id })); } data = await this.getCartGoodsAct(data); const actList = await this.ctx.service.trade.order.getActList(data); const pageData = await this.ctx.service.trade.order.getPageData(data, actList); return pageData; } /** * 实时获取购物车内的商品参与的活动 * @param {Array} data 购物车商品 */ async getCartGoodsAct(data) { data = data.map(i => ({ ...i, act: [] })); // 实时匹配活动问题 const platformActList = await this.platformActModel.find({ is_use: '0' }); const platform_act = platformActList.map(i => ObjectId(i._id).toString()); for (const cart of data) { const { goodsSpec: spec_id, goods: goods_id, act = [], is_set = '1', set_id } = cart; if (is_set === '1' || !set_id) { // 非套装处理, 套装不参与其他活动 const gjaList = await this.gjaModel.find({ platform_act, 'goods._id': ObjectId(goods_id).toString(), 'spec._id': ObjectId(spec_id).toString() }); for (const gja of gjaList) { const { platform_act_type: type, platform_act } = gja; // 加价购需要额外判断是否是基础商品 if (type === '4') { const goods_type = _.get(gja, 'config.goods_type'); if (goods_type === 'basic') act.push(platform_act); } else act.push(platform_act); } cart.act = _.uniq(act); } } return data; } /** ** 创建购物车信息,若找到该店的该商品规格.进行库存校验 ** 数量进行合并; ** 反之创建 ** 添加促销活动部分: ** 买赠,特价,加价购: 不需要在创建时处理,而是在查询时实时处理,显示相关信息 ** 满减/折:不需要在购物车出现 ** 套装:只有套装需要记录在购物车中,以同一商品形式出现 * @param {Object} body 请求参数 * @param body.num 数量 */ async create({ num, ...body }) { const { customer, shop, goods, goodsSpec, is_set = '1', set_id } = body; assert(customer, '缺少顾客信息'); if (is_set === '1') { assert(shop, '缺少店铺信息'); assert(goods, '缺少商品信息'); assert(goodsSpec, '缺少商品规格信息'); } else assert(set_id, '缺少套装id'); if (is_set === '1') { // 非套装 添加购物车 const query = _.pick(body, [ 'customer', 'shop', 'goods', 'goodsSpec' ]); const data = await this.model.findOne(query); if (data) { const buyNum = this.ctx.plus(data.num, num); const { enough, msg } = await this.checkGoodsNum({ goodsSpecId: goodsSpec, num: buyNum }); if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg); data.num = buyNum; await data.save(); } else { const { enough, msg } = await this.checkGoodsNum({ goodsSpecId: goodsSpec, num }); if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg); await this.model.create({ num, ...body }); } } else { // 套装添加购物车 const query = _.pick(body, [ 'customer', 'set_id' ]); const data = await this.model.findOne(query); if (data) { // 计算套装购买数量 const buyNum = this.ctx.plus(data.num, num); const { enough, msg } = await this.checkSetGoodsNum(data, buyNum); if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg); data.num = buyNum; await data.save(); } else { const { enough, msg, shop } = await this.checkSetGoodsNum(body, num); if (!enough) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg); await this.model.create({ shop, num, ...body }); } } } /** * 检查套装购物车库存是否足够 * @param {Object} data 套装的购物车数据 * @param {Number} buyNum 套装购买数量 */ async checkSetGoodsNum(data, buyNum) { const { set_id } = data; const setData = await this.setModel.findById(set_id).lean(); const { shop } = setData; const shopData = await this.shopModel.findById(shop); const shopRes = this.ctx.service.util.trade.checkShop(shopData); if (shopRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, shopRes.msg); const { set } = setData; const stockList = []; for (const sd of set) { const { goods, spec, set_num } = sd; const goodsData = await this.goodsModel.findById(goods); const goodsRes = this.ctx.service.util.trade.checkGoods(goodsData); if (goodsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, goodsRes.msg); const goodsSpecData = await this.goodsSpecModel.findById(spec); const setGoodsNum = this.ctx.multiply(buyNum, set_num); const gsRes = this.ctx.service.util.trade.checkGoodsSpec(goodsSpecData, setGoodsNum, '0'); if (gsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, gsRes.msg); const { num: gsnum = 0 } = goodsSpecData; // 应该用 库存/每套的件数 向下取整作为库存量 const stock_num = _.floor(this.ctx.divide(gsnum, set_num)); stockList.push(stock_num); } const total = _.min(stockList); return { enough: true, total, shop }; } /** * 检查商品规格的库存 * @param {String} goodsSpecId 商品规格id * @param {String} cartId 购物车id * @param {Number} num 需要的库存数量 * @param {Boolean} update 修改购物车 * @param {String} set_id 套装id * @return {Boolean} true:库存满足购物车条件;false:库存不满足条件 */ async checkGoodsNum({ cartId, goodsSpecId, num, set_id }, update = true) { if (set_id) { // 套装查询库存 const cartData = await this.model.findById(cartId); const res = await this.checkSetGoodsNum(cartData, num); if (update && cartId) { await this.model.updateOne({ _id: cartId }, { num }); } return res; } const { populate } = this.ctx.service.shop.goodsSpec.getRefMods(); const goodsSpec = await this.goodsSpecModel.findById(goodsSpecId).populate(populate); if (!goodsSpec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到商品的规格数据'); const goods = _.get(goodsSpec, 'goods'); const shop = _.get(goods, 'shop'); const shopRes = this.ctx.service.util.trade.checkShop(shop); if (shopRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, shopRes.msg); const goodsRes = this.ctx.service.util.trade.checkGoods(goods); if (goodsRes.result !== true) throw new BusinessError(ErrorCode.DATA_INVALID, goodsRes.msg); const gsRes = this.ctx.service.util.trade.checkGoodsSpec(goodsSpec, num); const { num: gsnum = 0 } = goodsSpec; if (gsRes.result !== true) { return { enough: false, total: gsnum, msg: gsRes.msg }; } if (update && cartId) { await this.model.updateOne({ _id: cartId }, { num }); } return { enough: true, total: gsnum }; } } module.exports = CartService;