count.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const { ObjectId } = require('mongoose').Types;
  5. const moment = require('moment');
  6. const { CrudService } = require('naf-framework-mongoose/lib/service');
  7. const { BusinessError, ErrorCode } = require('naf-core').Error;
  8. class CountService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'count');
  11. this.smodel = this.ctx.model.Student;
  12. this.tmodel = this.ctx.model.Trainplan;
  13. this.lmodel = this.ctx.model.Leave;
  14. this.schmodel = this.ctx.model.School;
  15. this.setmodel = this.ctx.model.Setting;
  16. }
  17. // 查询
  18. async countstudent() {
  19. // levelexit:退出人数 √
  20. // levelqj:请假人数 √
  21. // notrainstu:上传名单,但是未培训人数 student表中的数据不稳,后加的可能没有了.被人绑定更换也可能没有了.所以 这个数字用 student.total - user(type=4&&openid) 的结果 √
  22. // schstu:上传名单人数 该年度计划下,的student.total √
  23. // trainstu:已参加培训人数 user(type==4&&openid).total √
  24. // planstu:年度计划人数(重算) 根据年度计划学校的分配计算 √
  25. // schs:[{schname 学校名称,schnum 学校人数}] 学校上传人数 试用聚合做 √
  26. // 取得当前年份计划信息
  27. const setting = await this.setmodel.findOne();
  28. if (!setting) {
  29. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认设置');
  30. }
  31. const { planid } = setting;
  32. if (!planid) {
  33. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认年度计划');
  34. }
  35. const trainPlan = await this.tmodel.findById(planid);
  36. if (!trainPlan) {
  37. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认年度计划');
  38. }
  39. let { termnum } = trainPlan;
  40. if (!_.isArray(termnum)) {
  41. throw new BusinessError(ErrorCode.DATA_INVALID, '默认年度计划的期数据格式不正确');
  42. }
  43. // 转换下,因为之后用的期id不是ObjectId
  44. termnum = JSON.parse(JSON.stringify(termnum));
  45. const termids = termnum.map(i => i._id);
  46. const data = {};
  47. // 请假,退出
  48. const levelqj = await this.ctx.model.Leave.count({
  49. termid: { $in: termids },
  50. type: '0',
  51. status: '1',
  52. });
  53. const levelexit = await this.ctx.model.Leave.count({
  54. termid: { $in: termids },
  55. type: '1',
  56. status: '1',
  57. });
  58. data.levelqj = levelqj || 0;
  59. data.levelexit = levelexit || 0;
  60. // 参加培训的学生
  61. const trainstuRes = await this.ctx.model.Student.aggregate([{ $match: { planid } }, { $match: { openid: { $exists: true } } }, { $count: 'total' }]);
  62. const h = _.head(trainstuRes);
  63. if (h) {
  64. let { total: trainstu } = h;
  65. // 减去退出的学生
  66. trainstu = trainstu - data.levelexit || 0;
  67. data.trainstu = trainstu;
  68. } else {
  69. data.trainstu = 0;
  70. }
  71. // 已上传的学生总数
  72. const schstu = await this.ctx.model.Student.count({ planid });
  73. data.schstu = schstu;
  74. // 未参加培训的学生
  75. const notrainstu = schstu - data.trainstu;
  76. data.notrainstu = notrainstu;
  77. // 年度计划实际人数 这个还没用聚合,不是很优雅
  78. const mid = await this.ctx.model.Schtime.find({ planid });
  79. const planstu = mid.reduce((p, n) => p + n.arrange.reduce((np, nn) => np + (nn.number * 1 || 0), 0), 0);
  80. data.planstu = planstu;
  81. // 各个学校上传的人数
  82. const schs = await this.ctx.model.Student.aggregate([
  83. { $match: { planid } },
  84. { $group: { _id: '$schid', sum: { $sum: 1 } } },
  85. {
  86. $lookup: {
  87. from: 'school',
  88. localField: '_id',
  89. foreignField: 'code',
  90. as: 'school',
  91. },
  92. },
  93. { $unwind: '$school' },
  94. { $project: { _id: 0, schname: '$school.name', schnum: '$sum' } },
  95. ]);
  96. data.schs = schs;
  97. return data;
  98. }
  99. // 按学校id统计查询
  100. async countschstu({ id }) {
  101. // levelexit:退出人数 √
  102. // levelqj:请假人数 √
  103. // notrainstu:上传名单,但是未培训人数 student表中的数据不稳,后加的可能没有了.被人绑定更换也可能没有了.所以 这个数字用 student.total - user(type=4&&openid) 的结果 √
  104. // schstu:上传名单人数 该年度计划下,的student.total √
  105. // trainstu:已参加培训人数 user(type==4&&openid).total √
  106. // planstu:年度计划人数(重算) 根据年度计划学校的分配计算 √
  107. // leaveNum:请假人数未处理 √
  108. // uploadNum:计划上报未处理 √
  109. // 取得当前年份计划信息
  110. const setting = await this.setmodel.findOne();
  111. if (!setting) {
  112. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认设置');
  113. }
  114. const { planid, termid } = setting;
  115. if (!planid) {
  116. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认年度计划');
  117. }
  118. const trainPlan = await this.tmodel.findById(planid);
  119. if (!trainPlan) {
  120. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到默认年度计划');
  121. }
  122. let { termnum } = trainPlan;
  123. if (!_.isArray(termnum)) {
  124. throw new BusinessError(ErrorCode.DATA_INVALID, '默认年度计划的期数据格式不正确');
  125. }
  126. // 转换下,因为之后用的期id不是ObjectId
  127. termnum = JSON.parse(JSON.stringify(termnum));
  128. const termids = termnum.map(i => i._id);
  129. const data = {};
  130. // 请假,退出
  131. const levelqj = await this.ctx.model.Leave.count({
  132. termid: { $in: termids },
  133. type: '0',
  134. status: '1',
  135. schid: id,
  136. });
  137. const levelexit = await this.ctx.model.Leave.count({
  138. termid: { $in: termids },
  139. type: '1',
  140. status: '1',
  141. schid: id,
  142. });
  143. data.levelqj = levelqj || 0;
  144. data.levelexit = levelexit || 0;
  145. // 参加培训的学生
  146. const trainstuRes = await this.ctx.model.Student.aggregate([
  147. { $addFields: { u_id: { $toString: '$_id' } } },
  148. { $match: { planid, schid: id } },
  149. {
  150. $lookup: {
  151. from: 'user',
  152. localField: 'u_id',
  153. foreignField: 'uid',
  154. as: 'user',
  155. },
  156. },
  157. { $match: { 'user.type': '4', 'user.openid': { $exists: true } } },
  158. { $count: 'total' },
  159. ]);
  160. const h = _.head(trainstuRes);
  161. if (h) {
  162. let { total: trainstu } = h;
  163. // 减去退出的学生
  164. trainstu = trainstu - data.levelexit || 0;
  165. data.trainstu = trainstu;
  166. } else {
  167. data.trainstu = 0;
  168. }
  169. // 已上传的学生总数
  170. const schstu = await this.ctx.model.Student.count({ planid, schid: id });
  171. data.schstu = schstu;
  172. // 未参加培训的学生
  173. const notrainstu = schstu - data.trainstu;
  174. data.notrainstu = notrainstu;
  175. // 年度计划实际人数 这个还没用聚合,不是很优雅
  176. const mid = await this.ctx.model.Schtime.find({ planid, schid: id });
  177. const planstu = mid.reduce((p, n) => p + n.arrange.reduce((np, nn) => np + (nn.number * 1 || 0), 0), 0);
  178. data.planstu = planstu;
  179. // 请假或退出人数未处理
  180. const leaveNum = await this.ctx.model.Leave.count({
  181. termid,
  182. schid: id,
  183. status: '0',
  184. });
  185. data.leaveNum = leaveNum || 0;
  186. // 计划上报学校上传学生名单
  187. const schtime = await this.ctx.model.Schtime.findOne({ planid, schid: id }).lean();
  188. const uploadNum = 0;
  189. if (schtime && schtime.arrange) {
  190. for (const val of schtime.arrange) {
  191. const { termid, batchid } = val;
  192. const studentTotal = await this.ctx.model.Student.count({ termid, batchid, schid: id });
  193. if (studentTotal === 0) uploadNum + 1;
  194. }
  195. }
  196. data.uploadNum = uploadNum;
  197. return data;
  198. }
  199. }
  200. module.exports = CountService;