'use strict'; const assert = require('assert'); const _ = require('lodash'); const { ObjectId } = require('mongoose').Types; const { CrudService } = require('naf-framework-mongoose/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; class QuestionnaireService extends CrudService { constructor(ctx) { super(ctx, 'qusetionnaire'); this.questionnairemodel = this.ctx.model.Questionnaire; this.questionmodel = this.ctx.model.Question; } // 插入问卷 async create(data) { const { name, num, type, question } = data; assert(name, '问卷名称不能为空'); assert(num, '问卷序号不能为空'); assert(type, '问卷类型不能为空'); const quedata = []; for (const elm of question) { const ques = await this.questionmodel.findById(elm); if (ques) { quedata.push(ques); } } data.question = quedata; return await this.questionnairemodel.create(data); } // 根据id删除问卷 async delete({ id }) { await this.questionnairemodel.findByIdAndDelete(id); return 'deleted'; } // 根据id更新问卷信息 async update({ id }, data) { const { name, num, type, question } = data; const questionnaire = await this.questionnairemodel.findById(id); if (name) { questionnaire.name = name; } if (num) { questionnaire.num = num; } if (type) { questionnaire.type = type; } if (question) { questionnaire.question = question; } return await questionnaire.save(); } // 查询 async query({ skip, limit, ...num }) { const total = await this.questionnairemodel.count(num); const data = await this.questionnairemodel .find(num) .skip(Number(skip)) .limit(Number(limit)); const result = { total, data }; return result; } // 查询详情 async show({ id }) { const questionnaire = await this.questionnairemodel.findById(id); return questionnaire; } /** * 导出问卷 * @param {Object} { range, direction, questionnaireid, modelList } 数据集合 * @property Object range 学生的查询范围 * @property String direction horizontal横向/vertical纵向 * @property String questionnaireid 问卷id * @property Array modelList 要导出的字段,学生和问卷都在这里, 学生的table:student, 问卷的table:questionnaire */ async export({ range, direction, questionnaireid, modelList }) { // 将期批班转换 modelList = modelList.map(i => { const { model } = i; if (model === 'termid') i.model = 'termname'; else if (model === 'batchid') i.model = 'batchname'; else if (model === 'classid') i.model = 'classname'; return i; }); // 获取问卷 let questionnaire = await this.questionnairemodel.findById(questionnaireid); if (!questionnaire) { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到问卷信息'); } questionnaire = JSON.parse(JSON.stringify(questionnaire)); // 修改条件,termid变成数组了,需要一个一个查出来 const { planid, termid, batchid, classid } = range; const queryObject = {}; if (planid) queryObject.planid = planid; if (batchid) queryObject.batchid = batchid; if (classid) queryObject.classid = classid; const studentList = []; const questAnswerList = []; for (const t of termid) { queryObject.termid = t; const obj = await this.toGetData(queryObject, questionnaireid); if (!obj) continue; const { studentList: stuList, questAnswerList: qaList } = obj; if (stuList) studentList.push(...stuList); if (qaList) questAnswerList.push(...qaList); } // fn,根据范围+问卷 得出文件名 const fn = await this.toSetFileName(questionnaire.name, range); let excelData; if (direction === 'horizontal') { excelData = this.horizontalSetData(studentList, questAnswerList, modelList); } else if (direction === 'vertical') { excelData = this.verticaSetData(studentList, questAnswerList, modelList); } else { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到excel的表头排列方式'); } if (excelData) { const { head, data } = excelData; return await this.ctx.service.util.toExcel(data, head, fn); } } /** * 获取学生与学生回答的问卷 * @param {Object} condition 查询学生和学生回答问卷的条件 * @param {String} questionnaireid 问卷id */ async toGetData(condition, questionnaireid) { // 获取学生 let { data: studentList } = await this.ctx.service.student.query(condition); if (studentList.length <= 0) { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到任何学生信息'); } studentList = JSON.parse(JSON.stringify(studentList)); // 再获取问卷答案 let questAnswerList = await this.ctx.model.Uploadquestion.find({ ...condition, questionnaireid, }); if (!questAnswerList) { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到任何完成的问卷'); } questAnswerList = JSON.parse(JSON.stringify(questAnswerList)); const obj = {}; if (studentList) obj.studentList = studentList; if (questAnswerList) obj.questAnswerList = questAnswerList; return obj; } /** * 获得导出文件的文件名 * @param {String} questName 问卷名 * @param {Object} range 范围(计划-期-批-班) * @return {String} fn 文件名 */ async toSetFileName(questName, range) { let fn = `-${questName}`; const { planid, termid, batchid, classid } = range; if (classid) { const cla = await this.ctx.service.class.fetch({ id: classid }); if (cla) { const { name, term } = cla; if (name) fn = `${name.includes('班') ? name : `${name}班`}${fn}`; if (term) fn = `第${term}期${fn}`; } } else if (batchid) { const tid = _.head(termid); const obj = await this.toGetFn(tid, batchid); if (obj) { const { term, batch } = obj; if (batch) fn = `第${batch}批${fn}`; if (term) fn = `第${term}期${fn}`; } } else if (termid) { if (termid.length === 1) { const obj = await this.toGetFn(_.head(termid)); if (obj) { const { term } = obj; if (term) fn = `第${term}期${fn}`; } } else { let tStr = ''; for (let i = 0; i < termid.length; i++) { const tid = termid[i]; const obj = await this.toGetFn(tid); if (obj) { const { term } = obj; if (term) { if (i === 0) { tStr += `${term}期`; } else { tStr += `,${term}期`; } } } } fn = `${tStr}${fn}`; } } else { const trainPlan = await this.ctx.model.Trainplan.findById(planid); if (trainPlan) { const { title } = trainPlan; if (title) fn = `${title}${fn}`; } } return fn; } /** * 获取文件的期/期批 * @param {String} termid 期id * @param {String} batchid 批id */ async toGetFn(termid, batchid) { const trainPlan = await this.ctx.model.Trainplan.findOne({ 'termnum._id': ObjectId(termid) }); if (!trainPlan) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划信息'); const { termnum } = trainPlan; if (!termnum) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划的期信息'); const obj = {}; if (termid) { const term = termnum.id(termid); if (term) obj.term = term.term; if (batchid) { const { batchnum } = term; const batch = batchnum.id(batchid); if (batch) obj.batch = batch.batch; } } return obj; } /** * 横向组织数据 * @param {Array} studentList 学生列表 * @param {Array} questAnswerList 学生回答问卷列表 * @param {Array} modelList 需要导出的字段 */ horizontalSetData(studentList, questAnswerList, modelList) { // 第一步,组织excel表头 const head = modelList.map(i => { const { zh, model, _id } = i; const headObj = { header: zh }; if (model) headObj.key = model; else if (_id) headObj.key = _id; headObj.width = 20; return headObj; }); // 第二步,组织数据需要按照modelList的顺序进行整理 const data = []; for (const stu of studentList) { const obj = {}; for (const m of modelList) { const { model, table, _id } = m; if (table === 'student') { const prop = _.get(stu, model, ''); obj[model] = prop; } else if (table === 'questionnaire') { const qar = questAnswerList.find(f => f.studentid === stu._id); // 没找到该学生的问卷,下一个 if (!qar) continue; const { answers } = qar; // 回答的数据不存在/不是数组,数据格式不对,下一个 if (!(answers && _.isArray(answers))) continue; const ar = answers.find(f => f.questionid === _id); // 没找到答案,下个 if (!ar) continue; const { answer } = ar; // 没有答案,下个 if (!answer) continue; obj[_id] = _.trim(answer, '"'); } } data.push(obj); } const obj = {}; if (head)obj.head = head; if (data) obj.data = data; return obj; } /** * 纵向组织数据 * @param {Array} studentList 学生列表 * @param {Array} questAnswerList 学生回答问卷列表 * @param {Array} modelList 需要导出的字段 */ verticaSetData(studentList, questAnswerList, modelList) { // 第一步,组织excel表头,纵向不需要第一行表头,开始就是数据,表头是第一列 const head = [{ key: 'c', width: 20 }]; for (let i = 1; i <= studentList.length; i++) { head.push({ key: `c${i}`, width: 20 }); } // 第二部,整理数据 const data = []; for (const m of modelList) { const { table, model, _id, zh } = m; const obj = { c: zh }; if (table === 'student') { for (let i = 0; i < studentList.length; i++) { const stu = studentList[i]; const value = _.get(stu, model, ''); if (value) obj[`c${i + 1}`] = value; } } else if (table === 'questionnaire') { for (let i = 0; i < studentList.length; i++) { const stu = studentList[i]; const qar = questAnswerList.find(f => f.studentid === stu._id); // 没找到该学生的问卷,下一个 if (!qar) continue; const { answers } = qar; // 回答的数据不存在/不是数组,数据格式不对,下一个 if (!(answers && _.isArray(answers))) continue; const ar = answers.find(f => f.questionid === _id); // 没找到答案,下个 if (!ar) continue; const { answer } = ar; // 没有答案,下个 if (!answer) continue; obj[`c${i + 1}`] = _.trim(answer, '"'); } } data.push(obj); } const obj = {}; if (head) obj.head = head; if (data) obj.data = data; return obj; } } module.exports = QuestionnaireService;