'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 GoodsService extends CrudService { constructor(ctx) { super(ctx, 'goods'); this.goodsModel = this.ctx.model.Shop.Goods; this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec; this.gjaModel = this.ctx.model.Shop.GoodsJoinAct; this.goodsTagsModel = this.ctx.model.System.GoodsTags; this.platformActModel = this.ctx.model.System.PlatformAct; this.gjaModel = this.ctx.model.Shop.GoodsJoinAct; this.actTagsModel = this.ctx.model.System.ActTags; } /** * * @param {Object} query 查询条件 * @param query.id 商品数据id */ async goodsDetail({ id }) { assert(id, '缺少商品信息'); const pipeline = [{ $match: { _id: ObjectId(id) } }]; const goodsProject = { file: 1, tags: 1, name: 1, shot_brief: 1, brief: 1, send_time: 1, shop: 1, view_num: 1, act_tags: 1, sell_num: 1 }; // 将 活动标签都进行数据替换 pipeline.push({ $lookup: { from: 'actTags', localField: 'act_tags', foreignField: 'value', pipeline: [{ $match: { show_goods: '0' } }, { $project: { label: 1 } }], as: 'act_tags', }, }); // 找店铺信息 pipeline.push({ $addFields: { shop_id: { $toObjectId: '$shop' } } }); pipeline.push({ $lookup: { from: 'shop', localField: 'shop_id', foreignField: '_id', pipeline: [ { $addFields: { shop_id: { $toString: '$_id' } } }, // 计算店铺商品总数 { $lookup: { from: 'goods', localField: 'shop_id', foreignField: 'shop', pipeline: [{ $match: { status: '1' } }, { $count: 'gn' }], as: 'gn', }, }, { $project: { logo: 1, name: 1, person: 1, phone: 1, _id: 1, goods_score: 1, send_score: 1, service_score: 1, goods_num: { $first: '$gn.gn' } } }, ], as: 'shopInfo', }, }); pipeline.push({ $project: { ...goodsProject, shopInfo: { $first: '$shopInfo' } } }); // 找规格 pipeline.push({ $addFields: { goods_id: { $toString: '$_id' } } }); pipeline.push({ $lookup: { from: 'goodsSpec', localField: 'goods_id', foreignField: 'goods', pipeline: [ { $project: { sell_money: { $toDouble: '$sell_money' }, flow_money: { $toDouble: '$flow_money' }, freight: { $toDouble: '$freight' }, name: 1, num: 1, can_group: 1, group_config: 1, file: 1, }, }, ], as: 'specs', }, }); pipeline.push({ $project: { ...goodsProject, goods_id: 1, specs: 1, shopInfo: 1 } }); // 找到商品是否参与活动,且该活动是否正在进行 pipeline.push({ $lookup: { from: 'goodsJoinAct', localField: 'goods_id', foreignField: 'goods._id', pipeline: [ { $addFields: { pa: { $toObjectId: '$platform_act' } } }, { $lookup: { from: 'platformAct', localField: 'pa', foreignField: '_id', pipeline: [{ $match: { is_use: '0' } }], as: 'act', }, }, { $unwind: '$act' }, { $replaceRoot: { newRoot: '$act' } }, { $project: { act_time: 1, config: 1, type: 1 } }, ], as: 'act', }, }); // 整理数据 pipeline.push({ $project: { _id: 0, goods: { _id: '$$CURRENT._id', brief: '$$CURRENT.brief', file: '$$CURRENT.file', name: '$$CURRENT.name', send_time: '$$CURRENT.send_time', tags: '$$CURRENT.tags', act_tags: '$$CURRENT.act_tags', shot_brief: '$$CURRENT.shot_brief', view_num: '$$CURRENT.view_num', sell_num: '$$CURRENT.sell_num', }, shop: '$shopInfo', specs: '$specs', act: '$act', }, }); const res = await this.goodsModel.aggregate(pipeline); let data = _.head(res); if (data) data = JSON.parse(JSON.stringify(data)); if (data.goods.tags.length > 0) { const nt = []; for (const t of data.goods.tags) { const code = _.last(t); const data = await this.goodsTagsModel.findOne({ code }, 'label'); nt.push(data); } data.goods.tags = nt; } for (const d of data.act) { const { tag, text, aboutList } = await this.ctx.service.system.platformAct.getActText(d, data.goods); d.text = text; d.tag = tag; d.list = aboutList; if (d.type === '2') { // 处理赠品与规格的显示问题 for (const i of d.list) { const spec = data.specs.find(f => f._id === i.spec); if (spec) i.spec_name = _.get(spec, 'name'); } } } data.act = _.uniqBy(data.act, '_id'); return data; } async indexGoodsList(condition, { skip = 0, limit = 20 } = {}) { condition = this.dealFilter(condition); const pipeline = [{ $match: { status: { $ne: '0' } } }]; // { $sort: { sort: 1 } }, const { view_num, sell_num, sell_money, name, shop, tags, act_tags } = condition; let sort = {}; if (view_num) sort.view_num = parseInt(view_num); if (sell_num) sort.sell_num = parseInt(sell_num); if (sell_money) sort.sell_money = parseInt(sell_money); if (name) pipeline.push({ $match: { name: new RegExp(name) } }); if (shop) pipeline.push({ $match: { shop } }); if (tags) pipeline.push({ $match: { tags: { $elemMatch: { $elemMatch: { $eq: tags } } } } }); if (act_tags) pipeline.push({ $match: { act_tags: { $elemMatch: { $eq: act_tags } } } }); pipeline.push({ $addFields: { goods_id: { $toString: '$_id' }, create_time: { $dateToString: { date: '$meta.createdAt', format: '%Y-%m-%d %H:%M:%S', timezone: '+08:00' } } }, }); // 表关联 pipeline.push({ $lookup: { from: 'goodsSpec', localField: 'goods_id', foreignField: 'goods', as: 'specs', }, }); // 按照规格平铺数据 pipeline.push({ $unwind: '$specs' }); // 格式化平铺后的数据 pipeline.push({ $project: { name: 1, view_num: 1, sell_num: 1, file: 1, sort: 1, create_time: 1, sell_money: { $toDouble: '$specs.sell_money' }, flow_money: { $toDouble: '$specs.flow_money' }, num: '$specs.num', act_tags: 1, }, }); pipeline.push({ $group: { _id: '$_id', data: { $min: '$$CURRENT' }, }, }); pipeline.push({ $project: { name: '$data.name', view_num: '$data.view_num', sell_num: '$data.sell_num', file: '$data.file', sell_money: '$data.sell_money', flow_money: '$data.flow_money', num: '$data.num', create_time: '$data.create_time', sort: '$data.sort', act_tags: '$data.act_tags', }, }); if (Object.keys(sort).length <= 0) sort = { sort: -1, create_time: -1 }; pipeline.push({ $sort: { ...sort, sort: -1, create_time: -1 } }); // 分页处理 const qpipeline = _.cloneDeep(pipeline); if (parseInt(skip)) qpipeline.push({ $skip: parseInt(skip) }); if (parseInt(limit)) qpipeline.push({ $limit: parseInt(limit) }); let list = await this.goodsModel.aggregate(qpipeline); list = list.map(i => { const obj = _.pick(i, [ 'name', 'file', 'num', 'flow_money', 'sell_money', 'view_num', '_id', 'actTagsShow', 'act_tags' ]); return obj; }); // 处理活动标签 await this.getActTags(list); // 实时匹配活动 // 只找: 买赠;特价;满减/折; 特价需要修改价格; 剩下的打标签 const platformActList = await this.platformActModel.find({ is_use: '0', type: [ '2', '3', '5', '6' ] }); await this.getAboutAct(platformActList, list); const tpipeline = _.cloneDeep(pipeline); tpipeline.push({ $count: 'total' }); const total = await this.goodsModel.aggregate(tpipeline); return { list, total: _.get(_.head(total), 'total', 0) }; } async getActTags(list) { let tags = list.map(i => i.act_tags); tags = _.flattenDeep(tags); tags = _.uniq(tags); tags = _.compact(tags); const tagsData = await this.actTagsModel.find({ value: tags, show_goods: '0' }); for (const i of list) { const { act_tags = [] } = i; const actTagsShow = []; if (act_tags.length > 0) { for (const t of act_tags) { const r = tagsData.find(f => f.value === t); if (r) actTagsShow.push({ label: r.label }); } } i.actTagsShow = actTagsShow; delete i.act_tags; } } /** * 处理商品有关活动部分 * @param {Array} platformActList 正在进行中的活动 * @param {Array} list 查询商品 */ async getAboutAct(platformActList, list) { const platform_act = platformActList.map(i => ObjectId(i._id).toString()); for (const goods of list) { const { _id: goods_id, p_act = [], sell_money } = goods; const gjaList = await this.gjaModel.find({ platform_act, 'goods._id': ObjectId(goods_id).toString() }); if (gjaList.length <= 0) continue; for (const gja of gjaList) { const { platform_act_type: type } = gja; if (type === '2' && gjaList.length > 0) p_act.push('买赠'); else if (type === '3' && gjaList.length > 0) { p_act.push('特价'); let sortList = JSON.parse(JSON.stringify(gjaList)); sortList = sortList.map(i => ({ ...i, sp_price: _.get(i, 'config.sp_price', 0) })); sortList = _.orderBy(sortList, [ 'sp_price' ], [ 'asc' ]); sortList = sortList.filter(f => this.ctx.minus(0, f.sp_price) < 0); const lp = _.get(_.head(sortList), 'sp_price'); if (lp && this.ctx.minus(lp, sell_money) < 0) goods.sell_money = lp; } else if (type === '5' && gjaList.length > 0) p_act.push('满减'); else if (type === '6' && gjaList.length > 0) p_act.push('满折'); goods.p_act = _.uniq(p_act); } } } async indexActTagsGoods() { // 将使用中且展示在首页的查出来排序 const list = await this.ctx.model.System.ActTags.find({ status: '0', show_index: '0' }).sort({ sort: 1 }); const result = []; for (const t of list) { const { label, value } = t; const list = await this.searchActTagsGoods(value); const arr = []; for (const g of list) { const obj = { url: _.get(g, 'file'), name: _.get(g, 'name'), id: _.get(g, '_id'), value, }; if (arr.length === 0) { obj.title = label; } arr.push(obj); } result.push({ list: arr }); } return result; } async searchActTagsGoods(act_tags, limit = 2) { const pipeline = [{ $sort: { 'meta.createdAt': -1 } }, { $match: { status: { $ne: '0' }, act_tags } }]; pipeline.push({ $project: { name: 1, file: 1 } }); if (parseInt(limit)) pipeline.push({ $limit: parseInt(limit) }); const list = await this.goodsModel.aggregate(pipeline); return list; } } module.exports = GoodsService;