lrf 1 anno fa
parent
commit
4dd9178977

+ 17 - 42
app/controller/.experience.js

@@ -1,66 +1,41 @@
 module.exports = {
   create: {
-    requestBody: [
-      "planid",
-      "termid",
-      "batchid",
-      "classid",
-      "studentid",
-      "title",
-      "content",
-    ],
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid', 'title', 'content'],
   },
   destroy: {
-    params: ["!id"],
-    service: "delete",
+    params: ['!id'],
+    service: 'delete',
   },
   update: {
-    params: ["!id"],
-    requestBody: [
-      "planid",
-      "termid",
-      "batchid",
-      "classid",
-      "studentid",
-      "title",
-      "content",
-    ],
+    params: ['!id'],
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid', 'title', 'content'],
   },
   show: {
     parameters: {
-      params: ["!id"],
+      params: ['!id'],
     },
-    service: "fetch",
+    service: 'fetch',
   },
   index: {
     parameters: {
       query: {
-        termid: "termid",
-        batchid: "batchid",
-        classid: "classid",
-        studentid: "studentid",
-        content: "content",
+        termid: 'termid',
+        batchid: 'batchid',
+        classid: 'classid',
+        studentid: 'studentid',
+        content: 'content',
       },
     },
-    service: "query",
+    service: 'query',
     options: {
-      query: ["skip", "limit"],
-      sort: ["meta.createdAt"],
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
       desc: true,
       count: true,
     },
   },
   docx: {
-    parameters: {
-      query: {
-        planid: "planid",
-        termid: "termid",
-        batchid: "batchid",
-        classid: "classid",
-        studentid: "studentid",
-        id: "id",
-      },
-    },
-    service: "exportDocx",
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid'],
+    service: 'exportDocx',
   },
 };

+ 16 - 39
app/controller/.talented.js

@@ -1,63 +1,40 @@
 module.exports = {
   create: {
-    requestBody: [
-      "planid",
-      "termid",
-      "batchid",
-      "classid",
-      "studentid",
-      "files",
-    ],
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid', 'files'],
   },
   destroy: {
-    params: ["!id"],
-    service: "delete",
+    params: ['!id'],
+    service: 'delete',
   },
   update: {
-    params: ["!id"],
-    requestBody: [
-      "planid",
-      "termid",
-      "batchid",
-      "classid",
-      "studentid",
-      "files",
-    ],
+    params: ['!id'],
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid', 'files'],
   },
   show: {
     parameters: {
-      params: ["!id"],
+      params: ['!id'],
     },
-    service: "fetch",
+    service: 'fetch',
   },
   index: {
     parameters: {
       query: {
-        planid:"planid",
-        termid: "termid",
-        batchid: "batchid",
-        classid: "classid",
+        planid: 'planid',
+        termid: 'termid',
+        batchid: 'batchid',
+        classid: 'classid',
       },
     },
-    service: "query",
+    service: 'query',
     options: {
-      query: ["skip", "limit"],
-      sort: ["meta.createdAt"],
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
       desc: true,
       count: true,
     },
   },
   export: {
-    parameters: {
-      query: {
-        planid: "planid",
-        termid: "termid",
-        batchid: "batchid",
-        classid: "classid",
-        studentid: "studentid",
-        id: "id",
-      },
-    },
-    service: "export",
+    requestBody: ['planid', 'termid', 'batchid', 'classid', 'studentid'],
+    service: 'export',
   },
 };

+ 1 - 1
app/controller/cerconfirm.js

@@ -11,7 +11,7 @@ class CerconfirmController extends Controller {
     this.service = this.ctx.service.cerconfirm;
   }
 
-  // 一键分寝
+  //
   async index() {
     const res = await this.service.getStudentList(this.ctx.query);
     this.ctx.ok({ data: res });

+ 5 - 0
app/controller/weixin.js

@@ -211,6 +211,11 @@ class WeixinController extends Controller {
       throw new BusinessError(ErrorCode.NETWORK, '获取小程序信息失败,请尝试重新进入');
     }
   }
+  // 微信网页端登录(班主任,学生)
+  async openidLogin() {
+    const res = await this.ctx.service.login.openidLogin(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
 }
 
 module.exports = WeixinController;

+ 2 - 2
app/model/task.js

@@ -13,7 +13,7 @@ const questionInfo = new Schema({
   type: { type: String, required: false, maxLength: 200 }, // 类型,0-单选,1-多选,2-问答
   topic: { type: String, required: false, maxLength: 500 }, // 题目
   answer: { type: String, required: false, maxLength: 500 }, // 答案
-  option: { type: [ optionInfo ], select: true }, // 选项
+  option: { type: [ optionInfo ], required: false, select: true }, // 选项
   score: { type: String, required: false, maxLength: 500 }, // 分数
 });
 
@@ -23,7 +23,7 @@ const TaskSchema = {
   name: { type: String, required: true, maxLength: 500 }, // 科目名称
   title: { type: String, required: true, maxLength: 500 }, // 标题
   status: { type: String, required: false, maxLength: 200 }, // 状态,0-弃用,1-正常
-  question: { type: [ questionInfo ], select: true }, // 问题
+  question: { type: [ questionInfo ], required: false, select: true }, // 问题
 };
 
 const schema = new Schema(TaskSchema, { toJSON: { virtuals: true } });

+ 3 - 2
app/router.js

@@ -366,7 +366,7 @@ module.exports = app => {
 
 
   // 培训心得表设置路由
-  router.get('/api/train/experience/docx', controller.experience.docx); // index、create、show、destroy
+  router.post('/api/train/experience/docx', controller.experience.docx); // index、create、show、destroy
   router.resources('experience', '/api/train/experience', controller.experience); // index、create、show、destroy
   router.post('experience', '/api/train/experience/update/:id', controller.experience.update);
   // 职责说明表设置路由
@@ -469,6 +469,7 @@ module.exports = app => {
   router.get('/api/train/qrcode', controller.login.qrcode); // 登录
   router.post('/api/train/wxcheck', controller.login.wxcheck); // 微信检查登录
   router.post('/api/train/wxlogin', controller.login.wxlogin); // 登录
+  router.post('/api/train/openidLogin', controller.weixin.openidLogin); // 手机端登录
 
 
   // 计算教师的分数
@@ -672,7 +673,7 @@ module.exports = app => {
   router.post('/api/train/util', controller.util.utilMethod);
 
   // 新人才报
-  router.get('talented', '/api/train/talented/export', controller.talented.export);
+  router.post('talented', '/api/train/talented/export', controller.talented.export);
   router.resources('talented', '/api/train/talented', controller.talented); // index、create、show、destroy
   router.post('talented', '/api/train/talented/update/:id', controller.talented.update);
 

+ 4 - 0
app/service/class.js

@@ -302,6 +302,10 @@ class ClassService extends CrudService {
       const classs = await batch.class;
       for (const cla of classs) {
         const newdata = { name: cla.name, number: cla.number, batchid: batch.id, termid: term.id, planid: res.id, type: cla.type };
+        // // 查看班主任是否能上礼仪课 , headteacherid: cla.headteacherid
+        // const is_ly = await this.heamodel.count({ _id: cla.headteacherid, islyteacher: '1' });
+        // if (is_ly > 0)newdata.lyteacherid = cla.headteacherid;
+        // 地点设置
         const rescla = await this.model.create(newdata);
         await this.toSetClassSetting({ classid: rescla._id });
       }

+ 21 - 1
app/service/lesson.js

@@ -262,7 +262,27 @@ class LessonService extends CrudService {
       'lessons.teaid': teaid,
     });
     const classids = lessons.map(i => ObjectId(i.classid));
-    const data = await this.ctx.model.Class.find({ _id: { $in: classids } });
+    let data = await this.ctx.model.Class.find({ _id: { $in: classids } }).lean();
+    if (data.length <= 0) return;
+    const planid = data[0].planid;
+    const trainPlan = await this.tmodel.findById(planid).lean();
+    const { termnum = [] } = trainPlan;
+    if (termnum.length <= 0) return;
+    const termData = termnum.find(f => ObjectId(f._id).equals(termid));
+    if (!termData) return;
+    const { batchnum = [], term } = termData;
+    if (batchnum.length <= 0) return;
+    data = data.map(i => {
+      i.term = term;
+      const batchData = batchnum.find(f => ObjectId(f._id).equals(i.batchid));
+      if (batchData) {
+        i.batch = batchData.batch;
+        i.startdate = batchData.startdate;
+        i.enddate = batchData.enddate;
+      }
+      return i;
+    });
+
     return data;
   }
 

+ 31 - 2
app/service/login.js

@@ -36,6 +36,16 @@ class LoginService extends CrudService {
     return await this.createJwt(res);
   }
 
+  // 微信网页端登录(班主任,学生)
+  async openidLogin(data) {
+    const { openid, type } = data;
+    const res = await this.uModel.findOne({ openid, type });
+    if (!res) {
+      throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    }
+    return await this.createJwt(res);
+  }
+
   // 创建登录Token
   async createJwt({ _id, name, mobile, openid, type, uid, status }) {
     const { secret, expiresIn = '1d', issuer = type } = this.config.jwt;
@@ -53,17 +63,35 @@ class LoginService extends CrudService {
     } else if (type === '2') {
       _userid = uid.toString();
       const result = await this.schModel.findById(_userid);
+      if (!result) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的角色信息');
       res = { userid: _userid, code: result.code, name, type, id: _id, status };
     } else if (type === '3') {
       _userid = uid.toString();
       const result = await this.tModel.findById(_userid);
+      if (!result) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的角色信息');
       res = { userid: _userid, schid: result.schid, schname: result.schname, name, subid: result.subid, type, id: _id, status };
     } else if (type === '4') {
       _userid = uid;
       // console.log('进入查询--' + _userid);
       const result = await this.stuModel.findById(_userid);
+      if (!result) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的角色信息');
       // console.log(result);
-      res = { userid: _userid, schid: result.schid, schname: result.school_name, termid: result.termid, batchid: result.batchid, classid: result.classid, bedroomid: result.bedroomid, bedroom: result.bedroom, job: result.job, name, type, id: _id, status, planid: result.planid };
+      res = {
+        userid: _userid,
+        schid: result.schid,
+        schname: result.school_name,
+        termid: result.termid,
+        batchid: result.batchid,
+        classid: result.classid,
+        bedroomid: result.bedroomid,
+        bedroom: result.bedroom,
+        job: result.job,
+        name,
+        type,
+        id: _id,
+        status,
+        planid: result.planid,
+      };
       // console.warn(res);
     }
     const token = await jwt.sign(res, secret, { expiresIn, issuer, subject });
@@ -82,7 +110,8 @@ class LoginService extends CrudService {
       durable: true,
       headers: {
         openid,
-      } };
+      },
+    };
     if (mq) {
       await mq.topic(ex, qrcode, 'scaned', parm);
     }

+ 1 - 1
app/service/school.js

@@ -606,7 +606,7 @@ class SchoolService extends CrudService {
           wi = factor[i];
           sum += ai * wi;
         }
-        if (parity[sum % 11] !== code[17].toUpperCase()) {
+        if (parity[sum % 11] != code[17].toUpperCase()) {
           row = {
             pass: false,
             msg: '身份证号校验位错误',

+ 19 - 10
app/service/task.js

@@ -1,12 +1,11 @@
 '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;
-
+const moment = require('moment');
 class TaskService extends CrudService {
   constructor(ctx) {
     super(ctx, 'task');
@@ -14,6 +13,15 @@ class TaskService extends CrudService {
     const { baseUrl } = _.get(this.ctx.app.config, 'mission');
     if (baseUrl) this.missionBase = baseUrl;
   }
+
+  async update({ id }, data) {
+    assert(id, '缺少要修改的数据信息');
+    const question = _.get(data, 'question', []);
+    if (question.length <= 0) delete data.question;
+    await this.model.findByIdAndUpdate(id, data);
+    return await this.model.findById(id);
+  }
+
   // 建立任务
   async toExport(body) {
     const { range } = body;
@@ -117,7 +125,6 @@ class TaskService extends CrudService {
       console.log('作业导出=>100%');
     } catch (error) {
       this.ctx.service.util.updateProcess(missionid, undefined, '3');
-
     }
   }
 
@@ -133,13 +140,12 @@ class TaskService extends CrudService {
       lessonList = await this.ctx.model.Lesson.find({ termid: { $in: termid } });
     } else if (planid) {
       const trainPlan = await this.ctx.model.Trainplan.findById(planid);
-      if (!trainPlan) { throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划信息'); }
+      if (!trainPlan) {
+        throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划信息');
+      }
       let { termnum } = trainPlan;
       if (!termnum) {
-        throw new BusinessError(
-          ErrorCode.DATA_NOT_EXIST,
-          '未找到年度计划下的期信息'
-        );
+        throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到年度计划下的期信息');
       }
       termnum = JSON.parse(JSON.stringify(termnum));
       const termids = termnum.map(i => i._id);
@@ -204,7 +210,11 @@ class TaskService 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}期`;
+              }
             }
           }
         }
@@ -241,7 +251,6 @@ class TaskService extends CrudService {
     }
     return obj;
   }
-
 }
 
 module.exports = TaskService;

+ 21 - 5
app/service/uploadtask.js

@@ -16,15 +16,31 @@ class UploadtaskService extends CrudService {
 
   async query({ skip, limit, ...info }) {
     const total = await this.model.count(info);
-    const res = await this.model.find(info).skip(Number(skip)).limit(Number(limit));
+    const res = await this.model
+      .find(info)
+      .populate([
+        {
+          path: 'studentid',
+          model: 'Student',
+          select: 'name',
+        },
+      ])
+      .skip(Number(skip))
+      .limit(Number(limit));
     const data = [];
     for (const elm of res) {
       const _elm = _.cloneDeep(JSON.parse(JSON.stringify(elm)));
-      const stu = await this.stumodel.findById(_elm.studentid);
-      if (stu)_elm.stuname = stu.name;
-      else {
-        console.error(`学生id:${_elm.studentid}/数据错误`);
+      if (_.isObject(elm.studentid)) {
+        const { _id: studentid, name: stuname } = elm.studentid;
+        _elm.studentid = studentid;
+        if (!stuname) continue;
+        _elm.stuname = stuname;
       }
+      // const stu = await this.stumodel.findById(_elm.studentid);
+      // if (stu)_elm.stuname = stu.name;
+      // else {
+      //   console.error(`学生id:${_elm.studentid}/数据错误`);
+      // }
       data.push(_elm);
     }
     return { data, total };

+ 6 - 7
app/service/user.js

@@ -1,6 +1,5 @@
 'use strict';
 
-
 const assert = require('assert');
 const _ = require('lodash');
 const { ObjectId } = require('mongoose').Types;
@@ -114,7 +113,7 @@ class UserService extends CrudService {
   async bind(data) {
     const { openid, id_number, mobile, unionid } = data;
     assert(openid && id_number && mobile, '缺少部分信息项');
-    let user = await this.model.findOne({ mobile });
+    let user = await this.model.findOne({ mobile, type: '4' });
     if (user) {
       // 2021-06-07修改:根据身份证/手机号,找到对应的学生信息,将_id变为uid重保存
       const stuInfo = await this.stuModel.findOne({ $or: [{ id_number }, { mobile }] });
@@ -155,6 +154,7 @@ class UserService extends CrudService {
       } else {
         newdata.passwd = { secret: '12345678' };
         user = await this.model.create(newdata);
+        delete user.passwd;
       }
     }
     return user;
@@ -164,9 +164,10 @@ class UserService extends CrudService {
   async userbind(data) {
     const { openid, uid, qrcode } = data;
     assert(openid && uid && qrcode, '缺少部分信息项');
-    const user = await this.model.findById(uid);
+    let user = await this.model.findById(uid);
     if (!user) {
-      throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+      user = await this.model.findOne({ uid });
+      if (!user) throw new BusinessError(ErrorCode.USER_NOT_EXIST);
     }
     user.openid = openid;
     const res = await user.save();
@@ -286,9 +287,7 @@ class UserService extends CrudService {
   // 学生小程序绑定
   async appbind(data) {
     const { name, mobile, appopenid } = data;
-    console.error(
-      `appBind: name=>${name} / mobile=>${mobile} / appopenid = ${appopenid}`
-    );
+    console.error(`appBind: name=>${name} / mobile=>${mobile} / appopenid = ${appopenid}`);
     assert(name, '缺少姓名');
     assert(mobile, '缺少手机号');
     assert(appopenid, '缺少小程序openid');

+ 9 - 2
app/service/util.js

@@ -85,6 +85,13 @@ class UtilService extends CrudService {
     return res;
   }
   async utilMethod(query, body) {
+    const arr = this.app.router.stack;
+    const list = [];
+    for (const i of arr) {
+      const { name, methods, path } = i;
+      list.push([ name, methods, path ]);
+    }
+    await this.toExcel(list);
     // for (const trainPlan of allTranPlan) {
     //   const { termnum = [] } = trainPlan;
     //   const termids = termnum.map(f => ObjectId(f._id).toString());
@@ -367,9 +374,9 @@ class UtilService extends CrudService {
     const { app } = this;
     const rootPath = `${app.config.cdn.repos_root_path}`;
     const rooturl = `${app.config.cdn.repos_root_url_experience}`;
-    let path = `${rootPath}${rooturl}`;
+    const path = `${rootPath}${rooturl}`;
     // 如果不存在文件夹,就创建
-    if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
+    // if (process.env.NODE_ENV === 'development') path = 'E:\\exportFile\\';
     if (!fs.existsSync(path)) {
       fs.mkdirSync(path);
     }

+ 29 - 18
app/service/weixin.js

@@ -161,7 +161,7 @@ class WeixinAuthService extends AxiosService {
     if (!val) {
       throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
     }
-    const [ status, token ] = val.split(':', 2);
+    const [status, token] = val.split(':', 2);
     if (status !== 'scaned' || !token) {
       throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码状态无效');
     }
@@ -180,7 +180,7 @@ class WeixinAuthService extends AxiosService {
     if (!val) {
       throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
     }
-    const [ status ] = val.split(':', 2);
+    const [status] = val.split(':', 2);
     return { status };
   }
 
@@ -221,20 +221,26 @@ class WeixinAuthService extends AxiosService {
         },
       },
     };
-    await this.ctx.curl(url, {
-      method: 'post',
-      headers: {
-        'content-type': 'application/json',
-      },
-      dataType: 'json',
-      data: JSON.stringify(requestData),
-    });
+    try {
+      await this.ctx.curl(url, {
+        method: 'post',
+        headers: {
+          'content-type': 'application/json',
+        },
+        dataType: 'json',
+        data: JSON.stringify(requestData),
+      });
+    } catch (error) {
+      console.error(error);
+      console.error('微信发送消息失败');
+    }
   }
 
   // 发送微信模板消息自定义消息
   async sendTemplateDesign(templateid, openid, first, keyword1, keyword2, remark, tourl) {
     const { wxapi } = this.ctx.app.config;
     const url = wxapi.sendDirMq + wxapi.appid;
+    if (process.env.NODE_ENV === 'development') openid = 'ocPqjswkUejZHq2ANriNrFFC7A3I';
     const requestData = {
       // 发送模板消息的数据
       touser: openid,
@@ -259,14 +265,19 @@ class WeixinAuthService extends AxiosService {
         },
       },
     };
-    const res = await this.ctx.curl(url, {
-      method: 'post',
-      headers: {
-        'content-type': 'application/json',
-      },
-      dataType: 'json',
-      data: JSON.stringify(requestData),
-    });
+    try {
+      const res = await this.ctx.curl(url, {
+        method: 'post',
+        headers: {
+          'content-type': 'application/json',
+        },
+        dataType: 'json',
+        data: JSON.stringify(requestData),
+      });
+    } catch (error) {
+      console.error(error);
+      console.error('微信发送消息失败');
+    }
   }
 
   // 小程序登录

+ 7 - 9
config/config.local.js

@@ -1,5 +1,6 @@
 'use strict';
 const { sep } = require('path');
+const ip = '127.0.0.1';
 module.exports = () => {
   const config = (exports = {});
 
@@ -9,12 +10,9 @@ module.exports = () => {
   };
 
   config.wxapi = {
-    appid: 'wxdf3ed83c095be97a', // 微信公众号APPID
-    baseUrl: 'http://wx.cc-lotus.info', // 微信网关地址
-    mchid: '1505364491', // 商户ID
-    mchkey: '1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik9', // 商户key
-    wxurl: 'http://free.liaoningdoupo.com/api/wxpayback',
-    payurl: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
+    appid: 'wxd2e28415cb866c0b', // 微信公众号APPID
+    baseUrl: 'http://www.jilinjobswx.cn', // 微信网关地址
+    sendDirMq: 'http://www.jilinjobswx.cn/api.weixin.qq.com/cgi-bin/message/template/send?appid=',
   };
   config.cdn = {
     repos_root_path: `D:\\temp${sep}upload`,
@@ -24,14 +22,14 @@ module.exports = () => {
   };
 
   // 服务器发布路径
-  config.baseUrl = 'http://jytz.jilinjobs.cn/';
+  config.baseUrl = 'http://jytz.jilinjobs.cn';
   // 认证回调地址
   config.authUrl = '/api/auth';
 
   // mq config
   config.amqp = {
     client: {
-      hostname: '192.168.1.128',
+      hostname: ip,
       username: 'visit',
       password: 'visit',
       vhost: 'train',
@@ -44,7 +42,7 @@ module.exports = () => {
   config.redis = {
     client: {
       port: 6379, // Redis port
-      host: '192.168.1.128', // Redis host
+      host: ip, // Redis host
       password: '123456',
       db: 0,
     },

+ 102 - 0
test/test.js

@@ -0,0 +1,102 @@
+'use strict';
+function idCodeValid(code) {
+  // 身份证号合法性验证
+  // 支持15位和18位身份证号
+  // 支持地址编码、出生日期、校验位验证
+  const city = {
+    11: '北京',
+    12: '天津',
+    13: '河北',
+    14: '山西',
+    15: '内蒙古',
+    21: '辽宁',
+    22: '吉林',
+    23: '黑龙江 ',
+    31: '上海',
+    32: '江苏',
+    33: '浙江',
+    34: '安徽',
+    35: '福建',
+    36: '江西',
+    37: '山东',
+    41: '河南',
+    42: '湖北 ',
+    43: '湖南',
+    44: '广东',
+    45: '广西',
+    46: '海南',
+    50: '重庆',
+    51: '四川',
+    52: '贵州',
+    53: '云南',
+    54: '西藏 ',
+    61: '陕西',
+    62: '甘肃',
+    63: '青海',
+    64: '宁夏',
+    65: '新疆',
+    71: '台湾',
+    81: '香港',
+    82: '澳门',
+    91: '国外 ',
+  };
+  let row = {
+    pass: true,
+    msg: '验证成功',
+  };
+  if (!code || !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/.test(code)) {
+    row = {
+      pass: false,
+      msg: '身份证号格式错误',
+    };
+  } else if (!city[code.substr(0, 2)]) {
+    row = {
+      pass: false,
+      msg: '身份证号地址编码错误',
+    };
+  } else {
+    // 18位身份证需要验证最后一位校验位
+    if (code.length === 18) {
+      code = code.split('');
+      // ∑(ai×Wi)(mod 11)
+      // 加权因子
+      const factor = [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 ];
+      // 校验位
+      const parity = [ 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2 ];
+      let sum = 0;
+      let ai = 0;
+      let wi = 0;
+      for (let i = 0; i < 17; i++) {
+        ai = code[i];
+        wi = factor[i];
+        sum += ai * wi;
+      }
+      if (parity[sum % 11] !== code[17].toUpperCase()) {
+        row = {
+          pass: false,
+          msg: '身份证号校验位错误',
+          should: parity[sum % 11],
+          is: code[17].toUpperCase(),
+        };
+      }
+    }
+  }
+  return row;
+}
+const arr = [
+  '22010319950601161X',
+  '220103199506119700',
+  '220104199506196198',
+  '220104199506086490',
+  '220104199506039600',
+  '220104199506280349',
+  '220104199506036581',
+  '220104199506154414',
+  '220104199506040919',
+  '220104199506269852',
+  '220104199506039598',
+];
+for (const i of arr) {
+  const res = idCodeValid(i);
+  console.log(`${i}:${JSON.stringify(res)}`);
+}