lrf 2 лет назад
Родитель
Сommit
cb013f21cb

+ 7 - 1
README.md

@@ -10,4 +10,10 @@
 
 ## 拉取指定分支
 
-`git clone -b ${url}`
+`git clone -b ${branch_name} ${url}`
+
+## 避免冲突,不进行版本合并
+
+1.`git config --global merge.ours.driver true`
+2.设置`.gitattributes`
+3.master 修改忽略文件,提交; 分支修改忽略文件,提交;分支合并master,提交

+ 1 - 1
app/controller/shop/config/.goodsSpec.js

@@ -22,7 +22,7 @@ module.exports = {
         'meta.createdAt@start': 'meta.createdAt@start',
         'meta.createdAt@end': 'meta.createdAt@end',
         goods: 'goods',
-        name: 'name',
+        name: '%name%',
         status: 'status',
         can_group: 'can_group',
         sort: 'sort',

+ 2 - 2
app/controller/system/config/.config.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['title', 'config', 'agree', 'bottom_title', 'bottom_menu'],
+    requestBody: ['reward_day', 'title', 'config', 'agree', 'bottom_title', 'bottom_menu'],
   },
   destroy: {
     params: ['!id'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['title', 'config', 'agree', 'bottom_title', 'bottom_menu'],
+    requestBody: ['reward_day', 'title', 'config', 'agree', 'bottom_title', 'bottom_menu'],
   },
   show: {
     parameters: {

+ 3 - 0
app/controller/trade/config/.afterSale.js

@@ -83,4 +83,7 @@ module.exports = {
   getTransportInfo: {
     params: ['!id'],
   },
+  canRefund: {
+    params: ['!id'],
+  },
 };

+ 18 - 18
app/controller/user/config/.cashOut.js

@@ -1,41 +1,41 @@
-
 module.exports = {
   create: {
-    requestBody: ['!money','!customer','!apply_time','!apply_reason','deal_person','exam_time','exam_reason','status'],
+    requestBody: ['card_name', 'card_bank', 'card', '!money', '!customer', '!apply_time', '!apply_reason', 'deal_person', 'exam_time', 'exam_reason', 'status'],
   },
   destroy: {
-    params: ["!id"],
-    service: "delete",
+    params: ['!id'],
+    service: 'delete',
   },
   update: {
-    params: ["!id"],
-    requestBody: ['money','customer','apply_time','apply_reason','deal_person','exam_time','exam_reason','status'],
+    params: ['!id'],
+    requestBody: ['card_name', 'card_bank', 'card', 'money', 'customer', 'apply_time', 'apply_reason', 'deal_person', 'exam_time', 'exam_reason', 'status'],
   },
   show: {
     parameters: {
-      params: ["!id"],
+      params: ['!id'],
     },
-    service: "fetch",
+    service: 'fetch',
   },
   index: {
     parameters: {
       query: {
-        "meta.createdAt@start": "meta.createdAt@start",
-        "meta.createdAt@end": "meta.createdAt@end",
-        'customer': 'customer' ,
-        'apply_time': 'apply_time' ,
-        'deal_person': 'deal_person' ,
-        'exam_time': 'exam_time' ,
-        'status': 'status' ,
+        'meta.createdAt@start': 'meta.createdAt@start',
+        'meta.createdAt@end': 'meta.createdAt@end',
+        customer: 'customer',
+        apply_time: 'apply_time',
+        deal_person: 'deal_person',
+        exam_time: 'exam_time',
+        status: 'status',
+        card_name: '%card_name%',
       },
       // options: {
       //   "meta.state": 0 // 默认条件
       // },
     },
-    service: "query",
+    service: 'query',
     options: {
-      query: ["skip", "limit"],
-      sort: ["meta.createdAt"],
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
       desc: true,
       count: true,
     },

+ 41 - 0
app/controller/user/config/.notice.js

@@ -0,0 +1,41 @@
+module.exports = {
+  create: {
+    requestBody: ['!customer', 'source', 'source_id', 'time', 'content', 'status'],
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete',
+  },
+  update: {
+    params: ['!id'],
+    requestBody: ['customer', 'source', 'source_id', 'time', 'content', 'status'],
+  },
+  show: {
+    parameters: {
+      params: ['!id'],
+    },
+    service: 'fetch',
+  },
+  index: {
+    parameters: {
+      query: {
+        'time@start': 'time@start',
+        'time@end': 'timet@end',
+        customer: 'customer',
+        source: 'source',
+        source_id: 'source_id',
+        status: 'status',
+      },
+      // options: {
+      //   "meta.state": 0 // 默认条件
+      // },
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['time'],
+      desc: true,
+      count: true,
+    },
+  },
+};

+ 4 - 3
app/controller/user/config/.user.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['name', 'phone', 'password', 'icon', 'birth', 'gender', 'email', 'openid', 'status'],
+    requestBody: ['is_leader', 'name', 'phone', 'password', 'icon', 'birth', 'gender', 'email', 'openid', 'status'],
   },
   destroy: {
     requestBody: ['!key', '!target'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['name', 'phone', 'password', 'icon', 'birth', 'gender', 'email', 'openid', 'status'],
+    requestBody: ['is_leader', 'name', 'phone', 'password', 'icon', 'birth', 'gender', 'email', 'openid', 'status'],
   },
   show: {
     parameters: {
@@ -21,12 +21,13 @@ module.exports = {
       query: {
         'meta.createdAt@start': 'meta.createdAt@start',
         'meta.createdAt@end': 'meta.createdAt@end',
-        name: 'name',
+        name: '%name%',
         phone: 'phone',
         gender: 'gender',
         email: 'email',
         openid: 'openid',
         status: 'status',
+        is_leader: 'is_leader',
       },
       // options: {
       //   "meta.state": 0 // 默认条件

+ 18 - 0
app/controller/user/notice.js

@@ -0,0 +1,18 @@
+'use strict';
+const meta = require('./config/.notice.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+//
+class NoticeController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.user.notice;
+  }
+
+  async msgList() {
+    const res = await this.service.msgList(this.ctx.query);
+    this.ctx.ok(res);
+  }
+}
+module.exports = CrudController(NoticeController, meta);

+ 1 - 4
app/controller/util.js

@@ -12,20 +12,17 @@ class UtilController extends Controller {
     this.bodyObject = this.ctx.request.body;
     this.service = this.ctx.service.util;
     this.tradeService = this.ctx.service.util.trade;
-
   }
   async util() {
-    console.log('line 18 in function:');
+    await this.ctx.service.view.order.allOrder();
     this.ctx.ok();
   }
 
-
   async crk() {
     const key = await this.ctx.service.util.rk.crk();
     this.ctx.ok({ data: key });
   }
 
-
   /**
    * 查询是否可以购买
    */

+ 17 - 0
app/controller/view/order.js

@@ -0,0 +1,17 @@
+'use strict';
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+
+//
+class OrderController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.view.order;
+  }
+
+  async allOrder() {
+    const data = await this.service.allOrder(this.ctx.query);
+    this.ctx.ok(data);
+  }
+}
+module.exports = CrudController(OrderController, {});

+ 20 - 8
app/extend/context.js

@@ -1,37 +1,49 @@
 'use strict';
 const Decimal = require('decimal.js');
+const _ = require('lodash');
 // Decimal.set({ precision: 2 });
 module.exports = {
+  turnDecimal(n) {
+    if (_.isObject(n)) {
+      n = JSON.parse(JSON.stringify(n));
+      if (_.isObject(n)) n = _.get(n, '$numberDecimal');
+    }
+    return new Decimal(n);
+  },
   // 加法
   plus(n1 = 0, n2 = 0) {
-    const number1 = new Decimal(n1);
-    const number2 = new Decimal(n2);
+    const number1 = this.turnDecimal(n1);
+    const number2 = this.turnDecimal(n2);
     const result = number1.add(number2).toFixed(2, Decimal.ROUND_DOWN);
     return this.toNumber(result);
   },
   // 减法
   minus(n1 = 0, n2 = 0) {
-    const number1 = new Decimal(n1);
-    const number2 = new Decimal(n2);
+    const number1 = this.turnDecimal(n1);
+    const number2 = this.turnDecimal(n2);
     const result = number1.minus(number2).toFixed(2, Decimal.ROUND_DOWN);
     return this.toNumber(result);
   },
   // 乘法
   multiply(n1 = 0, n2 = 0) {
-    const number1 = new Decimal(n1);
-    const number2 = new Decimal(n2);
+    const number1 = this.turnDecimal(n1);
+    const number2 = this.turnDecimal(n2);
     const result = number1.mul(number2).toFixed(2, Decimal.ROUND_DOWN);
     return this.toNumber(result);
   },
   // 除法
   divide(n1 = 0, n2 = 0) {
-    const number1 = new Decimal(n1);
-    const number2 = new Decimal(n2);
+    const number1 = this.turnDecimal(n1);
+    const number2 = this.turnDecimal(n2);
     const result = number1.div(number2).toFixed(2, Decimal.ROUND_DOWN);
     return this.toNumber(result);
   },
 
   toNumber(num) {
+    if (_.isObject(num)) {
+      num = JSON.parse(JSON.stringify(num));
+      if (_.isObject(num)) num = _.get(num, '$numberDecimal');
+    }
     return new Decimal(num).toNumber();
   },
 };

+ 1 - 0
app/model/system/config.js

@@ -19,6 +19,7 @@ const config = {
   agree: { type: String, required: false, zh: '用户协议' }, // 富文本
   bottom_title: { type: String, required: false, zh: '底部加载数据结束的文字提示' }, //
   bottom_menu: { type: Object, zh: '底部菜单' },
+  reward_day: { type: Number, zh: '提现日期', default: 14 },
 };
 const schema = new Schema(config, { toJSON: { getters: true, virtuals: true } });
 schema.index({ id: 1 });

+ 3 - 0
app/model/user/cashOut.js

@@ -11,6 +11,9 @@ const cashOut = {
   deal_person: { type: String, required: false, zh: '审核处理人', ref: 'User.Admin' }, //
   exam_time: { type: String, required: false, zh: '审核时间' }, //
   exam_reason: { type: String, required: false, zh: '审核理由' }, //
+  card: { type: String, required: false, zh: '银行卡号' }, //
+  card_name: { type: String, required: false, zh: '银行卡所属' }, //
+  card_bank: { type: String, required: false, zh: '所属银行' }, //
   status: { type: String, required: true, default: '0', zh: '审核状态' }, // 字典表:withdrawal_exam_status
 };
 const schema = new Schema(cashOut, { toJSON: { getters: true, virtuals: true } });

+ 28 - 0
app/model/user/notice.js

@@ -0,0 +1,28 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose-free/lib/model/meta-plugin');
+
+// 系统通知表
+const notice = {
+  customer: { type: String, required: true, zh: '用户', ref: 'User.User' }, //
+  source: { type: String, required: false, zh: '消息来源' }, // 字典:notice_source
+  source_id: { type: String, required: false, zh: '来源id' }, //
+  time: { type: String, required: false, zh: '发送时间' }, //
+  content: { type: String, required: false, zh: '内容' }, //
+  status: { type: String, required: false, default: '0', zh: '状态' }, // 字典:notice_status
+};
+const schema = new Schema(notice, { toJSON: { getters: true, virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ 'meta.createdAt': 1 });
+schema.index({ customer: 1 });
+schema.index({ source: 1 });
+schema.index({ source_id: 1 });
+schema.index({ time: 1 });
+schema.index({ status: 1 });
+
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Notice', schema, 'notice');
+};

+ 3 - 1
app/model/user/user.js

@@ -14,7 +14,8 @@ const user = {
   gender: { type: String, required: false, zh: '性别' }, // 字典:gender
   email: { type: String, required: false, zh: '电子邮箱' }, //
   openid: { type: String, required: false, zh: '微信小程序' }, //
-  status: { type: String, required: false, default: '0', zh: '状态' }, // 字典statusr_status
+  status: { type: String, required: false, default: '0', zh: '状态' }, // 字典:statusr_status
+  is_leader: { type: String, required: false, default: '1', zh: '是否是团长' }, // 字典:is_use
 };
 const schema = new Schema(user, { toJSON: { getters: true, virtuals: true } });
 schema.index({ id: 1 });
@@ -24,6 +25,7 @@ schema.index({ phone: 1 });
 schema.index({ gender: 1 });
 schema.index({ openid: 1 });
 schema.index({ status: 1 });
+schema.index({ is_leader: 1 });
 
 schema.plugin(metaPlugin);
 

+ 23 - 10
app/service/trade/afterSale.js

@@ -15,6 +15,7 @@ class AfterSaleService extends CrudService {
     this.orderDetailModel = this.ctx.model.Trade.OrderDetail;
     this.orderModel = this.ctx.model.Trade.Order;
     this.goodsSpecModel = this.ctx.model.Shop.GoodsSpec;
+    this.configModel = this.ctx.model.System.Config;
     this.tran = new Transaction();
   }
   /**
@@ -34,11 +35,11 @@ class AfterSaleService extends CrudService {
     if (!orderDetail) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单信息');
     // 查看该商品是否已经申请售后
     let goods;
-    const hasData = await this.model.count({ order_detail, 'goods._id': goods_id, status: { $nin: [ '!1', '!2', '!3', '!4', '!5' ] } });
+    const hasData = await this.model.count({ order_detail, 'goods._id': goods_id, status: { $nin: ['!1', '!2', '!3', '!4', '!5'] } });
     if (hasData > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '该商品已有正在处理中的售后申请.请勿重复申请');
     if (type !== '4' && type !== '5') {
       const { goods: goodsList } = orderDetail;
-      goods = goodsList.find(f => {
+      goods = goodsList.find((f) => {
         return ObjectId(f._id).equals(goods_id);
       });
       if (!goods) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未在当前订单中搜索到要售后的商品');
@@ -218,7 +219,7 @@ class AfterSaleService extends CrudService {
     // 已退款记录
     const goodsRefundList = [];
     // 在下面处理已退款记录中,应该把当前的这个售后项,作为已处理售后
-    const tasq = { order_detail: data.order_detail, 'goods._id': _.get(data, 'goods._id'), status: [ '-1', '-2' ] };
+    const tasq = { order_detail: data.order_detail, 'goods._id': _.get(data, 'goods._id'), status: ['-1', '-2'] };
     const asdd = JSON.parse(JSON.stringify(data));
     if (_.get(update, 'money')) asdd.money = _.get(update, 'money');
     const tr = await this.checkIsAllRefund(data);
@@ -229,7 +230,7 @@ class AfterSaleService extends CrudService {
       const { goods, _id: order_detail } = od;
       // 组合成查售后的条件
       // 然后查这些商品有没有退款审核成功的记录, 且只有退全款的商品才能退券
-      const afterSaleQuerys = goods.map(i => ({ order_detail, 'goods._id': i._id, status: [ '-1', '-2' ] }));
+      const afterSaleQuerys = goods.map((i) => ({ order_detail, 'goods._id': i._id, status: ['-1', '-2'] }));
       for (const asq of afterSaleQuerys) {
         const asd = await this.model.findOne(asq);
         if (asd) {
@@ -245,7 +246,7 @@ class AfterSaleService extends CrudService {
       // 该优惠券影响的商品id列表
       const goodsIds = Object.keys(_.get(dd, uc_id, {}));
       // 然后在已退款/退货记录中找,这个优惠券影响的商品是否都退款了.退全额
-      const r = goodsIds.every(i => goodsRefundList.find(f => i === f['goods._id']));
+      const r = goodsIds.every((i) => goodsRefundList.find((f) => i === f['goods._id']));
       if (r) {
         // 说明这个优惠券影响的商品都退了,这个优惠券也就能退了
         tran.update('UserCoupon', uc_id, { status: '0' });
@@ -264,15 +265,15 @@ class AfterSaleService extends CrudService {
     // 获取拆分订单的数据,并找到商品列表
     const goodsList = _.get(orderDetail, 'goods');
     // 依次找这些商品是否都售后完成,都售后完成,就改订单状态
-    const asList = await this.model.find({ order_detail, status: { $nin: [ '0', '!1', '!2', '!3', '!4', '!5' ] } });
+    const asList = await this.model.find({ order_detail, status: { $nin: ['0', '!1', '!2', '!3', '!4', '!5'] } });
     let status;
     let fl = [];
     // 将当前数据添加进去
     fl.push({ goods: _.get(data, 'goods._id'), status: 'finish' });
     for (const gs of goodsList) {
-      const r = asList.find(f => ObjectId(_.get(f, 'goods._id')).equals(_.get(gs, '_id')));
+      const r = asList.find((f) => ObjectId(_.get(f, 'goods._id')).equals(_.get(gs, '_id')));
       if (r) {
-        const finishList = [ '-1', '-2', '-3', '-4', '-5' ];
+        const finishList = ['-1', '-2', '-3', '-4', '-5'];
         if (finishList.includes(r.status)) fl.push({ goods: gs._id, status: 'finish' });
       }
     }
@@ -320,7 +321,7 @@ class AfterSaleService extends CrudService {
       const { goods, _id: order_detail } = od;
       // 组合成查售后的条件
       // 然后查这些商品有没有退款审核成功的记录, 且只有退全款的商品才能退券
-      const afterSaleQuerys = goods.map(i => ({ order_detail, 'goods._id': i._id, status: [ '-1', '-2' ] }));
+      const afterSaleQuerys = goods.map((i) => ({ order_detail, 'goods._id': i._id, status: ['-1', '-2'] }));
       for (const asq of afterSaleQuerys) {
         const asd = await this.model.findOne(asq);
         if (asd) {
@@ -336,7 +337,7 @@ class AfterSaleService extends CrudService {
       // 该优惠券影响的商品id列表
       const goodsIds = Object.keys(_.get(dd, uc_id, {}));
       // 然后在已退款/退货记录中找,这个优惠券影响的商品是否都退款了.退全额
-      const r = goodsIds.every(i => goodsRefundList.find(f => i === f['goods._id']));
+      const r = goodsIds.every((i) => goodsRefundList.find((f) => i === f['goods._id']));
       if (r) {
         // 说明这个优惠券影响的商品都退了,这个优惠券也就能退了
         tran.update('UserCoupon', uc_id, { status: '0' });
@@ -487,6 +488,18 @@ class AfterSaleService extends CrudService {
     }
     return result;
   }
+
+  async canRefund({ id }) {
+    const od = await this.orderDetailModel.findById(id, { buy_time: 1 }).lean();
+    if (!od) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单数据');
+    const config = await this.configModel.findOne({}, { reward_day: 1 }).lean();
+    const rd = _.get(config, 'reward_day', 14);
+    const min = this.ctx.multiply(rd, this.ctx.multiply(24, 60));
+    const buy_time = _.get(od, 'buy_time');
+    const m = moment().diff(buy_time, 'm');
+    const r = m < min;
+    return r;
+  }
 }
 
 module.exports = AfterSaleService;

+ 37 - 7
app/service/user/cashBack.js

@@ -9,18 +9,48 @@ class CashBackService extends CrudService {
   constructor(ctx) {
     super(ctx, 'cashback');
     this.model = this.ctx.model.User.CashBack;
+    this.configModel = this.ctx.model.System.Config;
     this.orderDetailModel = this.ctx.model.Trade.OrderDetail;
   }
 
   async computedTotal({ customer }) {
     assert(customer, '缺少用户信息');
-    const res = await this.model.find({ inviter: customer });
-    const total = res.reduce((p, n) => {
-      let money = n.money;
-      if (!(n.source === '0' || n.source === '1')) money = -money;
-      return this.ctx.plus(p, money);
-    }, 0);
-    return total;
+    // status(状态) 和 source(来源) 均表示钱是入账还是出账,判断一个就行,主要还是依靠来源吧
+    const res = await this.model.find({ inviter: customer }).lean();
+    const config = await this.configModel.findOne({}, { reward_day: 1 }).lean();
+    const rd = _.get(config, 'reward_day', 14);
+    const min = this.ctx.multiply(rd, this.ctx.multiply(24, 60));
+    let total = 0;
+    for (const i of res) {
+      const { source, money } = i;
+      const sn = parseInt(source);
+      if (sn >= 0) total = this.ctx.plus(total, money);
+      else total = this.ctx.minus(total, money);
+    }
+    // 计算提现金额
+    // 上面总价包含了提现金额
+    // 下面计算的金额是可以提现的金额 提现总金额 - 提现金额
+    let canGet = 0;
+    const inBill = res.filter(f => parseInt(f.source) >= 0);
+    const outBill = res.filter(f => parseInt(f.source) < 0);
+    const getOutBill = res.filter(f => f.source === '-1');
+    const outBillMoney = getOutBill.reduce((p, n) => this.ctx.plus(p, n.money), 0);
+    for (const i of inBill) {
+      const cAt = _.get(i, 'time');
+      const m = moment().diff(cAt, 'm');
+      if (m >= min) {
+        // 已经过了提款时间,那就再找下,是否有该记录相应的退款
+        const { money, source_id } = i;
+        let cg = money;
+        const os = outBill.filter(f => f.source_id === source_id);
+        for (const o of os) {
+          cg = this.ctx.minus(cg, o.money);
+        }
+        canGet = this.ctx.plus(canGet, cg);
+      }
+    }
+    canGet = this.ctx.minus(canGet, outBillMoney);
+    return { total, canGet };
   }
   /**
    * 添加检查返现

+ 28 - 2
app/service/user/cashOut.js

@@ -1,4 +1,3 @@
-
 'use strict';
 const { CrudService } = require('naf-framework-mongoose-free/lib/service');
 const { BusinessError, ErrorCode } = require('naf-core').Error;
@@ -10,8 +9,35 @@ class CashOutService extends CrudService {
   constructor(ctx) {
     super(ctx, 'cashout');
     this.model = this.ctx.model.User.CashOut;
+    this.cashBackModel = this.ctx.model.User.CashBack;
+  }
+
+  async beforeCreate(data) {
+    const { customer } = data;
+    if (!customer) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '缺少提款人');
+    const num = await this.model.count({ customer, status: '0' });
+    if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '您有未处理提现申请,请联系管理员先处理之前的申请');
+    return data;
+  }
+
+  async beforeUpdate(filter, update) {
+    if (_.get(update, 'status') !== '0') {
+      update.deal_person = _.get(this.ctx, 'admin._id');
+    }
+    return { filter, update };
+  }
+
+  async afterUpdate(filter, body, data) {
+    const { status } = body;
+    if (status === '1') {
+      const { customer: inviter, money } = data;
+      const obj = { inviter, money, time: moment().format('YYYY-MM-DD HH:mm:ss'), status: '-1', source: '-1', source_id: data._id };
+      const num = await this.cashBackModel.count({ inviter, status: '-1', source: '-1', source_id: data._id });
+      // 说明该申请没有流水,添加流水;有流水就不加了
+      if (num <= 0) await this.cashBackModel.create(obj);
+    }
+    return data;
   }
 }
 
 module.exports = CashOutService;
-

+ 49 - 0
app/service/user/notice.js

@@ -0,0 +1,49 @@
+'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 NoticeService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'notice');
+    this.model = this.ctx.model.User.Notice;
+  }
+
+  async create(body) {
+    const { customer = [], ...others } = body;
+    if (!_.isArray(customer)) throw new BusinessError(ErrorCode.DATA_INVALID, '数据格式错误');
+    if (customer.length <= 0) throw new BusinessError(ErrorCode.BADPARAM, '缺少发送对象');
+    if (!_.get(others, 'source_id') && _.get(others, 'source') === '0') others.source_id = ObjectId();
+    const arr = customer.map(i => ({ customer: i, ...others }));
+    await this.model.insertMany(arr);
+  }
+
+  async msgList(query) {
+    const { skip, limit, ...others } = query;
+    const pipeline = [];
+    const $match = {};
+    if (_.get(others, 'source')) $match.source = _.get(others, 'source');
+    if (_.get(others, 'source_id')) $match.source_id = _.get(others, 'source_id');
+    if (_.get(others, 'customer')) $match.customer = _.get(others, 'customer');
+    if (Object.keys($match).length > 0) pipeline.push({ $match });
+    pipeline.push({ $group: { _id: '$source_id', source: { $first: '$source' }, time: { $first: '$time' } } });
+    pipeline.push({ $sort: { time: -1 } });
+
+    const qp = _.cloneDeep(pipeline);
+    if (skip && limit) {
+      qp.push({ $skip: parseInt(skip) });
+      qp.push({ $limit: parseInt(limit) });
+    }
+    const data = await this.model.aggregate(qp);
+    const tp = _.cloneDeep(pipeline);
+    tp.push({ $count: 'total' });
+    const tr = await this.model.aggregate(tp);
+    const total = _.get(_.head(tr), 'total', 0);
+    return { data, total };
+  }
+}
+
+module.exports = NoticeService;

+ 6 - 0
app/service/util/rk.js

@@ -13,6 +13,12 @@ class RkService extends CrudService {
   }
   // 检测,使用key
   async urk() {
+    // 针对项目检测.如果检测到project字段,且字段在设置中,则放行
+    const project = _.get(this.ctx, 'request.header.project');
+    if (project) {
+      const projects = this.app.config.projects;
+      if (projects.includes(project)) return;
+    }
     const key = _.get(this.ctx, 'request.header.rk');
     const keyName = this.getKeyName(key);
     const value = await this.redis.get(keyName);

+ 2 - 2
app/service/view/goods.js

@@ -25,7 +25,7 @@ class GoodsService extends CrudService {
   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 };
+    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: {
@@ -124,6 +124,7 @@ class GoodsService extends CrudService {
           act_tags: '$$CURRENT.act_tags',
           shot_brief: '$$CURRENT.shot_brief',
           view_num: '$$CURRENT.view_num',
+          sell_num: '$$CURRENT.sell_num',
         },
         shop: '$shopInfo',
         specs: '$specs',
@@ -159,7 +160,6 @@ class GoodsService extends CrudService {
     return data;
   }
 
-
   async indexGoodsList(condition, { skip = 0, limit = 20 } = {}) {
     condition = this.dealFilter(condition);
     const pipeline = [{ $match: { status: { $ne: '0' } } }]; // { $sort: { sort: 1 } },

+ 76 - 0
app/service/view/order.js

@@ -0,0 +1,76 @@
+'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 OrderService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'order');
+    this.orderModel = this.ctx.model.Trade.Order;
+  }
+
+  async allOrder(query = {}) {
+    const { skip, limit } = query;
+    const pipeline = [];
+    const step1 = [
+      { $addFields: { order_id: { $toString: '$_id' } } },
+      {
+        $lookup: {
+          from: 'orderDetail',
+          localField: 'order_id',
+          foreignField: 'order',
+          as: 'orderDetails',
+        },
+      },
+    ];
+    pipeline.push(...step1);
+    const step2 = [
+      {
+        $project: {
+          data: {
+            $cond: {
+              if: { $gt: [{ $size: '$orderDetails' }, 0 ] },
+              then: '$orderDetails',
+              else: '$$CURRENT',
+            },
+          },
+        },
+      },
+    ];
+    pipeline.push(...step2);
+    pipeline.push({ $unwind: '$data' });
+    pipeline.push({ $replaceRoot: { newRoot: '$data' } });
+    const qp = _.cloneDeep(pipeline);
+    qp.push({ $sort: { 'meta.createdAt': -1 } });
+    if (skip && limit) qp.push({ $skip: parseInt(skip) }, { $limit: parseInt(limit) });
+    const data = await this.orderModel.aggregate(qp);
+    const tr = await this.orderModel.aggregate([ ...pipeline, { $count: 'total' }]);
+    for (const i of data) {
+      const { pay, goods } = i;
+      if (pay) {
+        // 这个是order表的数据,需要用order的计算方式
+        const buy_num_total = goods.reduce(
+          (p, n) =>
+            this.ctx.plus(
+              p,
+              n.goods.reduce((np, ng) => this.ctx.plus(np, ng.buy_num), 0)
+            ),
+          0
+        );
+        i.buy_num_total = buy_num_total;
+        i.real_pay = _.get(i, 'pay.pay_money');
+      } else {
+        // 是orderDetail表的数据,需要用orderDetail的计算方式
+        const real_pay = this.ctx.service.util.orderDetail.computedRealPay(i);
+        i.real_pay = real_pay;
+        i.buy_num_total = goods.reduce((p, n) => this.ctx.plus(p, n.buy_num), 0);
+      }
+    }
+    const total = _.get(_.head(tr), 'total', 0);
+    return { data, total };
+  }
+}
+
+module.exports = OrderService;

+ 1 - 0
app/z_router/trade/afterSale.js

@@ -7,6 +7,7 @@ const rkey = 'afterSale';
 const ckey = 'trade.afterSale';
 const keyZh = '售后';
 const routes = [
+  { method: 'get', path: `${rkey}/canRefund/:id`, controller: `${ckey}.canRefund`, name: `${ckey}canRefund`, zh: `${keyZh}查询是否可以退款退货` },
   { method: 'get', path: `${rkey}/getTransportInfo/:id`, controller: `${ckey}.getTransportInfo`, name: `${ckey}getTransportInfo`, zh: `${keyZh}查询售后快递` },
   { method: 'post', path: `${rkey}/orderCancel`, controller: `${ckey}.orderCancel`, name: `${ckey}orderCancel`, zh: '取消订单' },
   { method: 'post', path: `${rkey}/cgfr`, controller: `${ckey}.cgfr`, name: `${ckey}cgfr`, zh: '查询退货商品的金额设置' },

+ 1 - 0
app/z_router/user/index.js

@@ -11,4 +11,5 @@ module.exports = app => {
   require('./storeGoods')(app); // 收藏商品
   require('./cashBack')(app); // 返现
   require('./cashOut')(app); // 提现
+  require('./notice')(app); // 系统消息
 };

+ 20 - 0
app/z_router/user/notice.js

@@ -0,0 +1,20 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'notice';
+const ckey = 'user.notice';
+const keyZh = '消息通知';
+const routes = [
+  { method: 'get', path: `${rkey}/msgList`, controller: `${ckey}.msgList`, name: `${ckey}msgList`, zh: `${keyZh}管理员列表查询` },
+  { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
+  { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, name: `${ckey}Update`, zh: `修改${keyZh}` },
+  { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
+];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 1 - 0
app/z_router/view/index.js

@@ -5,4 +5,5 @@ module.exports = app => {
   require('./goods')(app); // 商品视图
   require('./shop')(app); // 店铺视图
   require('./user')(app); // 用户视图
+  require('./order')(app); // 订单视图
 };

+ 13 - 0
app/z_router/view/order.js

@@ -0,0 +1,13 @@
+'use strict';
+// 路由配置
+const path = require('path');
+const regPath = path.resolve('app', 'public', 'routerRegister');
+const routerRegister = require(regPath);
+const rkey = 'viewOrder';
+const ckey = 'view.order';
+const keyZh = '订单相关视图';
+const routes = [{ method: 'get', path: `${rkey}`, controller: `${ckey}.allOrder`, name: `${ckey}allOrder`, zh: '全部订单' }];
+
+module.exports = app => {
+  routerRegister(app, routes, keyZh, rkey, ckey);
+};

+ 1 - 0
config/config.default.js

@@ -119,6 +119,7 @@ module.exports = appInfo => {
   config.logger = {
     level: 'NONE',
   };
+  config.projects = [ 'group-service', 'service-point_shop' ]; // 协作项目:团购服务,尊荣服务
   return {
     ...config,
     ...userConfig,