lesson.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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 LessonService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'lesson');
  11. this.model = this.ctx.model.Lesson;
  12. this.tmodel = this.ctx.model.Trainplan;
  13. this.clamodel = this.ctx.model.Class;
  14. this.lmodel = this.ctx.model.Lessonmode;
  15. this.teamodel = this.ctx.model.Teacher;
  16. this.stumodel = this.ctx.model.Student;
  17. this.schmodel = this.ctx.model.School;
  18. this.headteamodel = this.ctx.model.Headteacher;
  19. this.umodel = this.ctx.model.User;
  20. this.nmodel = this.ctx.model.Notice;
  21. }
  22. // 自动排课私有方法
  23. async autolesson({ id }) {
  24. // 首先将课程表清空
  25. const res = await this.tmodel.findById(id);
  26. if (!res) {
  27. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '全年计划信息不存在');
  28. }
  29. const terms = res.termnum;
  30. const _lessonmode = await this.lmodel.find();
  31. // 循环取得所有期
  32. for (const elm of terms) {
  33. // 根据期id清空课程表
  34. await this.model.deleteMany({ termid: elm.id });
  35. // 清空成功后,循环取得当前期下所有批次信息
  36. const batchs = elm.batchnum;
  37. for (const batch of batchs) {
  38. // 取得当前批次开始结束日期,并根据日期取得所有天数
  39. const sedays = await this.getAllDays(batch.startdate, batch.enddate);
  40. // 根据批次取得当前批次下所有班级
  41. const _classs = await this.clamodel.find({
  42. planid: id,
  43. termid: elm.id,
  44. batchid: batch.id,
  45. });
  46. // 循环班级
  47. const teachids = [];
  48. for (const cla of _classs) {
  49. // 取得课程模板信息
  50. let lessonmode_ = _.find(_lessonmode, { type: cla.type });
  51. if (!lessonmode_) {
  52. lessonmode_ = _lessonmode[0];
  53. if (!lessonmode_) {
  54. throw new BusinessError(
  55. ErrorCode.DATA_NOT_EXIST,
  56. '课程模板信息不存在'
  57. );
  58. }
  59. }
  60. // 取得模板内容并转化成json
  61. const lessons_ = JSON.parse(lessonmode_.lessons);
  62. // 记录天数
  63. let i = 1;
  64. // 循环天数
  65. const newlesson = [];
  66. for (const day of sedays) {
  67. // 循环课程模板,将模板信息排入班级课程表中
  68. for (const lessm of lessons_) {
  69. // 循环插入模板信息
  70. if (lessm['day' + i] !== '--') {
  71. let _subid = '';
  72. if (lessm['day' + i + 'type'] === '课程') {
  73. _subid = lessm['day' + i + 'subid'];
  74. } else {
  75. _subid = '';
  76. }
  77. let allday = 0;
  78. if (i === 6) {
  79. // 判断是否有外市的学生有的时候 将其设置为半天
  80. const ishalfday = await this.ishalfday(cla.id);
  81. if (ishalfday) {
  82. allday = 1;
  83. }
  84. }
  85. const data = {
  86. subid: _subid,
  87. subname: lessm['day' + i],
  88. date: day,
  89. time: lessm.time,
  90. day: allday,
  91. };
  92. // 将教师按照分数的综合成绩排序,上报时间,安排教师.
  93. const teacher_ = await this.autoteacher(_subid, teachids);
  94. if (teacher_) {
  95. data.teaid = teacher_.id;
  96. data.teaname = teacher_.name;
  97. teachids.push(teacher_.id);
  98. }
  99. newlesson.push(data);
  100. }
  101. }
  102. i = i + 1;
  103. }
  104. const newdata = {
  105. termid: elm.id,
  106. batchid: batch.id,
  107. classid: cla.id,
  108. lessons: newlesson,
  109. };
  110. // 将课程信息填入课程表
  111. await this.model.create(newdata);
  112. }
  113. }
  114. }
  115. }
  116. // 自动排教师,按照分数的综合成绩排序,上报时间,安排教师
  117. async autoteacher(subid, teachids) {
  118. // 按照上报时间取得所有老师,进行正序排列
  119. const teachers = await this.teamodel
  120. .find({ subid, status: '4' })
  121. .sort({ zlscore: '-1', msscore: '-1', xsscore: '-1' });
  122. for (const teaid of teachids) {
  123. _.remove(teachers, item => item.id === teaid);
  124. }
  125. let teacher = {};
  126. if (teachers.length > 0) {
  127. teacher = teachers[0];
  128. }
  129. return teacher;
  130. }
  131. // 判断是否为半天
  132. async ishalfday(classid) {
  133. // 通过班级id取得所有学生
  134. const students = await this.stumodel.find({ classid });
  135. let res = false;
  136. for (const stu of students) {
  137. const sch = await this.schmodel.findOne({ code: stu.schid });
  138. if (sch && sch.hascar === '0') {
  139. res = true;
  140. break;
  141. }
  142. }
  143. return res;
  144. }
  145. // 取得日期间所有日期
  146. async getAllDays(begin_date, end_date) {
  147. const errArr = [],
  148. resultArr = [],
  149. dateReg = /^[2]\d{3}-[01]\d-[0123]\d$/;
  150. if (
  151. typeof begin_date !== 'string' ||
  152. begin_date === '' ||
  153. !dateReg.test(begin_date)
  154. ) {
  155. return errArr;
  156. }
  157. if (
  158. typeof end_date !== 'string' ||
  159. end_date === '' ||
  160. !dateReg.test(end_date)
  161. ) {
  162. return errArr;
  163. }
  164. try {
  165. const beginTimestamp = Date.parse(new Date(begin_date)),
  166. endTimestamp = Date.parse(new Date(end_date));
  167. // 开始日期小于结束日期
  168. if (beginTimestamp > endTimestamp) {
  169. return errArr;
  170. }
  171. // 开始日期等于结束日期
  172. if (beginTimestamp === endTimestamp) {
  173. resultArr.push(begin_date);
  174. return resultArr;
  175. }
  176. let tempTimestamp = beginTimestamp,
  177. tempDate = begin_date;
  178. // 新增日期是否和结束日期相等, 相等跳出循环
  179. while (tempTimestamp !== endTimestamp) {
  180. resultArr.push(tempDate);
  181. // 增加一天
  182. tempDate = moment(tempTimestamp).add(1, 'd').format('YYYY-MM-DD');
  183. // 将增加时间变为时间戳
  184. tempTimestamp = Date.parse(new Date(tempDate));
  185. }
  186. // 将最后一天放入数组
  187. resultArr.push(end_date);
  188. return resultArr;
  189. } catch (err) {
  190. return errArr;
  191. }
  192. }
  193. // 根据计划id、教师id查询所有班级信息
  194. async classbyteaid({ planid, teaid }) {
  195. // 取得传入的计划id与教师id
  196. // 根据计划id取得所有期
  197. const plan = await this.tmodel.findById(planid);
  198. if (!plan) {
  199. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '全年计划信息不存在');
  200. }
  201. const terms = await plan.termnum;
  202. // 循环取得所有期信息
  203. const data = [];
  204. for (const term of terms) {
  205. // 根据期id与教师id查出课程班级信息
  206. const lessons = await this.model.find({
  207. termid: term.id,
  208. 'lessons.teaid': teaid,
  209. });
  210. const batchs = await term.batchnum;
  211. for (const elm of lessons) {
  212. const newdata = {};
  213. const batch = await batchs.id(elm.batchid);
  214. newdata.planid = planid;
  215. newdata.title = plan.title;
  216. newdata.termid = elm.termid;
  217. newdata.term = term.term;
  218. newdata.batchid = elm.batchid;
  219. newdata.batch = batch.batch;
  220. newdata.classid = elm.classid;
  221. if (elm.classid) {
  222. const cla = await this.clamodel.findById(elm.classid);
  223. if (cla) {
  224. newdata.classname = cla.name;
  225. }
  226. }
  227. const _lessons = elm.lessons.filter(item => item.teaid === teaid);
  228. newdata.lessons = _lessons;
  229. data.push(newdata);
  230. }
  231. }
  232. return data;
  233. }
  234. // 根据计划id、教师id查询所有班级信息
  235. async teaclass({ planid, teaid }) {
  236. // 取得传入的计划id与教师id
  237. // 根据计划id取得所有期
  238. const plan = await this.tmodel.findById(planid);
  239. if (!plan) {
  240. throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '全年计划信息不存在');
  241. }
  242. const terms = await plan.termnum;
  243. // 循环取得所有期信息
  244. const data = [];
  245. for (const term of terms) {
  246. // 根据期id与教师id查出课程班级信息
  247. const lessons = await this.model.find({
  248. termid: term.id,
  249. 'lessons.teaid': teaid,
  250. });
  251. for (const elm of lessons) {
  252. if (elm.classid) {
  253. const cla = await this.ctx.service.class.fetch({ id: elm.classid });
  254. data.push(cla);
  255. }
  256. }
  257. }
  258. return data;
  259. }
  260. async uplessones(data) {
  261. for (const _data of data) {
  262. await this.model.findByIdAndUpdate(_data.id, _data);
  263. }
  264. }
  265. /**
  266. * 修改课表的状态,并发送信息
  267. * @param {Array} ids 要修改的课表
  268. */
  269. async check({ ids }) {
  270. // 1,修改课表状态; TODO 2,拿到所有的班级,获取所有人员;3,然后发送信息
  271. const list = await this.model.find({ _id: { $in: ids } });
  272. const res = await this.model.updateMany({ _id: { $in: ids } }, { status: '1' });
  273. // 循环课表
  274. for (const lessonInfo of list) {
  275. // 获取期数
  276. const { termid, classid, lessons } = lessonInfo;
  277. const planRes = await this.tmodel.findOne({ termnum: { $elemMatch: { _id: termid } } });
  278. if (!planRes) continue;
  279. const term = planRes.termnum.find(f => ObjectId(termid).equals(f._id));
  280. if (!term) continue;
  281. const { term: termnum } = term;
  282. const classInfo = await this.clamodel.findById(classid);
  283. // 先查找信息,如果有,不需要创建,修改创建信息,获取数据
  284. const content = `欢迎参加由吉林省高等学校毕业生就业指导中心举办的"双困生培训会"第${termnum}期-${classInfo.name}${classInfo.name.includes('班') ? '' : '班'}`;
  285. let is_update = false;
  286. let nres = await this.nmodel.findOne({
  287. planyearid: planRes.planyearid,
  288. planid: planRes._id,
  289. termid,
  290. classid,
  291. noticeid: 'system',
  292. type: '4',
  293. });
  294. console.log(nres);
  295. if (nres) is_update = true;
  296. if (!is_update) {
  297. nres = await this.nmodel.create({
  298. planyearid: planRes.planyearid,
  299. planid: planRes._id,
  300. termid,
  301. classid,
  302. noticeid: 'system',
  303. type: '4',
  304. content,
  305. });
  306. }
  307. const { headteacherid, lyteacherid } = classInfo;
  308. const headteacher = await this.headteamodel.findById(headteacherid);
  309. if (headteacher) {
  310. const r = await this.toSendMsg(headteacher, 'headteacher', termnum, nres._id, content);
  311. if (r && !is_update) nres.notified.push(r);
  312. else if (r && is_update) {
  313. const dr = nres.notified.find(f => f.notifiedid === r.notifiedid);
  314. if (!dr) nres.notified.push(r);
  315. }
  316. }
  317. // 礼仪教师和班主任不是一个人时查礼仪教师是班主任还是任课教师,然后发消息
  318. if (lyteacherid !== headteacherid) {
  319. let lyTeacher = await this.headteamodel.findById(lyteacherid);
  320. if (lyTeacher) {
  321. const r = await this.toSendMsg(lyTeacher, 'headteacher', termnum, nres._id, content);
  322. if (r && !is_update) nres.notified.push(r);
  323. else if (r && is_update) {
  324. const dr = nres.notified.find(f => f.notifiedid === r.notifiedid);
  325. if (!dr) nres.notified.push(r);
  326. }
  327. } else {
  328. lyTeacher = await this.teamodel.findById(lyteacherid);
  329. if (lyTeacher) {
  330. const r = await this.toSendMsg(lyTeacher, 'teacher', termnum, nres._id, content);
  331. if (r && !is_update) nres.notified.push(r);
  332. else if (r && is_update) {
  333. const dr = nres.notified.find(f => f.notifiedid === r.notifiedid);
  334. if (!dr) nres.notified.push(r);
  335. }
  336. }
  337. }
  338. }
  339. // 获取所有任课教师ids
  340. const teacherList = _.compact(_.uniq(lessons.map(i => i.teaid)));
  341. if (teacherList) {
  342. for (const tea of teacherList) {
  343. const teacher = await this.teamodel.findById(tea);
  344. if (!teacher) continue;
  345. const r = await this.toSendMsg(teacher, 'teacher', termnum, nres._id, content);
  346. if (r && !is_update) nres.notified.push(r);
  347. else if (r && is_update) {
  348. const dr = nres.notified.find(f => f.notifiedid === r.notifiedid);
  349. if (!dr) nres.notified.push(r);
  350. }
  351. }
  352. }
  353. nres.save();
  354. }
  355. }
  356. async toSendMsg(teacherInfo, type, term, nresid, content) {
  357. let person = null;
  358. if (teacherInfo) {
  359. let email;
  360. if (type === 'headteacher') {
  361. const { qq } = teacherInfo;
  362. if (qq) email = `${qq}@qq.com`;
  363. } else {
  364. email = teacherInfo.email;
  365. }
  366. // 发邮件
  367. if (email) {
  368. console.error(`${teacherInfo.name}-email:${email}`);
  369. const subject = '吉林省高等学校毕业生就业指导中心通知';
  370. const text = teacherInfo.name + `${content},请您尽快登陆双困生培训系统查看您的安排`;
  371. this.ctx.service.util.sendMail(email, subject, text);
  372. }
  373. // 获取openid,推送
  374. const teacherUser = await this.umodel.findOne({ uid: teacherInfo._id });
  375. if (teacherUser) {
  376. const { openid } = teacherUser;
  377. if (openid) {
  378. const tourl = this.ctx.app.config.baseUrl + '/trainnotice/?userid=' + teacherUser.uid + '&noticeid=' + nresid;
  379. // TODO 推送
  380. await this.ctx.service.weixin.sendTemplateDesign(
  381. this.ctx.app.config.REVIEW_TEMPLATE_ID,
  382. openid,
  383. '您有一个新的通知,请点击信息,确认您已收到信息!(若已确认,则无需理会)',
  384. '您有新的安排',
  385. content,
  386. '感谢您的使用',
  387. tourl
  388. );
  389. person = { notifiedid: teacherUser.uid, username: teacherUser.name };
  390. }
  391. }
  392. }
  393. return person;
  394. }
  395. }
  396. module.exports = LessonService;