lrf 2 years ago
parent
commit
7f880e0a04
3 changed files with 95 additions and 70 deletions
  1. 10 1
      app/controller/config/.statistics.js.js
  2. 84 69
      app/service/statistics.js
  3. 1 0
      app/z_router/statistics.js

+ 10 - 1
app/controller/config/.statistics.js.js

@@ -3,7 +3,6 @@ module.exports = {
     parameters: {
       query: {
         school_id: 'school_id',
-        time: 'time',
       },
     },
   },
@@ -62,4 +61,14 @@ module.exports = {
       },
     },
   },
+  schoolInByLesson: {
+    parameters: {
+      query: {
+        school_id: 'school_id',
+        time: 'time',
+        skip: 'skip',
+        limit: 'limit',
+      },
+    },
+  },
 };

+ 84 - 69
app/service/statistics.js

@@ -46,7 +46,6 @@ class StatisticsService extends CrudService {
     return arr;
   }
 
-
   // 教练统计, 授课情况
   async coachLesson({ coach_id, school_id }) {
     assert(school_id, '缺少学校信息');
@@ -74,7 +73,6 @@ class StatisticsService extends CrudService {
     return arr;
   }
 
-
   // 学员统计,付费情况
   async studentPay({ school_id, student_id }) {
     assert(school_id, '缺少学校信息');
@@ -106,7 +104,6 @@ class StatisticsService extends CrudService {
     return arr;
   }
 
-
   // 学员统计,学员上课时长及签到
   async studentLearning({ school_id, student_id }) {
     assert(school_id, '缺少学校信息');
@@ -144,7 +141,6 @@ class StatisticsService extends CrudService {
     return { minutes, signs };
   }
 
-
   // 学校统计,教练收入
   async schoolCoachIn({ school_id, coach_id }) {
     assert(school_id, '缺少学校信息');
@@ -193,10 +189,8 @@ class StatisticsService extends CrudService {
       arr.push(obj);
     }
     return arr;
-
   }
 
-
   // 学校统计 每月上课次数
   async schoolSignCoach({ school_id, coach_id }) {
     assert(school_id, '缺少学校信息');
@@ -213,7 +207,7 @@ class StatisticsService extends CrudService {
       return [ start, end ];
     });
     let lcList = await this.lessonCoachModel.find(query, { coach_id: 1, meta: 1 });
-    if (lcList.length > 0) lcList = JSON.parse(JSON.stringify((lcList)));
+    if (lcList.length > 0) lcList = JSON.parse(JSON.stringify(lcList));
     const arr = [];
     for (const months of monthList) {
       const s = _.head(months);
@@ -228,7 +222,6 @@ class StatisticsService extends CrudService {
     return arr;
   }
 
-
   // 学校统计:学员按岁数区间
   async schoolStudentAge({ school_id }) {
     assert(school_id, '缺少学校信息');
@@ -243,7 +236,7 @@ class StatisticsService extends CrudService {
     ];
     const data = [];
     let list = await this.rssModel.find({ school_id }).populate('student_id', 'birth');
-    if (list.length > 0)list = JSON.parse(JSON.stringify(list));
+    if (list.length > 0) list = JSON.parse(JSON.stringify(list));
     for (const a of ageList) {
       const start = _.head(a);
       const end = _.last(a);
@@ -265,88 +258,110 @@ class StatisticsService extends CrudService {
     return data;
   }
 
-
   /**
    * 羽校总收入
-   * 统计账单的 收入(类型为-1/-2) - 退款(至余额,2) 且 is_pay 为 1
+   * 统计账单的 收入(类型为-1/-2) 且 is_pay 不为 0
    * m:当前这个月; 3m: 往前推3个月; 6m:往前推6个月; 1y:当前年
    * @param {Object} query 查询条件
    * @property {String} school_id 学校id
-   * @property {String} time 时间 m:月;3m:3月;6m:6个月; 1y:1年
+   * @property {String} skip 分页
+   * @property {String} limit 分页
    */
-  async schoolTotalIn({ school_id, time }) {
+  async schoolTotalIn({ school_id }) {
     assert(school_id, '缺少羽校信息');
-    assert(time, '缺少时间范围');
-    let query = { is_pay: '1', $or: [{ type: '-1' }, { type: '-2' }, { type: '2' }], school_id };
+    const query = { is_pay: { $ne: '0' }, school_id, type: [ '-1', '-2' ] };
+    const projection = { pay_for: 1, from_id: 1, type: 1, money: 1, time: 1, payer_id: 1 };
+    const qm = this.resetQuery('m');
+    const bm = await this.billModel.find({ ...qm, ...query }, projection);
+    const mm = bm.reduce((p, n) => this.ctx.plus(p, n.money), 0);
+    const q3m = this.resetQuery('3m');
+    const b3m = await this.billModel.find({ ...q3m, ...query }, projection);
+    const m3m = b3m.reduce((p, n) => this.ctx.plus(p, n.money), 0);
+    const q6m = this.resetQuery('6m');
+    const b6m = await this.billModel.find({ ...q6m, ...query }, projection);
+    const m6m = b6m.reduce((p, n) => this.ctx.plus(p, n.money), 0);
+    const qy = this.resetQuery('1y');
+    const by = await this.billModel.find({ ...qy, ...query }, projection);
+    const my = by.reduce((p, n) => this.ctx.plus(p, n.money), 0);
+    return { mm, m3m, m6m, my };
+  }
+
+  async schoolInByLesson({ school_id, time = 'm', skip = 0, limit }) {
+    const tq = this.resetQuery(time, 'time_start');
+    const query = { ...tq, school_id };
+    const pipeline = [{ $match: query }];
+    const baseCol = { time_start: 1, title: 1 };
+    // #region 整理课程数据
+    pipeline.push({
+      $project: {
+        ...baseCol,
+        money: { $toDouble: '$money' },
+      },
+    });
+    baseCol.money = 1;
+    // #endregion
+    // #region 正式学员
+    pipeline.push({ $addFields: { lesson_id: { $toString: '$_id' } } });
+    baseCol.lesson_id = 1;
+    pipeline.push({
+      $lookup: {
+        from: 'lessonStudent',
+        localField: 'lesson_id',
+        foreignField: 'lesson_id',
+        pipeline: [
+          { $group: { _id: '$lesson_id', num: { $sum: 1 } } },
+        ],
+        as: 'zStudent',
+      },
+    });
+    pipeline.push({ $set: { zStudent: { $sum: '$zStudent.num' } } });
+    pipeline.push({ $project: { ...baseCol, zStudent: { $toDouble: '$zStudent' } } });
+    baseCol.zStudent = 1;
+    // #endregion
+    // #region 临时学员
+    pipeline.push({
+      $lookup: {
+        from: 'tempLessonApply',
+        localField: 'lesson_id',
+        foreignField: 'lesson_id',
+        pipeline: [{ $group: { _id: '$lesson_id', num: { $sum: 1 } } }],
+        as: 'lStudent',
+      },
+    });
+    pipeline.push({ $set: { lStudent: { $sum: '$lStudent.num' } } });
+    pipeline.push({ $project: { ...baseCol, lStudent: { $toDouble: '$lStudent' } } });
+    baseCol.lStudent = 1;
+    pipeline.push({ $set: { total: { $multiply: [ '$money', { $add: [ '$zStudent', '$lStudent' ] }] } } });
+    // #endregion
+    if (parseInt(skip)) pipeline.push({ $skip: parseInt(skip) });
+    if (parseInt(limit))pipeline.push({ $limit: parseInt(limit) });
+    const res = await this.lessonModel.aggregate(pipeline);
+    return res;
+  }
+
+  resetQuery(time, key = 'time') {
+    let query = {};
     const { year, month, lastDate } = this.getPartsOfNow();
-    const timeQuery = (start, end) => ({ $and: [{ time: { $gte: start } }, { time: { $lte: end } }] });
-    let xList;
-    const yList = [];
+    const timeQuery = (start, end) => ({ $and: [{ [key]: { $gte: start } }, { [key]: { $lte: end } }] });
     if (time === 'm') {
-      // 这个月,按天查
       const start = `${year}-${month}-01`;
       const end = `${year}-${month}-${lastDate}`;
       query = { ...query, ...timeQuery(start, end) };
-      xList = this.getEachDay(start, end);
     } else if ([ '3m', '6m' ].includes(time)) {
       const ms = _.head(time.split('')); // 月份数量 3/6/...
-      xList = this.getMonthList(ms);
-      const start = _.last(xList);
-      const { year, month, lastDate } = this.getPartsOfNow(_.head(xList));
-      const end = `${year}-${month}-${lastDate}`;
+      const start = moment().subtract(ms, 'months').format('YYYY-MM-01');
+      const { lastDate } = this.getPartsOfNow();
+      const end = lastDate;
       query = { ...query, ...timeQuery(start, end) };
     } else if (time === '1y') {
       const { year } = this.getPartsOfNow();
       const start = `${year}-01-01`;
       const end = `${year}-12-31`;
-      xList = this.getMonthList(12, end);
       query = { ...query, ...timeQuery(start, end) };
     }
-
-    let billList = await this.billModel.find(query, { pay_for: 1, from_id: 1, type: 1, money: 1, time: 1, payer_id: 1 });
-    if (billList.length > 0) billList = JSON.parse(JSON.stringify(billList));
-    for (const i of billList) {
-      const { time } = i;
-      const date = this.getDate(time);
-      i.date = date;
-    }
-    if (time === 'm') {
-      for (const x of xList) {
-        const date = x;
-        const list = billList.filter(f => f.date === date);
-        const inList = list.filter(f => f.type !== '2');
-        const outList = list.filter(f => f.type === '2');
-        const inTotal = inList.reduce((p, n) => p + (n.money || 0), 0);
-        const outTotal = outList.reduce((p, n) => p + (n.money || 0), 0);
-        const total = inTotal - outTotal;
-        yList.push(total);
-      }
-      xList = xList.map(i => {
-        const { date } = this.getPartsOfNow(i);
-        return `${date}日`;
-      });
-    } else if ([ '3m', '6m', '1y' ].includes(time)) {
-      for (const x of xList) {
-        const start = x;
-        const { year, month, lastDate } = this.getPartsOfNow(x);
-        const end = `${year}-${month}-${lastDate}`;
-        const list = billList.filter(f => moment(f.date).isBetween(start, end, null, '[]'));
-        const inList = list.filter(f => f.type !== '2');
-        const outList = list.filter(f => f.type === '2');
-        const inTotal = inList.reduce((p, n) => p + (n.money || 0), 0);
-        const outTotal = outList.reduce((p, n) => p + (n.money || 0), 0);
-        const total = inTotal - outTotal;
-        yList.unshift(total);
-      }
-      xList = xList.map(i => {
-        const { month } = this.getPartsOfNow(i);
-        return `${parseInt(month)}月`;
-      });
-      xList = _.reverse(xList);
-    }
-    console.log('line 189 in function:');
-    return { x: xList, y: yList };
+    return query;
   }
+
   // 获取现在时间的各个部分 年月日时分秒 和 当月最后一天是几号
   getPartsOfNow(time = new Date()) {
     const year = moment(time).year();

+ 1 - 0
app/z_router/statistics.js

@@ -7,6 +7,7 @@ const rkey = 'statistics';
 const ckey = 'statistics';
 const keyZh = '统计';
 const routes = [
+  { method: 'get', path: `${rkey}/schoolInByLesson`, controller: `${ckey}.schoolInByLesson`, name: `${ckey}schoolInByLesson`, zh: `${keyZh}-收入明细` },
   { method: 'get', path: `${rkey}/coachStudentLesson`, controller: `${ckey}.coachStudentLesson`, name: `${ckey}coachStudentLesson`, zh: `${keyZh}-教练,学员情况` },
   { method: 'get', path: `${rkey}/coachLesson`, controller: `${ckey}.coachLesson`, name: `${ckey}coachLesson`, zh: `${keyZh}-教练,授课情况` },
   { method: 'get', path: `${rkey}/studentPay`, controller: `${ckey}.studentPay`, name: `${ckey}studentPay`, zh: `${keyZh}-学员,付费情况` },