123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- '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;
- this.goodsConfigModel = this.ctx.model.Shop.GoodsConfig;
- this.userModel = this.ctx.model.User.User;
- this.setModel = this.ctx.model.Shop.GoodsSet;
- }
- /**
- *
- * @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,
- buy_limit: 1,
- limit_num: 1,
- file: 1,
- leader_price: 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');
- const user_id = _.get(this.ctx, 'user._id');
- if (user_id) {
- // 检查团长价格
- const is_leader = await this.ctx.service.user.user.getUserIsLeader(user_id);
- if (is_leader) {
- data.specs = data.specs.map(i => {
- const { sell_money, leader_price } = i;
- const sm = this.ctx.toNumber(sell_money);
- const obj = { o_sell_money: sm };
- if (leader_price) {
- const lp = this.ctx.toNumber(leader_price);
- obj.leader_price = lp;
- obj.sell_money = lp;
- }
- return { ...i, ...obj };
- });
- } else {
- data.specs = data.specs.map(i => _.omit(i, [ 'leader_price' ]));
- }
- } else {
- data.specs = data.specs.map(i => _.omit(i, [ 'leader_price' ]));
- }
- const sets = await this.getGoodsSetList(data);
- data.sets = sets;
- return data;
- }
- /**
- * 套装查询
- * @param data
- */
- async getGoodsSetList(data) {
- const goods = _.get(data, 'goods');
- if (!goods) return [];
- const goodsSetList = await this.setModel.find({ goods: goods._id, is_use: '0' }).lean();
- const arr = [];
- for (const sd of goodsSetList) {
- const { _id, set = [], sell_money, name } = sd;
- const obj = { _id, name, sell_money, goods_total: set.length };
- const newSet = [];
- for (const s of set) {
- const { goods, goods_name, spec, spec_name } = s;
- const goodsData = await this.goodsModel.findById(goods, { file: 1 }).lean();
- const specData = await this.goodsSpecModel.findById(spec, { file: 1 }).lean();
- const file = [ ..._.get(specData, 'file', []), ..._.get(goodsData, 'file', []) ];
- const newSetData = { goods, goods_name, spec_name, file };
- newSet.push(newSetData);
- }
- obj.set = newSet;
- arr.push(obj);
- }
- return arr;
- }
- 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' },
- leader_price: { $toDouble: '$specs.leader_price' },
- 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',
- leader_price: '$data.leader_price',
- 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', 'leader_price' ]);
- if (obj.leader_price) obj.leader_price = this.ctx.toNumber(obj.leader_price);
- 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 user_id = _.get(this.ctx, 'user._id');
- const is_leader = await this.ctx.service.user.user.getUserIsLeader(user_id);
- if (!is_leader) {
- list = list.map(i => _.omit(i, [ 'leader_price' ]));
- } else {
- for (const i of list) {
- const { leader_price, _id: goods } = i;
- if (leader_price) continue;
- let specs = await this.goodsSpecModel.find({ goods, leader_price: { $gt: 0 } }, { leader_price: 1 }).lean();
- specs = _.orderBy(specs, [ 'leader_price' ], [ 'asc' ]);
- const head = _.head(specs);
- i.leader_price = this.ctx.toNumber(_.get(head, 'leader_price', 0));
- }
- }
- 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;
- }
- // 弃用
- async searchLeaderPrice(list) {
- for (const i of list) {
- const { _id: goods } = i;
- let gcl = await this.goodsConfigModel.find({ goods }).lean();
- gcl = _.orderBy(gcl, [ 'leader_price' ], [ 'asc' ]);
- i.leader_price = _.get(_.head(gcl), 'leader_price');
- }
- return list;
- }
- }
- module.exports = GoodsService;
|