lrf402788946 4 years ago
parent
commit
4a8e54063a
6 changed files with 436 additions and 369 deletions
  1. 4 4
      app/controller/student.js
  2. 1 1
      app/controller/util.js
  3. 55 0
      app/model/dstudent.js
  4. 27 0
      app/service/apply.js
  5. 280 313
      app/service/school.js
  6. 69 51
      app/service/student.js

+ 4 - 4
app/controller/student.js

@@ -12,10 +12,10 @@ class StudentController extends Controller {
     this.service = this.ctx.service.student;
   }
 
-  async index() {
-    const data = await this.service.query(this.ctx.query);
-    this.ctx.ok({ ...data });
-  }
+  // async index() {
+  //   const data = await this.service.query(this.ctx.query);
+  //   this.ctx.ok({ ...data });
+  // }
 
   // GET
   // 查询

+ 1 - 1
app/controller/util.js

@@ -9,7 +9,7 @@ class UtilController extends Controller {
 
   async utilMethod() {
     const res = await this.service.utilMethod(this.ctx.query, this.ctx.request.body);
-    this.ctx.ok({ res });
+    this.ctx.ok({ data: res });
   }
 }
 module.exports = UtilController;

+ 55 - 0
app/model/dstudent.js

@@ -0,0 +1,55 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose/lib/model/meta-plugin');
+
+// 学生表
+const StudentSchema = {
+  name: { type: String, required: true, maxLength: 200, zh: '姓名' }, // 姓名
+  number: { type: String, required: false, maxLength: 200, zh: '序号' }, // 序号
+  id_number: { type: String, required: true, maxLength: 200, zh: '身份证号' }, // 身份证号
+  phone: { type: String, required: true, maxLength: 200, zh: '手机号' }, // 手机号
+  gender: { type: String, required: false, maxLength: 200, zh: '性别' }, // 性别
+  nation: { type: String, required: false, maxLength: 200, zh: '民族' }, // 民族
+  insurance: { type: String, required: false, maxLength: 200, zh: '拓训时间' }, // 拓训时间
+  school_name: { type: String, required: false, maxLength: 200, zh: '学校名称' }, // 学校名称
+  isComming: { type: String, default: '0', zh: '是否参培' }, // 是否签到0否,1是
+  faculty: { type: String, required: false, maxLength: 200, zh: '院系' }, // 院系
+  major: { type: String, required: false, maxLength: 200, zh: '专业' }, // 专业
+  edua_level: { type: String, required: false, maxLength: 200, zh: '学历层次' }, // 学历层次
+  edua_system: { type: String, required: false, maxLength: 200, zh: '学制' }, // 学制
+  entry_year: { type: String, required: false, maxLength: 200, zh: '入学年份' }, // 入学年份
+  finish_year: { type: String, required: false, maxLength: 200, zh: '毕业年份' }, // 毕业年份
+  school_job: { type: String, required: false, maxLength: 200, zh: '在校担任何种职务' }, // 在校担任何种职务
+  bedroom: { type: String, required: false, maxLength: 200, zh: '寝室号' }, // 寝室号
+  is_fine: { type: String, required: false, maxLength: 200, default: '0', zh: '是否优秀' }, // 是否优秀,0-否,1-是,2-无资格
+  selfscore: { type: String, required: false, maxLength: 200, zh: '个人分' }, // 个人分
+  score: { type: String, required: false, maxLength: 200, zh: '总分' }, // 总分
+  qq: { type: String, required: false, maxLength: 200, zh: 'QQ号' }, // QQ号
+  email: { type: String, required: false, maxLength: 200, zh: '邮箱' }, // 邮箱
+  family_place: { type: String, required: false, maxLength: 200, zh: '家庭所在地' }, // 家庭所在地
+  family_is_hard: { type: String, required: false, maxLength: 200, zh: '家庭是否困难' }, // 家庭是否困难,0-否,1-是
+  have_grant: { type: String, required: false, maxLength: 200, zh: '是否获得过助学金' }, // 是否获得过助学金,0-否,1-是
+  job: { type: String, required: false, maxLength: 200, default: '普通学生', zh: '职务' }, // 职务
+  schid: { type: String, required: false, maxLength: 200, zh: '学校id' }, // 学校id
+  planyearid: { type: String, required: false, maxLength: 200, zh: '大批次id' }, // 大批次id
+  planid: { type: String, required: false, maxLength: 200 }, // 计划id
+  termid: { type: String, required: false, maxLength: 200, zh: '期' }, // 期id
+  batchid: { type: String, required: false, maxLength: 200, zh: '批次' }, // 批次id
+  classid: { type: String, required: false, maxLength: 200, zh: '班级' }, // 班级id
+  bedroomid: { type: String, required: false, maxLength: 200, zh: '寝室id' }, // 寝室id
+  openid: { type: String, required: false, maxLength: 200, zh: '微信openid' }, // 微信openid
+  diy: { type: { Object }, required: false, zh: '自定义' }, // 自定义
+  type: { type: String, required: false, maxLength: 200, default: '0' }, // 类型:0-正常,1-特殊
+  status: { type: String, required: false, maxLength: 200, default: '1' }, // 0:待确定。1:确定,2:失败
+  cert: { type: String, required: false, default: '0' }, // 证书状态,0=>未打印;1=已打印
+};
+
+
+const schema = new Schema(StudentSchema, { toJSON: { virtuals: true } });
+schema.index({ id: 1, termid: 1, batchid: 1, schid: 1 });
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Dstudent', schema, 'dstudent');
+};

+ 27 - 0
app/service/apply.js

@@ -605,6 +605,33 @@ class ApplyService extends CrudService {
       tourl
     );
   }
+
+  async repealConfirm({ planid, ids }) {
+    // 将指定计划,期数的教师状态解除确认
+    let trainPlan = await this.ctx.model.Trainplan.findById(planid);
+    trainPlan = JSON.parse(JSON.stringify(trainPlan));
+    let terms = {};
+    for (const term of trainPlan.termnum) {
+      const in_ids = ids.find(f => ObjectId(f).equals(term._id));
+      if (in_ids) {
+        for (const batch of term.batchnum) {
+          for (const c of batch.class) {
+            for (const l of c.lessons) {
+              l.status = '0';
+            }
+          }
+        }
+        terms = term;
+      }
+    }
+    // const i = trainPlan.termnum.findIndex(f => ObjectId('5f5aed5e69b4221aedaa5005').equals(f._id));
+    // trainPlan.termnum[i] = terms;
+    delete trainPlan.meta;
+    const r = await this.ctx.model.Trainplan.update(
+      { _id: ObjectId('5f5adb337ceb003386c9b0d4') },
+      { ...trainPlan }
+    );
+  }
 }
 
 module.exports = ApplyService;

+ 280 - 313
app/service/school.js

@@ -8,6 +8,7 @@ const { CrudService } = require('naf-framework-mongoose/lib/service');
 const { BusinessError, ErrorCode } = require('naf-core').Error;
 const moment = require('moment');
 const XLSX = require('xlsx');
+const Excel = require('exceljs');
 
 class SchoolService extends CrudService {
   constructor(ctx) {
@@ -48,336 +49,319 @@ class SchoolService extends CrudService {
     if (!plan) {
       throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '计划信息不存在');
     }
-    // const isOutOfDate = this.outOfDate(plan, termid, batchid);
-    // if (!isOutOfDate) {
-    //   throw new BusinessError(
-    //     ErrorCode.BUSINESS,
-    //     '已经超过上报时间,不允许上报名单'
-    //   );
-    // }
-    // 取得学校预计人数
-    const num_ = await this.getschnum(plan, type, schid, termid, batchid);
-    // console.log('*******************');
-    // console.log(num_);
-    // console.log('*******************');
-    // 检查学校是否上传过学生
-    const schstu = await this.smodel.count({ schid, batchid });
-    if (schstu && schstu > 0) throw new BusinessError(ErrorCode.BUSINESS, '该批次已经上传过学生,无需重复上传,若人员有变化,请联系中心负责人');
+    const term = plan.termnum.id(termid);
     const planid = plan.id;
     const planyearid = plan.planyearid;
-    // 取得excle中数据
-    const _filepath = 'http://127.0.0.1' + filepath; // this.ctx.app.config.baseUrl http://127.0.0.1 http://jytz.jilinjobs.cn
-    const studatas = await this.getImportXLSXData(
-      _filepath,
-      termid,
-      schid,
-      planid,
-      planyearid,
-      type,
-      batchid
-    );
-    // 将得到的数据校验
-    const datacheck = await this.datacheck(studatas);
-    if (datacheck.errorcode === '1') {
-      return datacheck;
-    }
-    const school_ = await this.model.findOne({ code: schid });
-    let schname = '';
-    if (school_) {
-      schname = school_.name;
+    // 检查这个范围的学生是否存在,存在的话是否更改过(classid,bedroomid这两项存不存在可以放过,但凡有一个人,就不行了)
+    let dbStuList = await this.ctx.model.Student.find({ termid, batchid, schid });
+    if (dbStuList.length > 0) {
+      // 查这个学校的这期学生是否修改过班级 或 寝室
+      const is_change = dbStuList.find(f => f.classid || f.bedroomid);
+      if (is_change) throw new BusinessError(ErrorCode.BUSINESS, '上报过的学生已经安排班级或寝室!若需要替换学生,让同性别的学生直接来和班主任说,修改信息即可.若还是有疑问,请和中心负责人联系(最好联系下)');
     }
-    const trem_ = await plan.termnum.id(termid);
-    if (!trem_) {
-      throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '期信息不存在');
+    // 获取学校名称
+    let school_name;
+    const sch = await this.ctx.model.School.findOne({ code: schid });
+    if (sch) school_name = sch.name;
+    const fullUrl = 'http://127.0.0.1' + filepath; // this.ctx.app.config.baseUrl http://127.0.0.1 http://jytz.jilinjobs.cn
+    let studentList = await this.getDataFromExcel(fullUrl);
+    const checkRes = await this.checkData(studentList);
+    const { errorcode } = checkRes;
+    if (errorcode === '1') {
+      return checkRes;
     }
-    const nowtime = moment().locale('zh-cn').format('YYYY-MM-DD HH:mm:ss');
-    if (studatas.length > num_) {
-      const jobdata = {
-        code: schid,
-        name: schname,
-        planid: plan.id,
-        termid,
-        term: trem_.term,
-        batchid,
-        filepath,
-        studs: JSON.stringify(studatas),
-        plannum: num_,
-        schnum: studatas.length,
-        isstore: '0',
-        createtime: nowtime,
-        type,
-        reason: '学校上传人数超过预期人数,请联系中心管理员',
-      };
-      await this.jmodel.create(jobdata);
-      throw new BusinessError(
-        ErrorCode.SERVICE_FAULT,
-        '学校上传人数超过预期人数,请联系中心管理员'
-      );
-    } else if (studatas.length < num_) {
-      const jobdata = {
-        code: schid,
-        name: schname,
-        planid: plan.id,
-        termid,
-        term: trem_.term,
-        batchid,
-        filepath,
-        studs: JSON.stringify(studatas),
-        plannum: num_,
-        schnum: studatas.length,
-        isstore: '0',
-        createtime: nowtime,
-        type,
-        reason: '学校上传人数少于预期人数,请联系中心管理员',
-      };
-      await this.jmodel.create(jobdata);
-      throw new BusinessError(
-        ErrorCode.SERVICE_FAULT,
-        '学校上传人数少于预期人数,请联系中心管理员'
-      );
-    }
-    // 将数据存入数据库中
-    for (const stu of studatas) {
-      const res = await this.smodel.create(stu);
-      // if (res) {
-      //   const newdata = { name: stu.name, mobile: stu.phone, type: '4', uid: res.id };
-      //   newdata.passwd = { secret: '12345678' };
-      //   await this.umodel.create(newdata);
-      // }
+    // 整理数据
+    studentList = this.lastSetData(studentList, {
+      planyearid,
+      planid,
+      batchid,
+      termid,
+      type,
+      schid,
+      school_name,
+    });
+    const num = await this.getschnum(plan, schid, batchid);
+    // 查看要求人数和整理完最后的人数能不能对上
+    if (studentList.length !== num) {
+      const res = await this.jmodel.findOne({ code: schid, batchid });
+      const reason = `学校上传人数${studentList.length > num ? '多于' : '少于'}预期人数,请联系中心管理员`;
+      if (res) {
+        res.reason = reason;
+        res.filepath = filepath;
+        await res.save();
+      } else {
+        const job = {
+          code: schid,
+          name: school_name,
+          planid,
+          termid,
+          term: term.term,
+          batchid,
+          filepath,
+          studs: JSON.stringify(studentList),
+          plannum: num,
+          schnum: studentList.length,
+          isstore: '0',
+          createtime: moment().format('YYYY-MM-DD HH:SS:mm'),
+          type,
+        };
+        job.reason = reason;
+        await this.jmodel.save(job);
+      }
+
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, reason);
+    } else {
+      // 复制,删除,添加
+      dbStuList = JSON.parse(JSON.stringify(dbStuList));
+      dbStuList = dbStuList.map(i => {
+        delete i.meta;
+        i.studentid = _.clone(i._id);
+        delete i.id;
+        delete i._id;
+        return i;
+      });
+      await this.ctx.model.Dstudent.insertMany(dbStuList);
+      await this.smodel.deleteMany({ termid, batchid, schid });
+      await this.smodel.insertMany(studentList);
     }
-    return datacheck;
   }
 
   // 取得学校预计人数
-  async getschnum(plan, type, schid, termid, batchid) {
+  async getschnum(plan, schid, batchid) {
     const schtime = await this.schmodel.findOne({ schid, planid: plan.id });
     const { arrange } = schtime;
     const r = arrange.find(f => f.batchid === batchid);
     if (!r) { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '没有找到该学校的计划'); }
     const { number } = r;
     return parseInt(number);
-    // const { termnum } = plan;
-    // arrange = _.groupBy(arrange, 'termid');
-    // const keys = Object.keys(arrange);
-    // let arr = keys.map(key => {
-    //   const rt = termnum.find(f => ObjectId(key).equals(f._id));
-    //   let ar = arrange[key];
-    //   ar = ar.map(a => {
-    //     const rb = rt.batchnum.find(f => ObjectId(a.batchid).equals(f._id));
-    //     if (rb) {
-    //       const bh = _.head(rb.class);
-    //       const { type } = bh;
-    //       a.type = type;
-    //       return a;
-    //     }
-    //   });
-    //   let garr = _.groupBy(ar, 'type');
-    //   const gks = Object.keys(garr);
-    //   garr = gks.map(gk => {
-    //     const { term, termid } = _.head(garr[gk]);
-    //     const number = garr[gk].reduce((p, n) => p + n.number * 1, 0);
-    //     return { term, termid, number, type: gk };
-    //   });
-    //   return garr;
-    // });
-    // arr = arr.flat();
-    // const obj_ = _.find(arr, { termid, type });
-    return obj_.number;
   }
-
-  // 获取导入的XLSX文件中的数据
-  async getImportXLSXData(
-    filepath,
-    termid,
-    schid,
-    planid,
-    planyearid,
-    type,
-    batchid
-  ) {
-    const file = await this.ctx.curl(filepath);
-    const workbook = XLSX.read(file.data);
-    // 读取内容
-    let exceldata = [];
-    const sheetNames = workbook.SheetNames; // 获取表名
-    const sheet = workbook.Sheets[sheetNames[0]]; // 通过表名得到表对象
-
-    // 遍历26个字母
-    const theadRule = [];
-    const range = XLSX.utils.decode_range(sheet['!ref']);
-    const col_start = range.s.c;
-    const col_end = range.e.c;
-    for (let i = col_start; i <= col_end; i++) {
-      const addr = XLSX.utils.encode_col(i) + XLSX.utils.encode_row(0);
-      theadRule.push(sheet[addr].v);
+  // 整理excel数据
+  async getDataFromExcel(url) {
+    const file = await this.ctx.curl(`http://127.0.0.1${url}`);
+    if (!(file && file.data)) {
+      throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到上传的名单');
     }
-    // const theadRule = [ sheet.A1.v, sheet.B1.v, sheet.C1.v, sheet.D1.v, sheet.E1.v, sheet.F1.v, sheet.G1.v, sheet.H1.v, sheet.I1.v, sheet.J1.v, sheet.K1.v, sheet.L1.v, sheet.M1.v, sheet.N1.v, sheet.O1.v, sheet.P1.v, sheet.Q1.v, sheet.R1.v ];
-    const params = XLSX.utils.sheet_to_json(sheet); // 通过工具将表对象的数据读出来并转成json
-    // const theadRule = [ '序号', '姓名', '性别', '民族', '身份证号', '学校名称', '院系', '专业', '入学年份', '毕业年份', '在校曾担任何种职务', '手机号', 'QQ号', '家庭所在地', '家庭是否困难', '是否获得过助学金' ];
-    if (!params) return [];
-    let i = 0;
-    const length = params.length;
-    const _datas = [];
-    let data = {};
-    for (i; i < length; i++) {
-      data = params[i];
-      const diy_ = [];
-      if (theadRule.length > 18) {
-        for (let j = 18; j < theadRule.length; j++) {
-          const newdata = {
-            itemname: theadRule[j],
-            itemvalue: data[theadRule[j]],
-          };
-          diy_.push(newdata);
-        }
-      }
-      _datas.push({
-        name: _.trim(data[theadRule[1]]),
-        gender: _.trim(data[theadRule[2]]),
-        nation: _.trim(data[theadRule[3]]),
-        id_number: _.trim(data[theadRule[4]]),
-        school_name: _.trim(data[theadRule[5]]),
-        faculty: _.trim(data[theadRule[6]]),
-        major: _.trim(data[theadRule[7]]),
-        entry_year: _.trim(data[theadRule[8]]),
-        finish_year: _.trim(data[theadRule[9]]),
-        school_job: _.trim(data[theadRule[10]]),
-        phone: _.trim(data[theadRule[11]]),
-        qq: _.trim(data[theadRule[12]]),
-        family_place: _.trim(data[theadRule[13]]),
-        family_is_hard: _.trim(data[theadRule[14]]),
-        have_grant: _.trim(data[theadRule[15]]),
-        edua_level: _.trim(data[theadRule[16]]),
-        edua_system: _.trim(data[theadRule[17]]),
-        diy: diy_,
-        termid,
-        batchid,
-        schid,
-        planid,
-        planyearid,
-        type,
-      });
+    const workbook = new Excel.Workbook();
+    await workbook.xlsx.load(file);
+    const worksheet = workbook.getWorksheet(1);
+    if (!worksheet) {
+      throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未发现excel中有工作表');
     }
-    exceldata = [ ...exceldata, ..._datas ];
+    const cols = this.getStucolumn();
+    const headRow = worksheet.getRow(1);
+    headRow.eachCell((cell, coli) => {
+      const r = cols.find(f => f.key === cell.value);
+      if (r) {
+        const ri = cols.findIndex(f => f.key === cell.value);
+        r.colIndex = coli;
+        cols[ri] = r;
+      }
+    });
+    const excelIsRigth = cols.find(f => f.colIndex);
+    if (!excelIsRigth) throw new BusinessError(ErrorCode.DATA_INVALID, 'Excel表格格式不正确,请使用系统提供的模板!');
+    // 删除掉第一行
+    worksheet.spliceRows(0, 1);
+    const stuList = [];
+    // 整理数据
+    worksheet.eachRow(row => {
+      const stu = {};
+      for (let i = 0; i < cols.length; i++) {
+        const col = cols[i];
+        if (!col) break;
+        let val = _.trim(row.getCell(col.colIndex));
+        if (col.column === 'id_number') val = val.toUpperCase();
+        stu[col.column] = val;
+      }
+      stuList.push(stu);
+    });
+    return stuList;
 
-    return exceldata;
   }
-
-  // 获取导入的XLSX文件中的数据
-  async datacheck(studatas) {
+  // 数据校验
+  async checkData(stuList) {
+    const cols = this.getStucolumn();
     let errorcode = '0';
     const errormsg = [];
-    for (const data of studatas) {
-      // 判断是否为空
-      if (!data.name) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '姓名不允许为空;';
-      }
-      if (!data.gender) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '性别不允许为空;';
-      }
-      // 判断性别是否是1个字
-      if (data.gender.length !== 1) {
-        errorcode = '1';
-        data.msg(data.msg || '') + '性别内容超出1个字.可能含有空格;';
-      }
-      if (!data.nation) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '民族不允许为空;';
-      }
-      if (!data.id_number) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '身份证号不允许为空;';
-      } else {
-        const res = await this.smodel.findOne({ id_number: data.id_number });
-        if (res) {
-          errorcode = '1';
-          data.msg = (data.msg || '') + '学生已经存在请检查;';
-        } else {
-          const { pass, msg } = this.idCodeValid(data.id_number);
+    for (const stu of stuList) {
+      const { name } = stu;
+      let error = false;
+      let msg = '';
+      // 各个字段检查,最低为非空检查
+      for (const col of cols) {
+        const { key, column } = col;
+        if (!column) throw new BusinessError(ErrorCode.SERVICE_FAULT, '未找到导出的字段名');
+        const val = _.get(stu, column);
+        // 空校验
+        if (!val || val === '') {
+          error = true;
+          msg = `${msg}"${key}"不能为空;`;
+          continue;
+        }
+        // 性别校验
+        if (column === 'gender') {
+          if (!(val.includes('男') || val.includes('女'))) {
+            error = true;
+            msg = `${msg}性别错误;`;
+          }
+          continue;
+        }
+        // 身份证号校验
+        if (column === 'id_number') {
+          // 因为删除再添加的流程导致此处 不能 校验数据库中是否有这个身份证号
+          // const res = await this.ctx.model.Student.findOne({ id_number: val });
+          // if (!res) {
+          const { pass, msg: idmsg } = this.ctx.service.school.idCodeValid(val);
           if (!pass) {
-            errorcode = '1';
-            data.msg = (data.msg || '') + `${msg},`;
+            error = true;
+            msg = `${msg}${idmsg};`;
           }
+          // } else {
+          //   error = true;
+          //   msg = `${msg}学生已存在`;
+          // }
+          const have_same = stuList.filter(f => f.id_number === val && f.name !== name);
+          if (have_same.length > 0) {
+            error = true;
+            const h = _.head(have_same);
+            const num = have_same.length;
+            if (num === 1) {
+              msg = `${msg}身份证号与本次名单的"${h.name}"重复;`;
+            } else msg = `${msg}身份证号与本次名单中"${h.name}"等${num}人重复;`;
+          }
+          continue;
         }
-
-      }
-      if (!data.school_name) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '学校名称不允许为空;';
-      }
-      if (!data.phone) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '手机号不允许为空;';
-      }
-      if (!data.faculty) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '院系不允许为空;';
-      }
-      if (!data.major) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '专业不允许为空;';
-      } else {
-        // 限制专业字段中不能含有 '专业' 字样
-        if (data.major.includes('专业')) {
-          errorcode = '1';
-          data.msg = (data.msg || '') + '专业列不能含有"专业"二字;';
+        // 手机号校验
+        if (column === 'phone') {
+          // 因为删除再添加的流程导致此处 不能 校验数据库中是否有这个手机号
+          // const res = await this.ctx.model.Student.findOne({ phone: val });
+          // if (!res) {
+          if (!/^\d{11}$/i.test(val)) {
+            error = true;
+            msg = `${msg}手机号位数不正确;`;
+          }
+          // } else {
+          //   error = true;
+          //   msg = `${msg}学生库中已有该手机号,请检查手机号是否正确,若无误,请联系中心负责人`;
+          // }
+          const have_same = stuList.filter(f => f.phone === val && f.name !== name);
+          if (have_same.length > 0) {
+            error = true;
+            const h = _.head(have_same);
+            const num = have_same.length;
+            if (num === 1) {
+              msg = `${msg}手机号与本次名单的"${h.name}"重复;`;
+            } else msg = `${msg}手机号与本次名单中"${h.name}"等${num}人重复;`;
+          }
+          continue;
         }
-      }
-
-      if (!data.entry_year) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '入学年份不允许为空;';
-      } else {
-        const m = /^\w{4}$/;
-        if (!data.entry_year.match(m)) {
-          errorcode = '1';
-          data.msg = (data.msg || '') + '入学年份格式不正确,只填写4位数字即可;';
+        // 专业校验
+        if (column === 'major') {
+          if (val.includes('专业')) {
+            error = true;
+            msg = `${msg}专业列不能含有"专业"二字;`;
+          }
+          continue;
         }
-      }
-      // 限制是4位数字
-      if (!data.finish_year) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '毕业年份不允许为空;';
-      } else {
-        const m = /^\w{4}$/;
-        if (!data.finish_year.match(m)) {
-          errorcode = '1';
-          data.msg = (data.msg || '') + '毕业年份格式不正确,只填写4位数字即可;';
+        // 入学年份
+        if (column === 'entry_year') {
+          const m = /^\w{4}$/;
+          if (!val.match(m)) {
+            error = true;
+            msg = `${msg}入学年份格式不正确,只填写4位数字;`;
+          }
+          continue;
+        }
+        // 毕业年份
+        if (column === 'finish_year') {
+          const m = /^\w{4}$/;
+          if (!val.match(m)) {
+            error = true;
+            msg = `${msg}毕业年份格式不正确,只填写4位数字;`;
+          }
+          continue;
+        }
+        // 双困检查
+        if (column === 'family_is_hard') {
+          if (!(val.includes('是') || val.includes('否'))) {
+            error = true;
+            msg = `${msg}家庭是否困难填写"是"或"否";`;
+          }
+          continue;
+        }
+        if (column === 'have_grant') {
+          if (!(val.includes('是') || val.includes('否'))) {
+            error = true;
+            msg = `${msg}是否获得过助学金填写"是"或"否";`;
+          }
+          continue;
         }
       }
-      if (!data.school_job) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '职务不允许为空;';
-      }
-      if (!data.qq) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + 'QQ号不允许为空;';
-      }
-      if (!data.family_place) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '家庭所在地不允许为空;';
-      }
-      if (!data.family_is_hard) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '家庭是否困难不允许为空;';
-      }
-      if (!data.have_grant) {
-        errorcode = '1';
-        data.msg = (data.msg || '') + '是否获得过助学金不允许为空;';
-      }
-      if (!/^\d{11}$/i.test(data.phone)) {
+      if (error) {
         errorcode = '1';
-        data.msg = (data.msg || '') + '手机号不正确;';
-      }
-      if (errorcode === '1') {
-        errormsg.push(data);
+        stu.msg = msg;
+        errormsg.push(stu);
       }
     }
     return { errorcode, errormsg };
   }
 
+  // 最后整合数据
+  lastSetData(stuList, data) {
+    const cols = this.getStucolumn();
+    const needChange = cols.filter(f => f.change);
+    stuList = stuList.map(i => {
+      const d = { ...i, ...data };
+      for (const col of needChange) {
+        const { column, change } = col;
+        if (!column && change && _.isArray(change)) continue;
+        const val = _.get(d, column);
+        if (!val) continue;
+        const r = change.find(f => f.key === val);
+        if (!r) continue;
+        const { value } = r;
+        d[column] = value;
+      }
+      return d;
+    });
+    return stuList;
+  }
+  // excel中学生字段
+  getStucolumn() {
+    const arr = [
+      { key: '姓名', column: 'name' },
+      { key: '性别', column: 'gender' },
+      { key: '民族', column: 'nation' },
+      { key: '身份证号', column: 'id_number' },
+      { key: '学校名称', column: 'school_name' },
+      { key: '院(系)', column: 'faculty' },
+      { key: '专业', column: 'major' },
+      { key: '入学年份', column: 'entry_year' },
+      { key: '毕业年份', column: 'finish_year' },
+      { key: '在校曾担任何种职务', column: 'school_job' },
+      { key: '手机号', column: 'phone' },
+      { key: 'QQ号', column: 'qq' },
+      { key: '家庭所在地', column: 'family_place' },
+      {
+        key: '家庭是否困难',
+        column: 'family_is_hard',
+        change: [
+          { key: '否', value: '0' },
+          { key: '是', value: '1' },
+        ],
+      },
+      {
+        key: '是否获得过助学金',
+        column: 'have_grant',
+        change: [
+          { key: '否', value: '0' },
+          { key: '是', value: '1' },
+        ],
+      },
+    ];
+    return arr;
+  }
+
+
   // 导出学校名单
   async exportSchool({ trainplanId }) {
     // 批次期次都在这里面
@@ -555,23 +539,6 @@ class SchoolService extends CrudService {
     }
     return row;
   }
-  // 判断是否超出该期前3天
-  outOfDate(plan, termid, batchid) {
-    const term = plan.termnum.find(f => ObjectId(termid).equals(f._id));
-    const { batchnum } = term;
-    if (!batchnum) throw new BusinessError(ErrorCode.BUSINESS, '没有找到该期下的批次');
-    const batch = batchnum.find(f => ObjectId(batchid).equals(f._id));
-    if (!batch) throw new BusinessError(ErrorCode.BUSINESS, '没有找到该批次');
-    // let startList = batchnum.map(i => ({ start: i.startdate }));
-    // startList = _.orderBy(startList, [ 'start' ], [ 'asc' ]);
-    // const start = _.get(_.head(startList), 'start');
-    const start = _.get(batch, 'startdate');
-    if (!start) throw new BusinessError(ErrorCode.BUSINESS, '没有找到开始日期');
-    const limit = moment(start).subtract(3, 'days').format('YYYY-MM-DD');
-    const now = moment().format('YYYY-MM-DD');
-    const res = moment(now).isBefore(limit);
-    return res;
-  }
 }
 
 module.exports = SchoolService;

+ 69 - 51
app/service/student.js

@@ -43,7 +43,10 @@ class StudentService extends CrudService {
 
   async delete({ id }) {
     // 删除小组中的这个人,作业表,问卷表,评分,考勤,用户表,学生表
-    await this.gmodel.update({ 'students.stuid': id }, { $pull: { students: { stuid: id } } });
+    await this.gmodel.update(
+      { 'students.stuid': id },
+      { $pull: { students: { stuid: id } } }
+    );
     await this.upmodel.deleteMany({ studentid: id });
     await this.uqmodel.deleteMany({ studentid: id });
     await this.scoremodel.deleteMany({ stuid: id });
@@ -61,54 +64,52 @@ class StudentService extends CrudService {
   }
 
   // 查询
-  async query({ skip, limit, ...info }) {
-    const total = await this.model.count(info);
-    let res = await this.model
-      .find(info)
-      .skip(Number(skip))
-      .limit(Number(limit));
-    res = JSON.parse(JSON.stringify(res));
-    let termList = res.map(i => i.termid);
-    termList = _.compact(_.uniq(termList));
-    termList = termList.map(i => ObjectId(i));
-    const planList = await this.tmodel.find({
-      'termnum._id': { $in: termList },
-    });
-    let classid = res.map(i => i.classid);
-    classid = _.compact(_.uniq(classid));
-    classid = classid.map(i => ObjectId(i));
-    const classList = await this.clamodel.find({ _id: { $in: classid } });
-    // 整理数据
-    res = res.map(i => {
-      const { planid, termid, batchid, classid } = i;
-      // 拿出班级名称
-      const cla = classList.find(f => ObjectId(classid).equals(f._id));
-      if (cla) {
-        const { name: classname } = cla;
-        i.classname = classname;
+  async query({ ...info }, { skip, limit }) {
+    let data = await this.model
+      .find({ ...info })
+      .populate([
+        {
+          path: 'classid',
+          model: 'Class',
+          select: 'name',
+        },
+        {
+          path: 'planid',
+          model: 'Trainplan',
+        },
+      ])
+      .skip(parseInt(skip))
+      .limit(parseInt(limit));
+    data = JSON.parse(JSON.stringify(data));
+    for (const stu of data) {
+      const { classid, planid, termid, batchid } = stu;
+      // 先拿学生
+      if (classid && _.isObject(classid)) {
+        const { _id, name } = classid;
+        if (name) stu.classname = name;
+        if (_id) stu.classid = _id;
       }
-      const plan = planList.find(f => ObjectId(planid).equals(f._id));
-      if (plan) {
-        const { termnum } = plan;
+      // 拼计划信息
+      if (planid && _.isObject(planid)) {
+        const { termnum, _id } = planid;
         if (termnum && _.isArray(termnum)) {
-          const termInfo = termnum.id(termid);
-          if (termInfo) {
-            const { term, batchnum } = termInfo;
-            if (term) i.termname = term;
+          const t = termnum.find(f => f._id === termid);
+          if (t) {
+            const { batchnum, term } = t;
+            if (term) stu.termname = term;
             if (batchnum && _.isArray(batchnum)) {
-              const batchInfo = batchnum.id(batchid);
-              if (batchInfo) {
-                const { batch } = batchInfo;
-                if (batch) i.batchname = batch;
+              const b = batchnum.find(f => f._id === batchid);
+              if (b) {
+                const { batch } = b;
+                if (batch) stu.batchname = batch;
               }
             }
           }
         }
+        stu.planid = _id;
       }
-      return i;
-    });
-    const result = { total, data: res };
-    return result;
+    }
+    return data;
   }
 
   // 查询
@@ -319,7 +320,10 @@ class StudentService extends CrudService {
       studentList
     );
     studentList = studentList.map(i => {
-      i.score = _.round((i.daily || 0) + (i.task || 0) + (i.groupscore || 0), 2);
+      i.score = _.round(
+        (i.daily || 0) + (i.task || 0) + (i.groupscore || 0),
+        2
+      );
       return i;
     });
     studentList = studentList.sort(
@@ -339,7 +343,9 @@ class StudentService extends CrudService {
       let plus = 1; // 这轮有多少人,到这了,这个人肯定是要改了,所以默认1
       // 评优
       student.is_fine = '1';
-      const rlist = studentList.filter(f => f.score === score && !ObjectId(f._id).equals(_id));
+      const rlist = studentList.filter(
+        f => f.score === score && !ObjectId(f._id).equals(_id)
+      );
       // 处理同分的人也都变成is_fine
       for (const stud of rlist) {
         stud.is_fine = '1';
@@ -490,7 +496,7 @@ class StudentService extends CrudService {
     // 请求数据
     // 修改条件,termid变成数组了,需要一个一个查出来
     const { planid, termid, batchid, classid } = data;
-    const queryObject = { };
+    const queryObject = {};
     if (planid) queryObject.planid = planid;
     if (batchid) queryObject.batchid = batchid;
     if (classid) queryObject.classid = classid;
@@ -562,7 +568,11 @@ class StudentService extends CrudService {
           if (obj) {
             const { term } = obj;
             if (term) {
-              if (i === 0) { tStr += `${term}期`; } else { tStr += `,${term}期`; }
+              if (i === 0) {
+                tStr += `${term}期`;
+              } else {
+                tStr += `,${term}期`;
+              }
             }
           }
         }
@@ -579,12 +589,18 @@ class StudentService extends CrudService {
     return await this.ctx.service.util.toExcel(studentList, model, fn);
   }
 
-
   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 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, '未找到年度计划的期信息');
+    if (!termnum) {
+      throw new BusinessError(
+        ErrorCode.DATA_NOT_EXIST,
+        '未找到年度计划的期信息'
+      );
+    }
     const obj = {};
     if (termid) {
       const term = termnum.id(termid);
@@ -600,11 +616,13 @@ class StudentService extends CrudService {
 
   // 确认学生打印证书
   async printCert({ ids }) {
-    const res = await this.model.updateMany({ _id: { $in: ids.map(i => ObjectId(i)) } }, { cert: '1' });
+    const res = await this.model.updateMany(
+      { _id: { $in: ids.map(i => ObjectId(i)) } },
+      { cert: '1' }
+    );
     return res;
   }
 
-
   // 根据计划id找到学校是否上传学生
   async getSchoolStudent({ planid }) {
     const res = await this.model.aggregate([