lrf 2 年之前
父節點
當前提交
4c145e3001

+ 6 - 0
app/controller/config/.matchSign.js

@@ -41,4 +41,10 @@ module.exports = {
       count: true,
     },
   },
+  checkHas: {
+    requestBody: ['!match_id', '!group_id', '!project_id', '!user_id', 'card'],
+  },
+  checkFit: {
+    requestBody: ['!match_id', '!group_id', '!project_id', '!user_id', 'card'],
+  },
 };

+ 1 - 1
app/model/race/matchGroup.js

@@ -7,7 +7,7 @@ const source = 'race';
 const match_group = {
   match_id: { type: String, required: true, zh: '赛事id', ref: 'Match', getProp: [ 'name' ] }, // 比赛信息中的比赛名称
   name: { type: String, required: true, zh: '名称' }, //
-  age: { type: String, required: false, zh: '年龄限制' }, //
+  age: { type: String, required: false, zh: '年龄限制' }, // 12-16 / null(不限)
   explain: { type: String, required: false, zh: '说明' }, //
 };
 const schema = new Schema(match_group, { toJSON: { getters: true, virtuals: true } });

+ 1 - 1
app/model/race/matchProject.js

@@ -8,7 +8,7 @@ const match_project = {
   group_id: { type: String, required: true, zh: '赛事分组id', ref: 'MatchGroup', getProp: [ 'name' ] }, // 赛事分组中的组名称
   type: { type: String, required: true, zh: '项目类别' }, // 字典表中的标签
   name: { type: String, required: true, zh: '名称' }, //
-  age: { type: String, required: false, zh: '年龄限制' }, //
+  age: { type: String, required: false, zh: '年龄限制' }, // 12-16 / null(不限)
   gender: { type: String, required: false, zh: '性别限制' }, // 字典表中的标签
   num: { type: Number, required: false, zh: '人数限制' }, //
   explain: { type: String, required: false, zh: '说明' }, //

+ 2 - 2
app/model/race/matchSign.js

@@ -9,8 +9,8 @@ const match_sign = {
   project_id: { type: String, required: true, zh: '项目id', ref: 'MatchGroup', getProp: [ 'name' ] }, // 组别项目中的名称
   user_id: { type: String, required: true, zh: '用户id' }, //
   is_share: { type: Boolean, required: false, zh: '是否转发' }, //
-  pay_id: { type: String, required: false, zh: '账单id' }, //
-  pay_status: { type: String, required: false, zh: '账单状态', default: '0' }, // 0:未支付;1支付成功;-1:支付失败;-2:申请退款(退赛)
+  pay_id: { type: String, required: false, zh: '账单id', ref: 'PayOrder' }, //
+  pay_status: { type: String, required: false, zh: '账单状态', default: '0' }, // 0:未支付;1支付成功;-1:支付失败;-2:申请退款(退赛);-3:已退款(退赛)
 };
 const schema = new Schema(match_sign, { toJSON: { getters: true, virtuals: true } });
 schema.index({ id: 1 });

+ 179 - 0
app/service/matchSign.js

@@ -9,14 +9,193 @@ class MatchSignService extends CrudService {
   constructor(ctx) {
     super(ctx, 'matchsign');
     this.model = this.ctx.model.Race.MatchSign;
+    this.matchModel = this.ctx.model.Race.Match;
+    this.matchGroupModel = this.ctx.model.Race.MatchGroup;
+    this.matchProjectModel = this.ctx.model.Race.MatchProject;
   }
   /**
    * 检查是否已经有该赛事,该组别,该项目的报名
    * @param {Object} body 参数体
    */
   async beforeCreate(body) {
+    await this.checkHas(body);
     return body;
   }
+
+  /**
+   * 检查有没有报过名
+   * @param {Object} body 参数体
+   * @return {Boolean} true 可以继续进行
+   */
+  async checkHas(body) {
+    const { match_id, group_id, project_id, user_id } = body;
+    const query = { match_id, group_id, project_id, user_id, pay_status: [ '0', '1' ] };
+    const num = await this.model.query(query);
+    if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '您已报名');
+    return true;
+  }
+
+  async checkFit(body) {
+    const { match_id, group_id, project_id, user_id, card = '22010319950601161X' } = body;
+    const { pass, msg, gender: cardGender, age: cardAge } = this.idCodeValid(card);
+    if (!pass) throw new BusinessError(ErrorCode.SERVICE_FAULT, msg);
+    // 1.看 match 状态 是否为报名中
+    const mnum = await this.matchModel.count({ _id: match_id, status: '1' });
+    if (mnum <= 0) throw new BusinessError(ErrorCode.DATA_INVALID, '当前赛事不处于报名期');
+    // 2.看matchGroup 年龄限制是否符合要求
+    const matchGroup = await this.matchGroupModel.findById(group_id);
+    if (!matchGroup) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到当前组别');
+    const { age } = matchGroup;
+    let next = this.checkAgeInRange(age, cardAge);
+    // 3.看 matchProject 年龄限制和性别限制,人数限制
+    if (!next) throw new BusinessError(ErrorCode.DATA_INVALID, '年龄不符合组别条件');
+    const matchProject = await this.matchProjectModel.findById(project_id);
+    if (!matchProject) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到组别项目');
+    const { age: projectAge, gender } = matchProject;
+    next = this.checkAgeInRange(projectAge, cardAge);
+    if (!next) throw new BusinessError(ErrorCode.DATA_INVALID, '年龄不符合组别项目条件');
+    if (gender !== '2') {
+      if (gender !== cardGender) throw new BusinessError(ErrorCode.DATA_INVALID, '性别不符合组别条件');
+    }
+    return true;
+
+  }
+
+  checkAgeInRange(age, cardAge) {
+    let next = true;
+    if (_.isString(age) && age.include('-')) {
+      const arr = age.split('-');
+      const start = parseInt(_.head(arr));
+      const end = parseInt(_.last(arr));
+      // inRange 是左闭右开区间,所以加1,形成闭区间
+      const r = _.inRange(cardAge, start, end + 1);
+      next = r;
+    } else if (!age || age === '不限') next = true;
+    return next;
+  }
+
+  // 身份证验证
+  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: '身份证号校验位错误',
+          };
+        }
+      }
+    }
+    if (row.pass) {
+      row.gender = this.maleOrFemalByIdCard(code);
+      row.age = this.getAge(code);
+    }
+    return row;
+  }
+  // 性别
+  maleOrFemalByIdCard(idCard) {
+    if (_.isArray) idCard = idCard.join('');
+    idCard = _.trim(idCard.replace(/ /g, '')); // 对身份证号码做处理。包括字符间有空格。
+    if (idCard.length === 15) {
+      if (idCard.substring(14, 15) % 2 === 0) {
+        return '1';
+      }
+      return '0';
+
+    } else if (idCard.length === 18) {
+      if (idCard.substring(14, 17) % 2 === 0) {
+        return '1';
+      }
+      return '0';
+
+    }
+    return null;
+
+  }
+  // 年龄
+  getAge(idCard) {
+    if (_.isArray) idCard = idCard.join('');
+    const ageDate = new Date();
+    const month = ageDate.getMonth() + 1;
+    const day = ageDate.getDate();
+    let age = ageDate.getFullYear() - idCard.substring(6, 10) - 1;
+    if (idCard.substring(10, 12) < month || (idCard.substring(10, 12) === month && idCard.substring(12, 14) <= day)) {
+      age++;
+    }
+    if (age <= 0) {
+      age = 1;
+    }
+    return age;
+  }
 }
 
 module.exports = MatchSignService;

+ 3 - 1
app/z_router/matchSign.js

@@ -7,9 +7,11 @@ const rkey = 'matchSign';
 const ckey = 'matchSign';
 const keyZh = '赛事报名';
 const routes = [
+  { method: 'post', path: `${rkey}/checkHas`, controller: `${ckey}.checkHas`, name: `${ckey}checkHas`, zh: `${keyZh}-检查是否报名` },
+  { method: 'post', path: `${rkey}/checkFit`, controller: `${ckey}.checkFit`, name: `${ckey}checkFit`, zh: `${keyZh}-检查是否符合报名条件` },
   { method: 'get', path: `${rkey}`, controller: `${ckey}.index`, name: `${ckey}Query`, zh: `${keyZh}列表查询` },
   { method: 'get', path: `${rkey}/:id`, controller: `${ckey}.show`, name: `${ckey}Show`, zh: `${keyZh}查询` },
-  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, middleware: [ 'password' ], name: `${ckey}Create`, zh: `创建${keyZh}` },
+  { method: 'post', path: `${rkey}`, controller: `${ckey}.create`, name: `${ckey}Create`, zh: `创建${keyZh}` },
   { method: 'post', path: `${rkey}/:id`, controller: `${ckey}.update`, name: `${ckey}Update`, zh: `修改${keyZh}` },
   { method: 'delete', path: `${rkey}/:id`, controller: `${ckey}.destroy`, name: `${ckey}Delete`, zh: `删除${keyZh}` },
 ];