apply.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. 'use strict';
  2. const assert = require('assert');
  3. const { after } = require('lodash');
  4. const _ = require('lodash');
  5. const moment = require('moment');
  6. const { ObjectId } = require('mongoose').Types;
  7. const { CrudService } = require('naf-framework-mongoose/lib/service');
  8. const { BusinessError, ErrorCode } = require('naf-core').Error;
  9. class ApplyService extends CrudService {
  10. constructor(ctx) {
  11. super(ctx, 'apply');
  12. this.model = this.ctx.model.Apply;
  13. this.tmodel = this.ctx.model.Teacher;
  14. this.submodel = this.ctx.model.Subject;
  15. this.trainmodel = this.ctx.model.Trainplan;
  16. this.dayList = [ '日', '一', '二', '三', '四', '五', '六' ];
  17. }
  18. // 查询
  19. async queryteacher(query) {
  20. const { termid, subid, date } = query;
  21. const data = await this.model
  22. .find({ termid, subid, date })
  23. .sort({ msscore: -1 });
  24. const teachers = [];
  25. for (const _data of data) {
  26. const teacherid = _data.teacherid;
  27. const teacher = await this.tmodel.findById(teacherid);
  28. teachers.push(teacher);
  29. }
  30. return teachers;
  31. }
  32. // 教师计划初步课表安排,可反复使用
  33. async arrangeteacher({ planid }) {
  34. const trainplan = await this.trainmodel.findById(planid);
  35. if (!trainplan) {
  36. throw new BusinessError(ErrorCode.DATA_EXISTED, '年度计划不存在');
  37. }
  38. // trainplan = JSON.parse(JSON.stringify(trainplan));
  39. // 查找所有教师列表
  40. let teacherList = await this.tmodel.find({ xsscore: { $exists: true } });
  41. teacherList = JSON.parse(JSON.stringify(teacherList));
  42. // 查找所有教师上报列表
  43. let teaplanList = await this.model.find();
  44. teaplanList = JSON.parse(JSON.stringify(teaplanList));
  45. // 课程
  46. let subjectList = await this.submodel.find();
  47. subjectList = JSON.parse(JSON.stringify(subjectList));
  48. const termList = _.cloneDeep(trainplan);
  49. let { termnum } = termList;
  50. if (!termnum) return;
  51. termnum = JSON.parse(JSON.stringify(termnum));
  52. // 整理出课表
  53. const arr = this.setLessonList(termnum);
  54. // 安排后的课表
  55. const afterList = [];
  56. // 排课
  57. for (const l of arr) {
  58. const { termid, subid, day: date, teaid, status, batchid } = l;
  59. if (status && `${status}` === '1') {
  60. afterList.push(l);
  61. continue;
  62. }
  63. const subject = subjectList.find(f => ObjectId(subid).equals(f._id));
  64. if (subject.need_teacher !== '0') {
  65. afterList.push(l);
  66. continue;
  67. }
  68. // 申请该天,该科目的教师,并查出教师的名字,分数;并按分数排序
  69. let applyList = teaplanList.filter(
  70. f => f.date === date && f.subid === subid
  71. );
  72. // console.log(applyList.length);
  73. applyList = applyList.map(i => {
  74. let obj = { ...JSON.parse(JSON.stringify(i)) };
  75. const r = teacherList.find(f => i.teacherid === f._id);
  76. if (r) {
  77. const { name: teaname, xsscore: score } = r;
  78. i.teaname = teaname;
  79. i.score = score * 1;
  80. obj = { ...obj, teaname, score };
  81. }
  82. return obj;
  83. });
  84. // 过滤出没有分数的,不排
  85. applyList = applyList.filter(f => f.score);
  86. // 按成绩排序
  87. applyList = _.orderBy(applyList, [ 'score' ], [ 'desc' ]);
  88. // 本期超过2次的教师列表,如果没有人就用这里分最高的排
  89. const outTwoTimesList = [];
  90. // 依次循环申请的教师列表,往这个课程安排中放教师
  91. for (const atea of applyList) {
  92. // 先查询,该教师,是否在今天有安排
  93. const tr = afterList.find(
  94. f => f.teaid === atea.teacherid && f.day === atea.date
  95. );
  96. if (tr) continue;
  97. // 查看这期内,每个申请上课的教师时候超过2天(2条记录),如果超过,则不排,但是如果最后没有人了,就得硬排了
  98. const r = afterList.filter(
  99. f => f.termid === termid && f.teaid === atea.teacherid
  100. );
  101. if (r.length >= 2) {
  102. outTwoTimesList.push(atea);
  103. continue;
  104. } else {
  105. l.teaid = atea.teacherid;
  106. l.teaname = atea.teaname;
  107. break;
  108. }
  109. }
  110. // 检查,该天,该科的课是否有教师
  111. const has_teaid = _.has(l, 'teaid');
  112. if (!has_teaid) {
  113. // 如果没有教师,就需要在outTowTimesList列表中找分最高的教师
  114. // const head = _.head();
  115. // if (head) {
  116. // l.teaid = head.teacherid;
  117. // l.teaname = head.teaname;
  118. // }
  119. const list = _.orderBy(outTwoTimesList, [ 'score' ], [ 'desc' ]);
  120. for (const i of list) {
  121. const tr = afterList.find(
  122. f => f.teaid === i.teacherid && f.day === i.date
  123. );
  124. if (tr) continue;
  125. else {
  126. l.teaid = i.teacherid;
  127. l.teaname = i.teaname;
  128. }
  129. }
  130. }
  131. afterList.push(l);
  132. }
  133. // 将afterList还原回正常的termnum;
  134. const newTermnum = this.returnTermnum(afterList, termnum);
  135. // 保存至计划
  136. trainplan.termnum = newTermnum;
  137. await trainplan.save();
  138. }
  139. /**
  140. * 拍平了的课表=>termnum
  141. * @param {Array} list 拍平了的课表,详情参考页面的初步课表的数据
  142. * @param {Array} termnum 原termnum
  143. */
  144. returnTermnum(list, termnum) {
  145. let newTermnum = [];
  146. for (const l of list) {
  147. const { termid, batchid, classid, ...info } = l;
  148. const updata = _.pick(info, [
  149. 'day',
  150. 'subid',
  151. 'subname',
  152. 'teaid',
  153. 'teaname',
  154. 'time',
  155. 'status',
  156. ]);
  157. newTermnum = termnum.map(t => {
  158. // 找到期
  159. if (termid === t._id) {
  160. t.batchnum = t.batchnum.map(b => {
  161. if (batchid === b._id) {
  162. // 找到批次
  163. b.class = b.class.map(c => {
  164. if (classid === c._id) {
  165. if (c.lessons) {
  166. // 说明有课程安排,找有没有重复的,没有就推进去,有就更改,subid查
  167. const r = c.lessons.find(f => f.subid === updata.subid);
  168. if (r) {
  169. const rindex = c.lessons.findIndex(
  170. f => f.subid === updata.subid
  171. );
  172. c.lessons[rindex] = updata;
  173. } else {
  174. c.lessons.push(updata);
  175. }
  176. } else {
  177. // 说明没有课程安排,放进去一条保存
  178. c.lessons = [ updata ];
  179. }
  180. }
  181. return c;
  182. });
  183. }
  184. return b;
  185. });
  186. }
  187. return t;
  188. });
  189. }
  190. return newTermnum;
  191. }
  192. /**
  193. * 将课表拍平了,从多维=>一维
  194. * @param {Array} termnum 计划的termnum
  195. */
  196. setLessonList(termnum) {
  197. const arr = [];
  198. for (const t of termnum) {
  199. const { batchnum, term, _id: termid } = t;
  200. // 班级和课程一一匹
  201. for (const b of batchnum) {
  202. const { class: classes, lessons, _id: batchid } = b;
  203. const claslesList = this.setList(
  204. term,
  205. termid,
  206. batchid,
  207. classes,
  208. lessons
  209. );
  210. arr.push(...claslesList);
  211. }
  212. }
  213. return arr;
  214. }
  215. /**
  216. * 将课表模板和班级整理成一维数组
  217. * @param {String} term 期数
  218. * @param {String} termid 期id
  219. * @param {String} batchid 批id
  220. * @param {Array} classes 班级列表
  221. * @param {Array} lessonTemplate 课表模板
  222. */
  223. setList(term, termid, batchid, classes, lessonTemplate) {
  224. const arr = [];
  225. // 班级和课程匹配
  226. for (const cla of classes) {
  227. let { lessons } = cla;
  228. if (!lessons) lessons = lessonTemplate;
  229. for (const i of lessons) {
  230. let nobj = {};
  231. nobj.term = term;
  232. nobj.termid = termid;
  233. nobj.batchid = batchid;
  234. const obj = _.omit(cla, [ 'lessons' ]);
  235. nobj.classid = _.clone(cla._id);
  236. nobj = _.assign(nobj, obj);
  237. nobj = _.assign(nobj, i);
  238. arr.push(nobj);
  239. }
  240. }
  241. return arr;
  242. }
  243. async arrangeSendMsg({ planid, ids }) {
  244. const trainplan = await this.trainmodel.findById(planid);
  245. if (!trainplan) {
  246. throw new BusinessError(ErrorCode.DATA_EXISTED, '年度计划不存在');
  247. }
  248. const plan = _.cloneDeep(trainplan);
  249. let { termnum } = plan;
  250. if (!termnum) return;
  251. termnum = JSON.parse(JSON.stringify(termnum));
  252. // 整理出课表
  253. let arr = this.setLessonList(termnum);
  254. // 过滤出需要发送的教师
  255. arr = arr.filter(f => ids.find(id => f.termid === id) && f.teaid);
  256. // && f.status !== '1'
  257. // 整理出要发送的教师列表
  258. let teaids = arr.map(i => i.teaid);
  259. teaids = _.uniq(teaids);
  260. // 找到教师信息
  261. let teaList = await this.tmodel.find({ _id: teaids });
  262. if (teaList) teaList = JSON.parse(JSON.stringify(teaList));
  263. // 发送,此处是根据安排,给教师发.还有一种方案是根据教师,整理安排一起发送
  264. for (const l of arr) {
  265. // 教师id,期数,班级名,上课的日期,课程名
  266. const { teaid, term, name, day, subname } = l;
  267. const tea = teaList.find(f => f._id === teaid);
  268. // 文案
  269. const msg = `${_.get(tea, 'name', '')}老师您好:
  270. 吉林省高等学校毕业生就业指导中心-双困生培训系统提醒您:
  271. ${term}期-${name.includes('班') ? name : `${name}班`}
  272. ${day}(星期${this.dayList[moment(day).days()]})
  273. 有您的课程安排:${subname}`;
  274. // 邮箱与微信都发送
  275. const { openid, email } = tea;
  276. if (email) {
  277. this.toSendEmail('402788946@qq.com', msg, tea.name);
  278. }
  279. if (openid) this.toSendWxMsg('ocPqjswkUejZHq2ANriNrFFC7A3I', msg, tea.name);
  280. }
  281. }
  282. /**
  283. * 计划-教师初步课表发送邮件
  284. * @param {String} email 邮件
  285. * @param {String} content 内容
  286. * @param {String} teaname 教师姓名
  287. */
  288. async toSendEmail(email, content, teaname) {
  289. if (!email) {
  290. console.error(`计划教师发送通知:${teaname}没有email`);
  291. return;
  292. }
  293. const subject = '吉林省高等学校毕业生就业指导中心通知(测试)'; //
  294. this.ctx.service.util.sendMail(email, subject, content);
  295. }
  296. /**
  297. * 计划-教师初步课表发送微信推送
  298. * @param {String} openid 微信公众号的openid
  299. * @param {String} content 内容
  300. * @param {String} teaname 教师姓名
  301. */
  302. async toSendWxMsg(openid, content, teaname) {
  303. if (!openid) {
  304. console.error(`计划教师发送微信推送:${teaname}没有openid`);
  305. return;
  306. }
  307. // TODO or notTODO 发送微信推送记录
  308. // const tourl = this.ctx.app.config.baseUrl + '/msgconfirm/?userid=' + teacherUser.uid + '&noticeid=' + nresid;
  309. await this.ctx.service.weixin.sendTemplateDesign(
  310. this.ctx.app.config.REVIEW_TEMPLATE_ID,
  311. openid,
  312. // '您有一个新的通知',
  313. '测试信息推送',
  314. '您有新的安排',
  315. content,
  316. '感谢您的使用'
  317. // tourl
  318. );
  319. }
  320. }
  321. module.exports = ApplyService;