student.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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. const { baseUrl } = _.get(this.ctx.app.config, 'mission');
  24. if (baseUrl) this.missionBase = baseUrl;
  25. }
  26. async create(data) {
  27. const { name, phone: mobile, gender } = data;
  28. const res = await this.model.create(data);
  29. if (res) {
  30. const obj = {
  31. name,
  32. mobile,
  33. gender,
  34. type: '4',
  35. passwd: '12345678',
  36. uid: res._id,
  37. };
  38. const user = await this.ctx.service.user.create(obj);
  39. }
  40. return res;
  41. }
  42. async delete({ id }) {
  43. // 删除小组中的这个人,作业表,问卷表,评分,考勤,用户表,学生表
  44. await this.gmodel.update(
  45. { 'students.stuid': id },
  46. { $pull: { students: { stuid: id } } }
  47. );
  48. await this.upmodel.deleteMany({ studentid: id });
  49. await this.uqmodel.deleteMany({ studentid: id });
  50. await this.scoremodel.deleteMany({ stuid: id });
  51. await this.attendmodel.deleteMany({ studentid: id });
  52. await this.umodel.deleteOne({ uid: id });
  53. await this.model.deleteOne({ _id: ObjectId(id) });
  54. }
  55. async update({ id }, data) {
  56. const student = await this.model.findByIdAndUpdate(id, data);
  57. if (student) {
  58. const { phone, name } = data;
  59. if (phone && name) {
  60. await this.umodel.findOneAndUpdate(
  61. { uid: id },
  62. { mobile: phone, name }
  63. );
  64. }
  65. }
  66. return student;
  67. }
  68. // 查询
  69. async query({ name, ...info } = {}, { skip = 0, limit = 0 } = {}) {
  70. const query = { ...info };
  71. if (name) {
  72. query.name = { $regex: name };
  73. }
  74. let data = await this.model
  75. .find(query)
  76. .populate([
  77. {
  78. path: 'classid',
  79. model: 'Class',
  80. select: 'name',
  81. },
  82. ])
  83. .skip(parseInt(skip))
  84. .limit(parseInt(limit));
  85. const planids = _.uniq(data.map(i => ObjectId(i.planid)));
  86. const plan = await this.ctx.model.Trainplan.find({ _id: planids });
  87. data = JSON.parse(JSON.stringify(data));
  88. data = data.map(i => {
  89. if (i.classid && _.isObject(i.classid)) {
  90. i.classname = _.get(i.classid, 'name');
  91. i.classid = _.get(i.classid, '_id');
  92. }
  93. const p = plan.find(f => ObjectId(f._id).equals(i.planid));
  94. if (p) {
  95. const { termnum } = p;
  96. const tr = termnum.id(i.termid);
  97. if (tr) {
  98. i.termname = _.get(tr, 'term');
  99. const br = tr.batchnum.id(i.batchid);
  100. if (br) {
  101. i.batchname = _.get(br, 'batch');
  102. }
  103. }
  104. }
  105. return i;
  106. });
  107. return data;
  108. }
  109. async count({ name, ...info } = {}) {
  110. const query = { ...info };
  111. if (name) {
  112. query.name = { $regex: name };
  113. }
  114. return await this.model.count(query);
  115. }
  116. // 查询
  117. async seek({ termid, type, batchid, skip, limit }) {
  118. const total = await this.model.count({
  119. termid,
  120. type,
  121. batchid,
  122. $or: [{ classid: null }, { classid: '' }],
  123. });
  124. const data = await this.model
  125. .find({
  126. termid,
  127. type,
  128. batchid,
  129. $or: [{ classid: null }, { classid: '' }],
  130. })
  131. .skip(Number(skip))
  132. .limit(Number(limit));
  133. const result = { total, data };
  134. return result;
  135. }
  136. async findbedroom(data) {
  137. const { batchid, classid } = data;
  138. const result = [];
  139. // 如果传的是批次id
  140. if (batchid) {
  141. // 查询该批次下的所有学生
  142. const students = await this.model.find({ batchid });
  143. const bedroomList = new Set();
  144. // 查询该批次的所有寝室号
  145. for (const student of students) {
  146. bedroomList.add(student.bedroom);
  147. }
  148. let studentList = [];
  149. // 查询该批次所有寝室下的学生名单
  150. for (const bedroom of bedroomList) {
  151. const newstudents = await this.model.find({ bedroom });
  152. for (const newstudent of newstudents) {
  153. studentList.push(newstudent.name);
  154. }
  155. result.push({ bedroom, studentList });
  156. studentList = [];
  157. }
  158. }
  159. // 如果传的是班级id
  160. if (classid) {
  161. // 查询该班级所有学生
  162. const students = await this.model.find({ classid });
  163. const bedroomList = new Set();
  164. // 查询该班级所有寝室号
  165. for (const student of students) {
  166. bedroomList.add(student.bedroom);
  167. }
  168. let studentList = [];
  169. // 查询该班级所有寝室的学生名单
  170. for (const bedroom of bedroomList) {
  171. const newstudents = await this.model.find({ bedroom });
  172. for (const newstudent of newstudents) {
  173. // 如果寝室中有非本班级学生(混寝),则过滤掉不予显示
  174. if (newstudent.classid === classid) {
  175. studentList.push(newstudent.name);
  176. }
  177. }
  178. result.push({ bedroom, studentList });
  179. studentList = [];
  180. }
  181. }
  182. return result;
  183. }
  184. async upjob(data) {
  185. const { stuid, job } = data;
  186. const student = await this.model.findById(stuid);
  187. student.job = job;
  188. if (job === '班长' || job === '学委') {
  189. const user = await this.umodel.findOne({ uid: stuid, type: '4' });
  190. const date = await this.ctx.service.util.updatedate();
  191. const openid = user.openid;
  192. const detail = '你已被班主任设置为' + job + ',请及时登录查看';
  193. const remark = '感谢您的使用';
  194. if (openid) {
  195. this.ctx.service.weixin.sendTemplateMsg(
  196. this.ctx.app.config.REVIEW_TEMPLATE_ID,
  197. openid,
  198. '您有一个新的通知',
  199. detail,
  200. date,
  201. remark
  202. );
  203. }
  204. }
  205. return await student.save();
  206. }
  207. // 根据学生id删除班级
  208. async deleteclass(data) {
  209. for (const el of data) {
  210. const student = await this.model.findById(el);
  211. if (student) {
  212. student.classid = '';
  213. await student.save();
  214. }
  215. }
  216. }
  217. // 根据班级id查出班级各个学生的分数
  218. async findscore({ skip, limit, ...info }) {
  219. const { classid } = info;
  220. const total = await this.model.count(info);
  221. const students = await this.model
  222. .find(info)
  223. .skip(Number(skip))
  224. .limit(Number(limit));
  225. const data = [];
  226. const groups = await this.gmodel.find({ classid });
  227. for (const student of students) {
  228. const _student = JSON.parse(JSON.stringify(student));
  229. const group = groups.find(item =>
  230. item.students.find(stuinfo => stuinfo.stuid === _student.id)
  231. );
  232. if (group) {
  233. _student.groupscore = group.score;
  234. }
  235. const tasks = await this.upmodel.find({ studentid: _student.id });
  236. _student.tasks = tasks;
  237. data.push(_student);
  238. }
  239. return { total, data };
  240. }
  241. async findbystuids({ data }) {
  242. const res = [];
  243. for (const stuid of data) {
  244. const stu = await this.model.findById(stuid);
  245. if (stu) res.push(stu);
  246. }
  247. return res;
  248. }
  249. // 根据学生id删除学生
  250. async deletestus(data) {
  251. throw new BusinessError(
  252. ErrorCode.BUSINESS,
  253. '此功能暂不开放,待确定好会出现的以外情况后,再次开放'
  254. );
  255. // for (const id of data) {
  256. // await this.model.deleteOne({ _id: ObjectId(id) });
  257. // await this.umodel.deleteOne({ uid: id });
  258. // await this.upmodel.deleteMany({ studentid: id });
  259. // await this.uqmodel.deleteMany({ studentid: id });
  260. // await this.scoremodel.deleteMany({ stuid: id });
  261. // await this.attendmodel.deleteMany({ studentid: id });
  262. // }
  263. }
  264. // 批量更新寝室号
  265. async updatabedroom(data) {
  266. for (const el of data) {
  267. const student = await this.model.findById(el.id);
  268. if (student) {
  269. student.bedroom = el.bedroom;
  270. await student.save();
  271. }
  272. }
  273. }
  274. /**
  275. * 计算班级的优秀学生
  276. * 规则:班级干部(学生的job!=='普通学生'),检查是否有评优资格(is_fine_status==='0'可以评优),全部评优;
  277. * 普通学生取总成绩前10的人评优(非10位并列时,名额该占用就占用;第10名若有并列,就全都要)
  278. * @param {String} param0 {id=>班级id}
  279. */
  280. async getFineStudent({ id: classid }) {
  281. // 获取班级学生列表
  282. let studentList = await this.model.find({ classid });
  283. // 重置评优,干部全优秀
  284. studentList = studentList.map(i => {
  285. if (i.job.includes('普通')) i.is_fine = '0';
  286. else i.is_fine = '1';
  287. return i;
  288. });
  289. // 初始化后取出不需要算的人,他们就这样,没必要算
  290. const reverseList = studentList.filter(
  291. f => !(f.is_fine !== '2' && f.job.includes('普通'))
  292. );
  293. // 过滤出取消评优资格的学生和干部;干部就是优秀;被取消资格就别凑热闹了
  294. studentList = studentList.filter(
  295. f => f.is_fine !== '2' && f.job.includes('普通')
  296. );
  297. // 获取平时分
  298. let dailyScoreList = await this.psmodel.find({ classid });
  299. // 去重复
  300. dailyScoreList = this.getDailyScore(dailyScoreList);
  301. studentList = this.dealScoreList(dailyScoreList, studentList, 'daily');
  302. // 获取作业分
  303. let taskScoreList = await this.upmodel.find({ classid });
  304. taskScoreList = this.getTaskScore(taskScoreList);
  305. studentList = this.dealScoreList(taskScoreList, studentList, 'task');
  306. // 获取小组分,小组
  307. const groupList = await this.gmodel.find({ classid });
  308. const groupScoreList = await this.gsmodel.find({ classid });
  309. studentList = this.dealGroupScoreList(
  310. groupList,
  311. groupScoreList,
  312. studentList
  313. );
  314. studentList = studentList.map(i => {
  315. i.score = _.round(
  316. (i.daily || 0) + (i.task || 0) + (i.groupscore || 0),
  317. 2
  318. );
  319. return i;
  320. });
  321. studentList = studentList.sort(
  322. (a, b) => (b.score * 1 || 0) - (a.score * 1 || 0)
  323. );
  324. // 排名
  325. // eslint-disable-next-line no-unused-vars
  326. let num = 0;
  327. for (const student of studentList) {
  328. // 先判断是否超过第10位,超过就跳出
  329. if (num >= 10) break;
  330. const { score, is_fine, job, name, _id } = student;
  331. // 最开始初始化过所有人的状态,并且将干部和不能评优的人都过滤出去,所以正常学生应该都是没有评优,如果此处已经是优秀,那么就是和前人同分改的,直接跳过就好
  332. if (is_fine === '1') continue;
  333. // 没有分凑什么热闹
  334. if (!score) continue;
  335. let plus = 1; // 这轮有多少人,到这了,这个人肯定是要改了,所以默认1
  336. // 评优
  337. student.is_fine = '1';
  338. const rlist = studentList.filter(
  339. f => f.score === score && !ObjectId(f._id).equals(_id)
  340. );
  341. // 处理同分的人也都变成is_fine
  342. for (const stud of rlist) {
  343. stud.is_fine = '1';
  344. const sindex = studentList.findIndex(f =>
  345. ObjectId(stud._id).equals(f._id)
  346. );
  347. if (sindex >= 0) {
  348. studentList[sindex] = stud;
  349. plus++;
  350. }
  351. }
  352. // num+plus,算下num
  353. num = num + plus;
  354. }
  355. // 算完的和不用算的合并,提交
  356. const lastList = [ ...studentList, ...reverseList ];
  357. for (const student of lastList) {
  358. // const res = await student.save();
  359. // const { meta, ...data } = student;
  360. const r = await this.model.findByIdAndUpdate(
  361. { _id: ObjectId(student._id) },
  362. { score: student.score, is_fine: student.is_fine }
  363. );
  364. }
  365. }
  366. /**
  367. * 将分数放到学生身上
  368. * @param {Array} scoreList 班级的分数列表
  369. * @param {Array} studentList 学生列表
  370. * @param {String} type 分数类型
  371. */
  372. dealScoreList(scoreList, studentList, type) {
  373. scoreList = JSON.parse(JSON.stringify(scoreList));
  374. studentList = JSON.parse(JSON.stringify(studentList));
  375. scoreList = _.groupBy(scoreList, 'studentid');
  376. const arr = [];
  377. for (let i of studentList) {
  378. const slist = scoreList[i._id];
  379. const obj = {};
  380. if (slist) {
  381. const score = slist.reduce((p, n) => p + (n.score * 1 || 0), 0);
  382. obj[type] = score;
  383. } else {
  384. obj[type] = 0;
  385. }
  386. i = Object.assign(i, obj);
  387. arr.push(i);
  388. }
  389. return arr;
  390. }
  391. /**
  392. * 将 学生所在 组的 团队平均分 + 到学生身上
  393. * @param {Array} groupList 班级的小组的列表
  394. * @param {Array} scoreList 所有小组的分数列表
  395. * @param {Array} studentList 学生列表
  396. */
  397. dealGroupScoreList(groupList, scoreList, studentList) {
  398. scoreList = _.groupBy(scoreList, 'groupid');
  399. // 算出每组的平均分,之后加给学生
  400. groupList = groupList.map(i => {
  401. const { students } = i;
  402. if (students.length > 0) {
  403. const slist = scoreList[i._id];
  404. if (slist) {
  405. i.score = slist.reduce((p, n) => p + (n.score * 1 || 0), 0);
  406. i.score = _.floor(_.divide(i.score, students.length), 2);
  407. }
  408. }
  409. return i;
  410. });
  411. // 每个学生加自己的组的平均分
  412. studentList = studentList.map(i => {
  413. const r = groupList.find(f =>
  414. f.students.find(sf => ObjectId(sf.stuid).equals(i._id))
  415. );
  416. if (r) {
  417. // i.score = _.round((i.score * 1 || 0) + (r.score * 1 || 0), 2);
  418. i.groupscore = r.score * 1 || 0;
  419. } else {
  420. i.groupscore = 0;
  421. }
  422. return i;
  423. });
  424. return studentList;
  425. }
  426. // 平时分去重处理
  427. getDailyScore(list) {
  428. const arr = [];
  429. const mid1 = _.groupBy(list, 'studentid');
  430. const keys = Object.keys(mid1);
  431. for (const key of keys) {
  432. const mid2 = _.uniqBy(mid1[key], 'subid');
  433. arr.push(...mid2);
  434. }
  435. return arr;
  436. }
  437. // 作业分去重处理
  438. getTaskScore(list) {
  439. const arr = [];
  440. const mid1 = _.groupBy(list, 'studentid');
  441. const keys = Object.keys(mid1);
  442. for (const key of keys) {
  443. const mid2 = _.uniqBy(mid1[key], 'lessonid');
  444. arr.push(...mid2);
  445. }
  446. return arr;
  447. }
  448. // 将学生排号
  449. async arrangeNumber({ classid }) {
  450. const studList = await this.model.find({ classid });
  451. let number = 1;
  452. // 查每个学生的编号,如果没有,就给赋上值;有,就给number赋上值,然后继续下一位
  453. for (const stu of studList) {
  454. if (!stu.number) {
  455. if (number * 1 < 10) stu.number = `0${number}`;
  456. else stu.number = number;
  457. await stu.save();
  458. } else {
  459. number = stu.number * 1;
  460. }
  461. number = number * 1 + 1;
  462. }
  463. number = 1;
  464. }
  465. // 建立导出学生名单的任务
  466. async toExport(body) {
  467. const { planid, termid, batchid, classid } = body;
  468. const trainPlan = await this.ctx.model.Trainplan.findById(planid);
  469. let fn = '学生名单';
  470. if (termid && termid.length > 0) {
  471. if (classid) {
  472. const cla = await this.ctx.model.Class.findById(classid);
  473. if (cla) {
  474. const { name } = cla;
  475. if (name) {
  476. fn = `${name.includes('班') ? name : `${name}班`}${fn}`;
  477. }
  478. }
  479. }
  480. if (termid.length === 1) {
  481. const tid = _.head(termid);
  482. const { termnum } = trainPlan;
  483. const t = termnum.id(tid);
  484. if (t) {
  485. const { term, batchnum } = t;
  486. if (batchid) {
  487. const b = batchnum.id(batchid);
  488. if (b) {
  489. const { batch } = b;
  490. if (b) fn = `第${batch}批${fn}`;
  491. }
  492. }
  493. if (term) fn = `第${term}期${fn}`;
  494. }
  495. } else if (termid.length > 1) {
  496. const { termnum } = trainPlan;
  497. let str = '第';
  498. for (const tid of termid) {
  499. const t = termnum.id(tid);
  500. if (t) {
  501. const { term } = t;
  502. str = `${str}${term} `;
  503. }
  504. }
  505. fn = `${str}期${fn}`;
  506. }
  507. } else {
  508. const { title } = trainPlan;
  509. fn = `${title}${fn}`;
  510. }
  511. const data = {
  512. title: fn,
  513. params: {
  514. project: 'center',
  515. // router: '/api/train/mission/student/export',
  516. service: 'student',
  517. method: 'exportStudent',
  518. body,
  519. },
  520. };
  521. if (this.missionBase) {
  522. const url = `${this.missionBase}/api/mission`;
  523. const res = await this.ctx.curl(url, {
  524. method: 'post',
  525. headers: {
  526. 'content-type': 'application/json',
  527. },
  528. data,
  529. dataType: 'json',
  530. });
  531. if (res.status !== 200 || res.data.errcode !== 0) {
  532. throw new BusinessError(ErrorCode.SERVICE_FAULT, '创建任务失败');
  533. }
  534. } else {
  535. throw new BusinessError(ErrorCode.SERVICE_FAULT, '未找到任务项目设置');
  536. }
  537. }
  538. // 导出学生
  539. async exportStudent(body) {
  540. let { missionid, model, ...data } = body;
  541. assert(missionid, '缺少任务信息,无法执行任务');
  542. try {
  543. // 整理model
  544. model = model.map(i => {
  545. const { zh, model } = i;
  546. if (zh && model) {
  547. const obj = {};
  548. obj.header = zh;
  549. if (model === 'termid') obj.key = 'termname';
  550. else if (model === 'batchid') obj.key = 'batchname';
  551. else if (model === 'classid') obj.key = 'classname';
  552. else obj.key = model;
  553. obj.width = 20;
  554. return obj;
  555. }
  556. });
  557. model = _.compact(model);
  558. // 请求数据
  559. // 修改条件,termid变成数组了,需要一个一个查出来
  560. const { planid, termid, batchid, classid, isComming } = data;
  561. const queryObject = {};
  562. if (planid) queryObject.planid = planid;
  563. if (batchid) queryObject.batchid = batchid;
  564. if (classid) queryObject.classid = classid;
  565. if (isComming) {
  566. // 因为isComming有3种,0=>没来;1=>来了;2=>来了又退了.而已参培的类型应该是不为0,而不是单纯的1,只管来没来,不管走没走,毕竟吃饭了,得交钱
  567. if (isComming !== '0') queryObject.isComming = { $ne: '0' };
  568. else queryObject.isComming = isComming;
  569. }
  570. let studentList = [];
  571. console.log(queryObject);
  572. if (termid && termid.length > 0) {
  573. for (const t of termid) {
  574. queryObject.termid = t;
  575. const stuList = await this.query(queryObject);
  576. studentList.push(...stuList);
  577. }
  578. } else {
  579. studentList = await this.query(queryObject);
  580. }
  581. console.log(studentList.length);
  582. // 获取学生,4分之1
  583. this.ctx.service.util.updateProcess(missionid, '25');
  584. console.log('学生导出=>25%');
  585. studentList = JSON.parse(JSON.stringify(studentList));
  586. let ids = studentList.map(i => i.classid);
  587. ids = _.uniq(ids);
  588. ids = _.compact(ids);
  589. ids = ids.map(i => ObjectId(i));
  590. const classList = await this.ctx.model.Class.find({
  591. _id: { $in: ids },
  592. });
  593. for (const stu of studentList) {
  594. // 转换是否参培
  595. const { classid, isComming } = stu;
  596. if (!classid) continue;
  597. const cla = await this.ctx.service.class.fetch({ id: classid });
  598. if (!cla) continue;
  599. const { startdate } = cla;
  600. if (!startdate) continue;
  601. stu.insurance = moment(startdate).add(1, 'd').format('YYYY-MM-DD');
  602. if (isComming) {
  603. if (isComming === '0') stu.isComming = '否';
  604. if (isComming === '1') stu.isComming = '是';
  605. }
  606. }
  607. // 整理完数据,4分之2
  608. this.ctx.service.util.updateProcess(missionid, '50');
  609. console.log('学生导出=>50%');
  610. let fn = '学生名单';
  611. // 因为是递进下来, batchid和classid并列,并非递进
  612. // 获取fn
  613. if (classid) {
  614. // 文件名称: 期, 班
  615. // 班级名上面有直接拽来
  616. const cla = classList.find(f => ObjectId(classid).equals(f._id));
  617. if (cla) {
  618. const { name, termid } = cla;
  619. if (name) fn = `${name.includes('班') ? name : `${name}班`}${fn}`;
  620. if (termid) {
  621. const obj = await this.toGetFn(termid);
  622. if (obj) {
  623. const { term } = obj;
  624. fn = `第${term}期${fn}`;
  625. }
  626. }
  627. }
  628. } else if (batchid) {
  629. // 文件名称,期,批
  630. const tid = _.head(termid);
  631. const obj = await this.toGetFn(tid, batchid);
  632. if (obj) {
  633. const { term, batch } = obj;
  634. if (batch) fn = `第${batch}批${fn}`;
  635. if (term) fn = `第${term}期${fn}`;
  636. }
  637. } else if (termid) {
  638. // 文件名称: 期
  639. if (termid.length === 1) {
  640. const obj = await this.toGetFn(_.head(termid));
  641. if (obj) {
  642. const { term } = obj;
  643. if (term) fn = `第${term}期${fn}`;
  644. }
  645. } else {
  646. let tStr = '';
  647. for (let i = 0; i < termid.length; i++) {
  648. const tid = termid[i];
  649. const obj = await this.toGetFn(tid);
  650. if (obj) {
  651. const { term } = obj;
  652. if (term) {
  653. if (i === 0) {
  654. tStr += `${term}期`;
  655. } else {
  656. tStr += `,${term}期`;
  657. }
  658. }
  659. }
  660. }
  661. fn = `${tStr}${fn}`;
  662. }
  663. } else if (planid) {
  664. // 文件名称:该计划标题
  665. const trainPlan = await this.ctx.model.Trainplan.findById(planid);
  666. if (trainPlan) {
  667. const { title } = trainPlan;
  668. if (title) fn = `${title}${fn}`;
  669. }
  670. }
  671. // 获取文件名,60%
  672. this.ctx.service.util.updateProcess(missionid, '60');
  673. console.log('学生导出=>60%');
  674. const res = await this.ctx.service.util.toExcel(studentList, model, fn);
  675. if (!res) {
  676. console.error(
  677. `${moment().format('YYYY-MM-DD HH:SS:mm')} ${fn} 导出失败`
  678. );
  679. throw new BusinessError(ErrorCode.SERVICE_FAULT, `${fn}导出失败`);
  680. }
  681. this.ctx.service.util.updateProcess(missionid, '100', '2', {
  682. uri: res,
  683. });
  684. console.log('学生导出=>100%');
  685. } catch (error) {
  686. this.ctx.service.util.updateProcess(missionid, undefined, '3');
  687. }
  688. }
  689. async toGetFn(termid, batchid) {
  690. const trainPlan = await this.ctx.model.Trainplan.findOne({
  691. 'termnum._id': ObjectId(termid),
  692. });
  693. if (!trainPlan) {
  694. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划信息');
  695. }
  696. const { termnum } = trainPlan;
  697. if (!termnum) {
  698. throw new BusinessError(
  699. ErrorCode.DATA_NOT_EXIST,
  700. '未找到年度计划的期信息'
  701. );
  702. }
  703. const obj = {};
  704. if (termid) {
  705. const term = termnum.id(termid);
  706. if (term) obj.term = term.term;
  707. if (batchid) {
  708. const { batchnum } = term;
  709. const batch = batchnum.id(batchid);
  710. if (batch) obj.batch = batch.batch;
  711. }
  712. }
  713. return obj;
  714. }
  715. // 确认学生打印证书
  716. async printCert({ ids }) {
  717. const res = await this.model.updateMany(
  718. { _id: { $in: ids.map(i => ObjectId(i)) } },
  719. { cert: '1' }
  720. );
  721. return res;
  722. }
  723. // 根据计划id找到学校是否上传学生
  724. async getSchoolStudent({ planid }) {
  725. const res = await this.model.aggregate([
  726. { $match: { planid } },
  727. { $group: { _id: '$schid', sum: { $sum: 1 } } },
  728. { $project: { _id: 0, schid: '$_id', schnum: '$sum' } },
  729. ]);
  730. return res;
  731. }
  732. // excel导出表头设置
  733. metaBx(type) {
  734. const header = [
  735. {
  736. header: '姓名',
  737. key: 'name',
  738. width: 20,
  739. },
  740. ];
  741. return header;
  742. }
  743. }
  744. module.exports = StudentService;