123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- 'use strict';
- const assert = require('assert');
- const { after } = require('lodash');
- const _ = require('lodash');
- const moment = require('moment');
- const { ObjectId } = require('mongoose').Types;
- const { CrudService } = require('naf-framework-mongoose/lib/service');
- const { BusinessError, ErrorCode } = require('naf-core').Error;
- class ApplyService extends CrudService {
- constructor(ctx) {
- super(ctx, 'apply');
- this.model = this.ctx.model.Apply;
- this.tmodel = this.ctx.model.Teacher;
- this.submodel = this.ctx.model.Subject;
- this.trainmodel = this.ctx.model.Trainplan;
- this.umodel = this.ctx.model.User;
- this.nmodel = this.ctx.model.Notice;
- this.dayList = [ '日', '一', '二', '三', '四', '五', '六' ];
- }
- // 查询
- async queryteacher(query) {
- const { termid, subid, date } = query;
- const data = await this.model
- .find({ termid, subid, date })
- .sort({ msscore: -1 });
- const teachers = [];
- for (const _data of data) {
- const teacherid = _data.teacherid;
- const teacher = await this.tmodel.findById(teacherid);
- teachers.push(teacher);
- }
- return teachers;
- }
- // 教师计划初步课表安排,可反复使用
- async arrangeteacher({ planid }) {
- const trainplan = await this.trainmodel.findById(planid);
- if (!trainplan) {
- throw new BusinessError(ErrorCode.DATA_EXISTED, '年度计划不存在');
- }
- // trainplan = JSON.parse(JSON.stringify(trainplan));
- // 查找所有教师列表
- let teacherList = await this.tmodel.find({ xsscore: { $exists: true } });
- teacherList = JSON.parse(JSON.stringify(teacherList));
- // 查找所有教师上报列表
- let teaplanList = await this.model.find();
- teaplanList = JSON.parse(JSON.stringify(teaplanList));
- // 课程
- let subjectList = await this.submodel.find();
- subjectList = JSON.parse(JSON.stringify(subjectList));
- const termList = _.cloneDeep(trainplan);
- let { termnum } = termList;
- if (!termnum) return;
- termnum = JSON.parse(JSON.stringify(termnum));
- // 整理出课表
- const arr = this.setLessonList(termnum);
- // 安排后的课表
- const afterList = [];
- // 排课
- for (const l of arr) {
- const { termid, subid, day: date, teaid, status, batchid } = l;
- // 本期超过2次的教师列表,如果没有人就用这里分最高的排
- let outTwoTimesList = [];
- if (status && `${status}` === '1') {
- afterList.push(l);
- continue;
- }
- const subject = subjectList.find(f => ObjectId(subid).equals(f._id));
- if (subject.need_teacher !== '0') {
- afterList.push(l);
- continue;
- }
- // 申请该天,该科目的教师,并查出教师的名字,分数;并按分数排序
- let applyList = teaplanList.filter(
- f => f.date === date && f.subid === subid
- );
- applyList = applyList.map(i => {
- let obj = { ...JSON.parse(JSON.stringify(i)) };
- const r = teacherList.find(f => i.teacherid === f._id);
- if (r) {
- const { name: teaname, xsscore: score } = r;
- i.teaname = teaname;
- i.score = score * 1;
- obj = { ...obj, teaname, score };
- }
- return obj;
- });
- // 过滤出没有分数的,不排
- applyList = applyList.filter(f => f.score);
- // 按成绩排序
- applyList = _.orderBy(applyList, [ 'score' ], [ 'desc' ]);
- // 依次循环申请的教师列表,往这个课程安排中放教师
- for (const atea of applyList) {
- // 先查询,该教师,是否在今天有安排
- const tr = afterList.find(
- f => f.teaid === atea.teacherid && f.day === atea.date
- );
- if (tr) continue;
- // 查看这期内,每个申请上课的教师时候超过2天(2条记录),如果超过,则不排,但是如果最后没有人了,就得硬排了
- const r = afterList.filter(
- f => f.termid === termid && f.teaid === atea.teacherid
- );
- if (r.length >= 2) {
- outTwoTimesList = [ ...outTwoTimesList, atea ];
- continue;
- } else {
- l.teaid = atea.teacherid;
- l.teaname = atea.teaname;
- break;
- }
- }
- // 检查,该天,该科的课是否有教师
- const has_teaid = _.get(l, 'teaid');
- if (!has_teaid) {
- // // 如果没有教师,就需要在outTowTimesList列表中找分最高的教师
- const list = _.orderBy(outTwoTimesList, [ 'score' ], [ 'desc' ]);
- for (const i of list) {
- const tr = afterList.find(
- f => f.teaid === i.teacherid && f.day === i.date
- );
- if (tr) continue;
- else {
- l.teaid = i.teacherid;
- l.teaname = i.teaname;
- break;
- }
- }
- }
- afterList.push(l);
- }
- // 将afterList还原回正常的termnum;
- const newTermnum = this.returnTermnum(afterList, termnum);
- // 保存至计划
- trainplan.termnum = newTermnum;
- await trainplan.save();
- }
- // 确认计划安排
- async arrangeConfirm({ planid, ids, classtype }) {
- console.log(planid, ids, classtype);
- const trainplan = await this.trainmodel.findById(planid);
- if (!trainplan) {
- throw new BusinessError(ErrorCode.DATA_EXISTED, '年度计划不存在');
- }
- const plan = _.cloneDeep(trainplan);
- let { termnum } = plan;
- if (!termnum) return;
- termnum = JSON.parse(JSON.stringify(termnum));
- // 过滤出确认的期,TODO:没有做通知
- // termnum = termnum.filter(f => );
- // 找到每个教师的位置,然后把状态(status)改成1=>已确认
- for (const t of termnum) {
- if (!ids.includes(t._id)) continue;
- const { term } = t;
- if (!(t.batchnum && _.isArray(t.batchnum))) continue;
- for (const b of t.batchnum) {
- const { batch } = b;
- if (!(b.class && _.isArray(b.class))) continue;
- for (const c of b.class) {
- // 检查是否要求班级类型
- if (classtype) {
- // 获取这个班级的班级类型
- const { type } = c;
- // 判断班级类型与要求的符不符合,不符合就跳过不改
- if (`${type}` !== `${classtype}`) continue;
- }
- if (!(c.lessons && _.isArray(c.lessons))) continue;
- for (const l of c.lessons) {
- l.status = '1';
- }
- }
- }
- }
- // console.log(termnum);
- trainplan.termnum = termnum;
- await trainplan.save();
- }
- /**
- * 拍平了的课表=>termnum
- * @param {Array} list 拍平了的课表,详情参考页面的初步课表的数据
- * @param {Array} termnum 原termnum
- */
- returnTermnum(list, termnum) {
- let newTermnum = [];
- for (const l of list) {
- const { termid, batchid, classid, ...info } = l;
- const updata = _.pick(info, [
- 'day',
- 'subid',
- 'subname',
- 'teaid',
- 'teaname',
- 'time',
- 'status',
- ]);
- newTermnum = termnum.map(t => {
- // 找到期
- if (termid === t._id) {
- t.batchnum = t.batchnum.map(b => {
- if (batchid === b._id) {
- // 找到批次
- b.class = b.class.map(c => {
- if (classid === c._id) {
- if (c.lessons) {
- // 说明有课程安排,找有没有重复的,没有就推进去,有就更改,subid查
- const r = c.lessons.find(f => f.subid === updata.subid);
- if (r) {
- const rindex = c.lessons.findIndex(
- f => f.subid === updata.subid
- );
- c.lessons[rindex] = updata;
- } else {
- c.lessons.push(updata);
- }
- } else {
- // 说明没有课程安排,放进去一条保存
- c.lessons = [ updata ];
- }
- }
- return c;
- });
- }
- return b;
- });
- }
- return t;
- });
- }
- return newTermnum;
- }
- /**
- * 将课表拍平了,从多维=>一维
- * @param {Array} termnum 计划的termnum
- */
- setLessonList(termnum) {
- let arr = [];
- for (const t of termnum) {
- const { batchnum, term, _id: termid } = t;
- // 班级和课程一一匹
- for (const b of batchnum) {
- const { class: classes, lessons, _id: batchid } = b;
- const claslesList = this.setList(
- term * 1,
- termid,
- batchid,
- classes,
- lessons
- );
- arr.push(...claslesList);
- }
- }
- arr = _.orderBy(arr, [ 'term', 'day' ], [ 'asc', 'asc' ]);
- return arr;
- }
- /**
- * 将课表模板和班级整理成一维数组
- * @param {String} term 期数
- * @param {String} termid 期id
- * @param {String} batchid 批id
- * @param {Array} classes 班级列表
- * @param {Array} lessonTemplate 课表模板
- */
- setList(term, termid, batchid, classes, lessonTemplate) {
- const arr = [];
- // 班级和课程匹配
- for (const cla of classes) {
- let { lessons } = cla;
- if (!lessons) lessons = lessonTemplate;
- for (const i of lessons) {
- let nobj = {};
- nobj.term = term;
- nobj.termid = termid;
- nobj.batchid = batchid;
- nobj.type = cla.type;
- const obj = _.omit(cla, [ 'lessons' ]);
- nobj.classid = _.clone(cla._id);
- nobj = _.assign(nobj, obj);
- nobj = _.assign(nobj, i);
- arr.push(nobj);
- }
- }
- return arr;
- }
- /**
- * 发送消息
- * @param {Object} param planid:年度计划id,ids,发送的期列表;classtype:发送班级类型 undefined 都发,有的话就找指定班级类型发
- */
- async arrangeSendMsg({ planid, ids, classtype }) {
- const trainplan = await this.trainmodel.findById(planid);
- if (!trainplan) {
- throw new BusinessError(ErrorCode.DATA_EXISTED, '年度计划不存在');
- }
- // 大批次id,年度计划id
- const plan = _.cloneDeep(trainplan);
- let { termnum, planyearid } = plan;
- if (!termnum) return;
- termnum = JSON.parse(JSON.stringify(termnum));
- // 整理出课表
- let arr = this.setLessonList(termnum);
- // 过滤出需要发送的教师
- arr = arr.filter(f => ids.find(id => f.termid === id) && f.teaid);
- // && f.status !== '1'
- // 整理出要发送的教师列表
- let teaids = arr.map(i => i.teaid);
- teaids = _.uniq(teaids);
- // 找到教师信息
- let teaList = await this.tmodel.find({ _id: teaids });
- // 找到教师用户信息
- let teauserList = await this.umodel.find({ uid: teaids });
- if (teaList) teaList = JSON.parse(JSON.stringify(teaList));
- if (teauserList)teauserList = JSON.parse(JSON.stringify(teauserList));
- // 发送,此处是根据安排,给教师发.还有一种方案是根据教师,整理安排一起发送
- // 查询是否发送过这期的通知
- // 排序
- arr = _.orderBy(arr, [ 'day' ], [ 'asc' ]);
- for (const l of arr) {
- // 教师id,期数,班级名,上课的日期,课程名
- const { teaid, term, name, day, subname, termid, classid, type, status } = l;
- // 已确认的教师不发信息
- if (status === '1') continue;
- // 判断发送的班级类型
- if (!(classtype && classtype === type)) continue;
- const tea = teaList.find(f => f._id === teaid);
- const teauser = teauserList.find(f => f.uid === teaid);
- // 文案
- let msg = `${_.get(tea, 'name', '')}老师您好:
- 吉林省高等学校毕业生就业指导中心-双困生培训系统提醒您:
- ${term}期-${name.includes('班') ? name : `${name}班`}
- ${day}(星期${this.dayList[moment(day).days()]})
- 有您的课程安排:${subname}`;
- msg = `${msg}\n 如果您无法进行授课,请及时联系中心负责人`;
- const { openid } = teauser;
- let tourl;
- let to_send = false;
- if (openid) {
- let notice = await this.nmodel.findOne({ planid, termid, classid, type: '6' });
- // 找下是否发过信息
- if (notice) {
- // 发过信息,找有没有这个教师
- const { notified } = notice;
- if (_.isArray(notified)) {
- const has_notice = notified.find(f => f.notifiedid === teaid);
- if (has_notice) {
- const { status } = has_notice;
- if (status !== '1') to_send = true;
- } else {
- const obj = { notifiedid: teaid, username: _.get(tea, 'name', ''), content: msg };
- notice.notified.push(obj);
- await notice.save();
- to_send = true;
- }
- }
- } else {
- const notified = [{ notifiedid: teaid, username: _.get(tea, 'name', ''), content: msg }];
- const noticeObj = { planyearid, planid, termid, classid, noticeid: 'system', type: '6', content: `${term}期-${name.includes('班') ? name : `${name}班`}教师计划初步信息确认`, notified };
- await this.nmodel.create(noticeObj);
- notice = await this.nmodel.findOne({ planid, termid, classid, type: '6' });
- to_send = true;
- }
- tourl = this.ctx.app.config.baseUrl + '/msgconfirm/?userid=' + teaid + '¬iceid=' + notice._id;
- }
- if (to_send) {
- // 邮箱与微信都发送
- const { email } = tea;
- if (email) {
- this.toSendEmail(email, msg, tea.name);
- }
- if (openid) {
- this.toSendWxMsg(openid, msg, tea.name, tourl);
- }
- }
- }
- }
- /**
- * 计划-教师初步课表发送邮件
- * @param {String} email 邮件
- * @param {String} content 内容
- * @param {String} teaname 教师姓名
- */
- async toSendEmail(email, content, teaname) {
- if (!email) {
- console.error(`计划教师发送通知:${teaname}没有email`);
- return;
- }
- const subject = '吉林省高等学校毕业生就业指导中心通知(系统邮件,请勿回复)'; //
- this.ctx.service.util.sendMail(email, subject, content);
- }
- /**
- * 计划-教师初步课表发送微信推送
- * @param {String} openid 微信公众号的openid
- * @param {String} content 内容
- * @param {String} teaname 教师姓名
- * @param {String} tourl 确认地址
- */
- async toSendWxMsg(openid, content, teaname, tourl) {
- if (!openid) {
- console.error(`计划教师发送微信推送:${teaname}没有openid`);
- return;
- }
- // TODO or notTODO 发送微信推送记录
- await this.ctx.service.weixin.sendTemplateDesign(
- this.ctx.app.config.REVIEW_TEMPLATE_ID,
- openid,
- '您有一个新的通知',
- '您有新的安排',
- content,
- '感谢您的使用',
- tourl
- );
- }
- }
- module.exports = ApplyService;
|