student.js 24 KB

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