goods.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. const { ObjectId } = require('mongoose').Types;
  7. //
  8. class GoodsService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'goods');
  11. this.goodsModel = this.ctx.model.Shop.Goods;
  12. this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
  13. }
  14. /**
  15. *
  16. * @param {Object} query 查询条件
  17. * @param query.id 商品数据id
  18. */
  19. async goodsDetail({ id }) {
  20. const { populate } = this.ctx.service.shop.goods.getRefMods();
  21. let goods = await this.goodsModel.findById(id, { file: 1, tags: 1, name: 1, shot_brief: 1, brief: 1, send_time: 1, shop: 1, view_num: 1 }).populate(populate);
  22. if (!goods) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到商品数据');
  23. goods = JSON.parse(JSON.stringify(goods));
  24. let specs = await this.goodsSpecModel.find({ goods: id, status: '0' }, { sell_money: 1, flow_money: 1, freight: 1, name: 1, num: 1, can_group: 1, group_config: 1, file: 1 });
  25. specs = JSON.parse(JSON.stringify(specs));
  26. goods = _.omit(goods, [ 'meta', '__v' ]);
  27. const shop = _.pick(goods.shop, [ 'logo', 'name', 'person', 'phone', '_id' ]);
  28. delete goods.shop;
  29. // 2022-10-17反馈问题6:将规格图片和商品图片合并,规格图片在前
  30. for (const spec of specs) {
  31. if (_.isArray(spec.file)) {
  32. goods.file = [ ...spec.file, ..._.get(goods, 'file', []) ];
  33. }
  34. }
  35. // goods: 商品信息; specs:商品规格信息; shop:店铺信息
  36. const returnData = { goods, specs, shop };
  37. // 添加浏览次数
  38. await this.goodsModel.updateOne({ _id: id }, { view_num: (goods.view_num || 0) + 1 });
  39. return returnData;
  40. }
  41. async indexGoodsList(condition, { skip = 0, limit = 20 } = {}) {
  42. condition = this.dealFilter(condition);
  43. const pipline = [{ $sort: { sort: 1 } }, { $match: { status: { $ne: '0' } } }];
  44. const { view_num, sell_num, sell_money, name, shop, tags } = condition;
  45. const sort = {};
  46. if (view_num) sort.view_num = view_num;
  47. if (sell_num) sort.sell_num = sell_num;
  48. if (sell_money) sort.sell_money = sell_money;
  49. if (name) pipline.push({ $match: { name: new RegExp(name) } });
  50. if (shop) pipline.push({ $match: { shop } });
  51. if (tags) pipline.push({ $match: { tags: { $elemMatch: { $elemMatch: { $eq: tags } } } } });
  52. pipline.push({ $addFields: { goods_id: { $toString: '$_id' } } });
  53. // 表关联
  54. pipline.push({
  55. $lookup: {
  56. from: 'goodsSpec',
  57. localField: 'goods_id',
  58. foreignField: 'goods',
  59. as: 'specs',
  60. },
  61. });
  62. // 按照规格平铺数据
  63. pipline.push({ $unwind: '$specs' });
  64. // 格式化平铺后的数据
  65. // 2022-10-17反馈-问题6:将规格图片也拿出来
  66. // TODO: 整理规格图片与正常的商品图片
  67. pipline.push({
  68. $project: { name: 1, view_num: 1, sell_num: 1, file: 1, sell_money: { $toDouble: '$specs.sell_money' }, createdAt: '$meta.createdAt', spec_file: '$specs.file' },
  69. });
  70. pipline.push({
  71. $group: {
  72. _id: '$_id',
  73. name: { $first: '$name' },
  74. view_num: { $first: '$view_num' },
  75. sell_num: { $first: '$sell_num' },
  76. sell_money: { $min: '$sell_money' },
  77. file: { $first: '$file' },
  78. spec_file: { $first: '$spec_file' },
  79. createdAt: { $first: '$createdAt' },
  80. },
  81. });
  82. // 排序处理
  83. if (view_num) pipline.push({ $sort: { view_num } });
  84. if (sell_num) pipline.push({ $sort: { sell_num } });
  85. if (sell_money) pipline.push({ $sort: { sell_money } });
  86. // 分页处理
  87. const qPipline = _.cloneDeep(pipline);
  88. qPipline.push({ $sort: { createdAt: -1 } });
  89. if (parseInt(skip)) qPipline.push({ $skip: parseInt(skip) });
  90. if (parseInt(limit)) qPipline.push({ $limit: parseInt(limit) });
  91. let list = await this.goodsModel.aggregate(qPipline);
  92. // 处理合并图片
  93. list = list.map(i => {
  94. if (_.isArray(i.spec_file)) {
  95. i.file = [ ...i.spec_file, ...i.file ];
  96. }
  97. return i;
  98. });
  99. const tPipline = _.cloneDeep(pipline);
  100. tPipline.push({ $count: 'total' });
  101. const total = await this.goodsModel.aggregate(tPipline);
  102. return { list, total: _.get(_.head(total), 'total', 0) };
  103. }
  104. async indexActTagsGoods() {
  105. const list = await this.ctx.model.System.ActTags.find({ status: '0' }).sort({ sort: 1 });
  106. const result = [];
  107. for (const t of list) {
  108. const { label, value } = t;
  109. const list = await this.searchActTagsGoods(value);
  110. const arr = [];
  111. for (const g of list) {
  112. const obj = {
  113. url: _.get(g, 'file'),
  114. name: _.get(g, 'name'),
  115. id: _.get(g, '_id'),
  116. };
  117. if (arr.length === 0) {
  118. obj.title = label;
  119. }
  120. arr.push(obj);
  121. }
  122. result.push({ list: arr });
  123. }
  124. return result;
  125. }
  126. async searchActTagsGoods(act_tags, limit = 2) {
  127. const pipline = [{ $sort: { 'meta.createdAt': -1 } }, { $match: { status: { $ne: '0' }, act_tags } }];
  128. pipline.push({ $project: { name: 1, file: 1 } });
  129. if (parseInt(limit)) pipline.push({ $limit: parseInt(limit) });
  130. const list = await this.goodsModel.aggregate(pipline);
  131. return list;
  132. }
  133. }
  134. module.exports = GoodsService;