student.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const { ObjectId } = require('mongoose').Types;
  5. const { CrudService } = require('naf-framework-mongoose/lib/service');
  6. const { BusinessError, ErrorCode } = require('naf-core').Error;
  7. const moment = require('moment');
  8. class StudentService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'student');
  11. this.model = this.ctx.model.Student;
  12. this.umodel = this.ctx.model.User;
  13. this.tmodel = this.ctx.model.Trainplan;
  14. this.clamodel = this.ctx.model.Class;
  15. this.upmodel = this.ctx.model.Uploadtask;
  16. this.gmodel = this.ctx.model.Group;
  17. this.psmodel = this.ctx.model.Personalscore;
  18. this.gsmodel = this.ctx.model.Groupscore;
  19. this.uqmodel = this.ctx.model.Uploadquestion;
  20. this.scoremodel = this.ctx.model.Score;
  21. this.leavemodel = this.ctx.model.Leave;
  22. this.attendmodel = this.ctx.model.Attendance;
  23. }
  24. async create(data) {
  25. const { name, phone: mobile, gender } = data;
  26. const res = await this.model.create(data);
  27. if (res) {
  28. const obj = {
  29. name,
  30. mobile,
  31. gender,
  32. type: '4',
  33. passwd: '12345678',
  34. uid: res._id,
  35. };
  36. const user = await this.ctx.service.user.create(obj);
  37. }
  38. return res;
  39. }
  40. async delete({ id }) {
  41. // 学生表,用户表,作业表,问卷表,评分,请假,考勤
  42. await this.model.deleteOne({ _id: ObjectId(id) });
  43. await this.umodel.deleteOne({ uid: id });
  44. await this.upmodel.deleteMany({ studentid: id });
  45. await this.uqmodel.deleteMany({ studentid: id });
  46. await this.scoremodel.deleteMany({ stuid: id });
  47. await this.leavemodel.deleteMany({ studentid: id });
  48. await this.attendmodel.deleteMany({ studentid: id });
  49. }
  50. async update({ id }, data) {
  51. const student = await this.model.findByIdAndUpdate(id, data);
  52. if (student) {
  53. const { phone, name } = data;
  54. await this.umodel.findOneAndUpdate({ uid: id }, { mobile: phone, name });
  55. }
  56. return student;
  57. }
  58. // 查询
  59. async query({ skip, limit, ...info }) {
  60. const { termid, classid } = info;
  61. if (!classid) {
  62. const classes = await this.clamodel.find({ termid });
  63. for (const cla of classes) {
  64. await this.arrangeNumber(cla._id);
  65. }
  66. } else {
  67. await this.arrangeNumber(classid);
  68. }
  69. const total = await this.model.count(info);
  70. const res = await this.model
  71. .find(info)
  72. .skip(Number(skip))
  73. .limit(Number(limit));
  74. const data = [];
  75. for (const elm of res) {
  76. const plan = await this.tmodel.findOne({
  77. 'termnum._id': ObjectId(elm.termid),
  78. });
  79. const newdata = { ...JSON.parse(JSON.stringify(elm)) };
  80. if (plan) {
  81. const term = await plan.termnum.id(elm.termid);
  82. newdata.termname = term.term;
  83. if (elm.batchid) {
  84. const _batch = await term.batchnum.id(elm.batchid);
  85. newdata.batchname = _batch.batch;
  86. }
  87. }
  88. if (elm.classid) {
  89. const classs = await this.clamodel.findById(elm.classid);
  90. if (classs) {
  91. newdata.classname = classs.name;
  92. }
  93. }
  94. data.push(newdata);
  95. }
  96. const result = { total, data };
  97. return result;
  98. }
  99. // 查询
  100. async seek({ termid, type, batchid, skip, limit }) {
  101. const total = await this.model.count({
  102. termid,
  103. type,
  104. batchid,
  105. $or: [{ classid: null }, { classid: '' }],
  106. });
  107. const data = await this.model
  108. .find({
  109. termid,
  110. type,
  111. batchid,
  112. $or: [{ classid: null }, { classid: '' }],
  113. })
  114. .skip(Number(skip))
  115. .limit(Number(limit));
  116. const result = { total, data };
  117. return result;
  118. }
  119. async findbedroom(data) {
  120. const { batchid, classid } = data;
  121. const result = [];
  122. // 如果传的是批次id
  123. if (batchid) {
  124. // 查询该批次下的所有学生
  125. const students = await this.model.find({ batchid });
  126. const bedroomList = new Set();
  127. // 查询该批次的所有寝室号
  128. for (const student of students) {
  129. bedroomList.add(student.bedroom);
  130. }
  131. let studentList = [];
  132. // 查询该批次所有寝室下的学生名单
  133. for (const bedroom of bedroomList) {
  134. const newstudents = await this.model.find({ bedroom });
  135. for (const newstudent of newstudents) {
  136. studentList.push(newstudent.name);
  137. }
  138. result.push({ bedroom, studentList });
  139. studentList = [];
  140. }
  141. }
  142. // 如果传的是班级id
  143. if (classid) {
  144. // 查询该班级所有学生
  145. const students = await this.model.find({ classid });
  146. const bedroomList = new Set();
  147. // 查询该班级所有寝室号
  148. for (const student of students) {
  149. bedroomList.add(student.bedroom);
  150. }
  151. let studentList = [];
  152. // 查询该班级所有寝室的学生名单
  153. for (const bedroom of bedroomList) {
  154. const newstudents = await this.model.find({ bedroom });
  155. for (const newstudent of newstudents) {
  156. // 如果寝室中有非本班级学生(混寝),则过滤掉不予显示
  157. if (newstudent.classid === classid) {
  158. studentList.push(newstudent.name);
  159. }
  160. }
  161. result.push({ bedroom, studentList });
  162. studentList = [];
  163. }
  164. }
  165. return result;
  166. }
  167. async upjob(data) {
  168. const { stuid, job } = data;
  169. const student = await this.model.findById(stuid);
  170. student.job = job;
  171. if (job === '班长' || job === '学委') {
  172. const user = await this.umodel.findOne({ uid: stuid, type: '4' });
  173. const date = await this.ctx.service.util.updatedate();
  174. const openid = user.openid;
  175. const detail = '你已被班主任设置为' + job + ',请及时登录查看';
  176. const remark = '感谢您的使用';
  177. if (openid) {
  178. this.ctx.service.weixin.sendTemplateMsg(
  179. this.ctx.app.config.REVIEW_TEMPLATE_ID,
  180. openid,
  181. '您有一个新的通知',
  182. detail,
  183. date,
  184. remark
  185. );
  186. }
  187. }
  188. return await student.save();
  189. }
  190. // 根据学生id删除班级
  191. async deleteclass(data) {
  192. for (const el of data) {
  193. const student = await this.model.findById(el);
  194. if (student) {
  195. student.classid = '';
  196. await student.save();
  197. }
  198. }
  199. }
  200. // 根据班级id查出班级各个学生的分数
  201. async findscore({ skip, limit, ...info }) {
  202. const { classid } = info;
  203. const total = await this.model.count(info);
  204. const students = await this.model
  205. .find(info)
  206. .skip(Number(skip))
  207. .limit(Number(limit));
  208. const data = [];
  209. const groups = await this.gmodel.find({ classid });
  210. for (const student of students) {
  211. const _student = JSON.parse(JSON.stringify(student));
  212. const group = groups.find(item =>
  213. item.students.find(stuinfo => stuinfo.stuid === _student.id)
  214. );
  215. console.log(group);
  216. if (group) {
  217. _student.groupscore = group.score;
  218. }
  219. const tasks = await this.upmodel.find({ studentid: _student.id });
  220. _student.tasks = tasks;
  221. data.push(_student);
  222. }
  223. return { total, data };
  224. }
  225. async findbystuids({ data }) {
  226. const res = [];
  227. for (const stuid of data) {
  228. const stu = await this.model.findById(stuid);
  229. if (stu) res.push(stu);
  230. }
  231. return res;
  232. }
  233. // 根据学生id删除学生
  234. async deletestus(data) {
  235. for (const id of data) {
  236. await this.model.deleteOne({ _id: ObjectId(id) });
  237. await this.umodel.deleteOne({ uid: id });
  238. await this.upmodel.deleteMany({ studentid: id });
  239. await this.uqmodel.deleteMany({ studentid: id });
  240. await this.scoremodel.deleteMany({ stuid: id });
  241. await this.leavemodel.deleteMany({ studentid: id });
  242. await this.attendmodel.deleteMany({ studentid: id });
  243. }
  244. }
  245. // 批量更新寝室号
  246. async updatabedroom(data) {
  247. for (const el of data) {
  248. const student = await this.model.findById(el.id);
  249. if (student) {
  250. student.bedroom = el.bedroom;
  251. await student.save();
  252. }
  253. }
  254. }
  255. /**
  256. * 计算班级的优秀学生
  257. * 规则:班级干部(学生的job!=='普通学生'),检查是否有评优资格(is_fine_status==='0'可以评优),全部评优;
  258. * 普通学生取总成绩前10的人评优(非10位并列时,名额该占用就占用;第10名若有并列,就全都要)
  259. * @param {String} param0 {id=>班级id}
  260. */
  261. async getFineStudent({ id: classid }) {
  262. // 获取班级学生列表
  263. let studentList = await this.model.find({ classid });
  264. // 重置评优,干部全优秀
  265. studentList = studentList.map(i => {
  266. if (i.job.includes('普通')) i.is_fine = '0';
  267. else i.is_fine = '1';
  268. return i;
  269. });
  270. // 初始化后取出不需要算的人,他们就这样,没必要算
  271. const reverseList = studentList.filter(
  272. f => !(f.is_fine !== '2' && f.job.includes('普通'))
  273. );
  274. // 过滤出取消评优资格的学生和干部;干部就是优秀;被取消资格就别凑热闹了
  275. studentList = studentList.filter(
  276. f => f.is_fine !== '2' && f.job.includes('普通')
  277. );
  278. // 获取平时分
  279. const dailyScoreList = await this.psmodel.find({ classid });
  280. studentList = this.dealScoreList(dailyScoreList, studentList);
  281. // 获取作业分
  282. const taskScoreList = await this.upmodel.find({ classid });
  283. studentList = this.dealScoreList(taskScoreList, studentList);
  284. // 获取小组分,小组
  285. const groupList = await this.gmodel.find({ classid });
  286. const groupScoreList = await this.gsmodel.find({ classid });
  287. studentList = this.dealGroupScoreList(
  288. groupList,
  289. groupScoreList,
  290. studentList
  291. );
  292. studentList = studentList.sort(
  293. (a, b) => (b.score * 1 || 0) - (a.score * 1 || 0)
  294. );
  295. // 排名
  296. // eslint-disable-next-line no-unused-vars
  297. let num = 0;
  298. for (const student of studentList) {
  299. // 先判断是否超过第10位,超过就跳出
  300. if (num > 10) break;
  301. const { score, is_fine } = student;
  302. // 最开始初始化过所有人的状态,并且将干部和不能评优的人都过滤出去,所以正常学生应该都是没有评优,如果此处已经是优秀,那么就是和前人同分改的,直接跳过就好
  303. if (is_fine === '1') continue;
  304. // 没有分凑什么热闹
  305. if (!score) continue;
  306. let plus = 1; // 这轮有多少人,到这了,这个人肯定是要改了,所以默认1
  307. // 评优
  308. student.is_fine = '1';
  309. const rlist = studentList.filter(f => f.score === score);
  310. // 处理同分的人也都变成is_fine
  311. for (const stud of rlist) {
  312. stud.is_fine = '1';
  313. const sindex = studentList.findIndex(f =>
  314. ObjectId(stud._id).equals(f._id)
  315. );
  316. if (sindex >= 0) {
  317. studentList[sindex] = stud;
  318. plus++;
  319. }
  320. }
  321. // num+plus,算下num
  322. num = num + plus;
  323. }
  324. // 算完的和不用算的合并,提交
  325. const lastList = [ ...studentList, ...reverseList ];
  326. for (const student of lastList) {
  327. const res = await student.save();
  328. // const { meta, ...data } = student;
  329. // const r = await this.model.findByIdAndUpdate(
  330. // { _id: ObjectId(student._id) },
  331. // data
  332. // );
  333. console.log(res);
  334. }
  335. }
  336. /**
  337. * 将分数放到学生身上
  338. * @param {Array} scoreList 班级的分数列表
  339. * @param {Array} studentList 学生列表
  340. */
  341. dealScoreList(scoreList, studentList) {
  342. scoreList = _.groupBy(scoreList, 'studentid');
  343. studentList = studentList.map(i => {
  344. const slist = scoreList[i._id];
  345. if (slist) {
  346. i.score =
  347. (i.score * 1 || 0) +
  348. slist.reduce((p, n) => p + (n.score * 1 || 0), 0);
  349. }
  350. return i;
  351. });
  352. return studentList;
  353. }
  354. /**
  355. * 将 学生所在 组的 团队平均分 + 到学生身上
  356. * @param {Array} groupList 班级的小组的列表
  357. * @param {Array} scoreList 所有小组的分数列表
  358. * @param {Array} studentList 学生列表
  359. */
  360. dealGroupScoreList(groupList, scoreList, studentList) {
  361. // console.log(groupList);
  362. scoreList = _.groupBy(scoreList, 'groupid');
  363. // 算出每组的平均分,之后加给学生
  364. groupList = groupList.map(i => {
  365. const { students } = i;
  366. if (students.length > 0) {
  367. const slist = scoreList[i._id];
  368. if (slist) {
  369. i.score = slist.reduce((p, n) => p + (n.score * 1 || 0), 0);
  370. i.score = _.floor(_.divide(i.score, students.length), 2);
  371. }
  372. }
  373. return i;
  374. });
  375. // 每个学生加自己的组的平均分
  376. studentList = studentList.map(i => {
  377. const r = groupList.find(f =>
  378. f.students.find(sf => ObjectId(sf.stuid).equals(i._id))
  379. );
  380. if (r) i.score = (i.score * 1 || 0) + (r.score * 1 || 0);
  381. return i;
  382. });
  383. return studentList;
  384. }
  385. // 将学生排号
  386. async arrangeNumber(classid) {
  387. const studList = await this.model.find({ classid });
  388. let number = 1;
  389. // 查每个学生的编号,如果没有,就给赋上值;有,就给number赋上值,然后继续下一位
  390. for (const stu of studList) {
  391. if (!stu.number) {
  392. if (number * 1 < 10) stu.number = `0${number}`;
  393. else stu.number = number;
  394. await stu.save();
  395. } else {
  396. number = stu.number * 1;
  397. }
  398. number = number * 1 + 1;
  399. }
  400. // console.log(`last number => ${number}`);
  401. }
  402. // 导出学生 目前:拓展训练保险用
  403. async exportStudent(data) {
  404. let { data: studentList } = await this.query(data);
  405. studentList = JSON.parse(JSON.stringify(studentList));
  406. let ids = studentList.map(i => i.classid);
  407. ids = _.uniq(ids);
  408. const classList = [];
  409. for (const id of ids) {
  410. const cla = await this.ctx.service.class.fetch({ id });
  411. if (!cla) continue;
  412. cla.date = moment(cla.startdate).add(1, 'd').format('YYYY-MM-DD');
  413. classList.push(cla);
  414. }
  415. studentList = studentList.map(i => {
  416. const c = classList.find(f => ObjectId(i.classid).equals(f._id));
  417. if (c) i.date = c.date;
  418. return i;
  419. });
  420. const meta = this.metaBx();
  421. let fn = '学生名单';
  422. const head = _.head(classList);
  423. if (head) fn = `${head.term}期-${head.batch}批${fn}`;
  424. return await this.ctx.service.util.toExcel(studentList, meta, fn);
  425. }
  426. metaBx() {
  427. const header = [
  428. {
  429. header: '姓名',
  430. key: 'name',
  431. width: 20,
  432. },
  433. {
  434. header: '拓训日期',
  435. key: 'date',
  436. width: 20,
  437. },
  438. {
  439. header: '性别',
  440. key: 'gender',
  441. width: 10,
  442. },
  443. {
  444. header: '民族',
  445. key: 'nation',
  446. width: 20,
  447. },
  448. {
  449. header: '身份证号',
  450. key: 'id_number',
  451. width: 20,
  452. },
  453. {
  454. header: '期',
  455. key: 'termname',
  456. width: 20,
  457. },
  458. {
  459. header: '批次',
  460. key: 'batchname',
  461. width: 20,
  462. },
  463. {
  464. header: '班级',
  465. key: 'classname',
  466. width: 20,
  467. },
  468. {
  469. header: '学校',
  470. key: 'school_name',
  471. width: 20,
  472. },
  473. {
  474. header: '院系',
  475. key: 'faculty',
  476. width: 20,
  477. },
  478. {
  479. header: '专业',
  480. key: 'major',
  481. width: 20,
  482. },
  483. {
  484. header: '手机号',
  485. key: 'phone',
  486. width: 20,
  487. },
  488. ];
  489. return header;
  490. }
  491. }
  492. module.exports = StudentService;