lrf 4 months ago
parent
commit
d579ac6016

+ 155 - 5
src/controller/match/matchExt.controller.ts

@@ -8,6 +8,7 @@ import { MatchExtService } from '../../service/match/matchExt.service';
 import { MatchRegistrationService } from '../../service/match/matchRegistration.service';
 import { UserService } from '../../service/system/user.service';
 import { AliyunSmsService } from '../../service/thirdParty/aliyunSms.service';
+import { MatchService } from '../../service/platform/match.service';
 const namePrefix = '创新大赛拓展';
 @ApiTags(['创新大赛拓展'])
 @Controller('/matchExt', { tagName: namePrefix })
@@ -20,6 +21,9 @@ export class MatchExtController implements BaseController {
   userService: UserService;
   @Inject()
   smsService: AliyunSmsService;
+  @Inject()
+  matchService: MatchService
+
 
   @Get('/')
   @ApiTags('列表查询')
@@ -42,6 +46,7 @@ export class MatchExtController implements BaseController {
   @ApiTags('创建数据')
   @Validate()
   async create(@Body() data: object) {
+    // 跟随赛事信息创建,不能直接创建
     return false;
   }
 
@@ -50,7 +55,9 @@ export class MatchExtController implements BaseController {
   @Validate()
   async update(@Param('id') id: number, @Body() data: object) {
     if (!id) throw new ServiceError(ErrorCode.ID_NOT_FOUND);
-    const result = await this.service.update({ id }, data);
+    // 所有的修改,是不允许修改状态的
+    const newData = omit(data, ['status'])
+    const result = await this.service.update({ id }, newData);
     return result;
   }
 
@@ -59,6 +66,14 @@ export class MatchExtController implements BaseController {
   @Validate()
   async delete(@Param('id') id: number) {
     if (!id) throw new ServiceError(ErrorCode.ID_NOT_FOUND);
+    // 不允许删除非报名中的赛事
+    const data = await this.service.fetch({ id });
+    if (!data) return
+    const status = get(data, 'status')
+    if (status != '0') {
+      // 抛出异常:赛事不处于报名中的状态,无法删除.
+      throw new ServiceError(ErrorCode.MATCH_EXT_IS_BEGINNING);
+    }
     const result = await this.service.delete({ id });
     return result;
   }
@@ -69,12 +84,108 @@ export class MatchExtController implements BaseController {
     return data;
   }
 
+  /**
+   * 进入:报名阶段-已结束
+   * @param data body
+   * @property match_id 赛事id
+   */
+  @Post('/step1', { routerName: '报名阶段-已结束' })
+  async step1(@Body() data: object) {
+    //报名结束,需要检查 报名的创建接口是否允许创建信息----不允许再添加报名信息了
+    /**
+     * 检查内容:
+     *  赛事拓展表:
+     *    ext.status = "0"
+     *  赛事表:
+     *    match.status = "1"
+     * 修改内容: 
+     *  赛事拓展表:
+     *    ext.status => "1" 变为 报名阶段-已结束
+     *  赛事表:
+     *    match.status =>"2" 变为 进行中
+     *  赛事报名表:
+     *    处于 报名阶段 且 已通过审核的报名信息 变为 待审核(初赛阶段)
+     *    reg.status = "1" & reg.ext_status = "0" => reg.ext_status:"1" reg.status='0' 
+     */
+    const match_id = get(data, 'match_id')
+    await this.service.checkMatchStatus(match_id, "1")
+    await this.service.checkMatchExtStatus(match_id, "0")
+    await this.matchService.update({ id: match_id }, { status: '2' })
+    await this.service.update({ match_id }, { status: "1" })
+    await this.matchRegService.update({ match_id, ext_status: '0', status: '1' }, { ext_status:'1', status: '0' })
+    return 'ok';
+  }
+
+  // 报名阶段-已结束 到 初赛阶段-组织初审 过程只是变化状态码,不需要处理其他内容
+
+  /**
+   * 进入: 初赛阶段-组织初审
+   * @param data body
+   * @property match_id 赛事id
+   */
+  @Post('/step2', { routerName: '初赛阶段-组织初审' })
+  async regBack(@Body() data: object) {
+    // 只修改状态,不做时间修改,时间修改用另一个接口
+    /**
+    * 检查内容:
+    *  赛事拓展表:
+    *    ext.status = "1"
+    * 修改内容: 
+    *  赛事拓展表:
+    *    ext.status => "2" 变为 初赛阶段-组织初审
+    */
+    const match_id = get(data, 'match_id')
+    await this.service.checkMatchExtStatus(match_id, "1")
+    await this.service.update({ match_id }, { status: '2' })
+  }
+
+  /**
+   * 初赛阶段-组织初审 到 初赛阶段-公示名单 需要修改 参加初赛人员信息的 初赛开始时间
+   * @param data body
+   * @property match_id 赛事id
+   * @property start_time 初赛时间
+   */
+  @Post("/step2/fill", { routerName: '初赛阶段-组织初审-补充初赛时间信息' })
+  async step2Fill(@Body() data: object) {
+    /**
+   * 检查内容:
+   *  赛事拓展表:
+   *    ext.status = "2"
+   * 修改内容: 
+   *  赛事报名表:
+   *    reg.status = '0' 的 reg.start_time => ${start_time}
+   */
+    const match_id = get(data, 'match_id')
+    await this.service.checkMatchExtStatus(match_id, "2")
+  }
+
+  @Post('/step3', { routerName: '初赛阶段-公示名单' })
+  async step3(@Body() data: object) { }
+
+  @Post('/step4', { routerName: '初赛阶段-赛事进行' })
+  async step4(@Body() data: object) { }
+
+  @Post('/step5', { routerName: '决赛阶段-组织决赛' })
+  async step5(@Body() data: object) { }
+
+  @Post('/step6', { routerName: '决赛阶段-名单公示' })
+  async step6(@Body() data: object) { }
+
+  @Post('/step7', { routerName: '决赛阶段-赛事进行' })
+  async step7(@Body() data: object) { }
+
+  @Post('/step8', { routerName: '决赛阶段-赛事结束' })
+  async step8(@Body() data: object) { }
+
   @Get('/firstStep/:match_id', { routerName: '进入初赛阶段' })
   async toFirstStep(@Param('match_id') match_id: string, @Body() data: object) {
     // 进入初赛阶段,查询选手名单(未被退回的),根据选手名单的报名顺序,排列名单,并赋予开始时间
     /**赛事拓展数据 */
     const extData = await this.service.fetch({ match_id });
     if (extData) {
+      const nowStatus = get(extData, 'status')
+      // 如果状态不是 '0', 说明状态不对,
+      // if (nowStatus !== '0') throw new ServiceError(ErrorCode.MATCH_EXT_STATUS_ERROR)
       // 修改流程进度为下一步----初赛
       await this.service.update({ match_id }, { status: '1' });
     }
@@ -83,14 +194,15 @@ export class MatchExtController implements BaseController {
     await this.matchRegService.update(regQuery, { start_time });
     /**赛事报名数据 */
     const { data: list } = await this.matchRegService.query(regQuery);
+    // 获取赛事名称
+    const matchData = await this.matchService.fetch({ id: match_id })
     // 发送短信通知
-    const msg = `您参加的赛事: ${get(extData, 'name')} 将于 ${start_time}开始初赛,具体安排请通过网页或小程序查看.请您提前做好准备.`;
+    const msg = `您参加的赛事: ${get(matchData, 'name')} 将于 ${start_time}开始初赛,具体安排请通过网页或小程序查看.请您提前做好准备.`;
     for (const i of list) {
       const user_id = get(i, 'user_id');
       if (!user_id) continue;
-      const user = await this.userService.fetch({ id: user_id });
-      if (!user) continue;
-      const phone = get(user, 'phone');
+      const ups = await this.service.getUserProps(user_id, ['phone'])
+      const phone = get(ups, 'phone');
       try {
         await this.smsService.send(phone, msg);
       } catch (error) {
@@ -98,4 +210,42 @@ export class MatchExtController implements BaseController {
       }
     }
   }
+
+  @Post('/secondStep/:match_id', { routerName: '进入决赛准备阶段' })
+  async toSecondStep(@Param('match_id') match_id: string, @Body() data: object) {
+    // 进入决赛准备阶段,获取前端传来的人数,保存在拓展表里.
+    const extData = await this.service.fetch({ match_id });
+    const final_persons = get(data, 'final_persons')
+    // TODO: 抛出异常,缺少进入决赛人数设置
+    // if (!final_persons) throw new ServiceError(ErrorCode.MATCH_EXT_NO_FINAL_PERSON)
+    if (extData) {
+      const nowStatus = get(extData, 'status')
+      // 如果状态不是 '1', 说明状态不对,
+      // TODO:抛出异常 赛事状态错误
+      // if (nowStatus !== '1') throw new ServiceError(ErrorCode.MATCH_EXT_STATUS_ERROR)
+      // 修改流程进度为下一步----决赛准备阶段
+      await this.service.update({ match_id }, { status: '2', final_persons });
+    }
+    // 根据进入决赛的人数,查询列表; 初赛分数在走这个接口前就需要上完.
+    const query = { match_id }
+    const others = { skip: 0, limit: final_persons, others: { score: 'DESC' } }
+    const { data: list } = await this.matchRegService.query(query, others)
+    // 获取赛事名称
+    const matchData = await this.matchService.fetch({ id: match_id })
+    const match_name = get(matchData, 'name')
+    const msg = `您在赛事: ${match_name}中已进入决赛,请通过网站或小程序进入平台进行信息确认.`
+    const lastList = [];
+    for (const i of list) {
+      const user_id = get(i, 'user_id');
+      if (!user_id) continue;
+      // 发短信通知去确认
+      const ups = await this.service.getUserProps(user_id, ['phone'])
+      const phone = get(ups, 'phone');
+      try {
+        await this.smsService.send(phone, msg);
+      } catch (error) {
+        console.error('matchExt - toSecondStep:发送短信发生错误');
+      }
+    }
+  }
 }

+ 25 - 0
src/controller/match/matchRegistration.controller.ts

@@ -66,6 +66,22 @@ export class MatchRegistrationController implements BaseController {
   @ApiTags('创建数据')
   @Validate()
   async create(@Body() data: object) {
+    // 检查赛事拓展表中对应的拓展信息状态,如果不是报名中,则不允许添加
+    const match_id = get(data, 'match_id')
+    if (!match_id) {
+      // 抛出异常: 需要赛事信息
+      throw new ServiceError(ErrorCode.MATCH_REG_NEED_MATCH_ID)
+    }
+    const matchInfo = await this.matchService.fetch({ id: match_id })
+    if (!matchInfo) {
+      // 抛出异常: 未找到赛事拓展信息
+      throw new ServiceError(ErrorCode.MATCH_EXT_DATA_NOT_FOUND)
+    }
+    const match_status = get(matchInfo, 'status');
+    if (match_status != "0") {
+      // 抛出异常: 该赛事已不处于报名阶段,无法进行报名
+      throw new ServiceError(ErrorCode.MATCH_REG_MATCH_IS_NOT_REG);
+    }
     const userColumns = ['user', 'user_id'];
     let regData = omit(data, userColumns);
     let returnUser = {};
@@ -152,6 +168,15 @@ export class MatchRegistrationController implements BaseController {
     return data;
   }
 
+  @Post('/score/:id', { routerName: '为初赛选手打分' })
+  async setScore(@Param('id') id: string, @Body() data: object) {
+    const regData = await this.service.fetch({ id })
+    if (!regData) throw new ServiceError(ErrorCode.DATA_NOT_FOUND)
+    const score = get(data, 'score')
+    if (score) await this.service.update({ id }, { score })
+    return 'ok'
+  }
+
   @Get('/view/:match_id', { routerName: '查看初赛名单结果' })
   async viewOrderByScore(@Param('match_id') match_id: string) {
     const query = { match_id, status: '0' };

+ 14 - 0
src/entity/match/matchExt.entity.ts

@@ -1,5 +1,17 @@
 import { Entity, Column } from 'typeorm';
 import { BaseModel } from '../../frame/BaseModel';
+/**
+ * 流程状态:
+ * 0:报名阶段-报名中 -- 默认生成数据的状态
+ * 1:报名阶段-已结束
+ * 2:初赛阶段-组织初审
+ * 3:初赛阶段-公示名单
+ * 4:初赛阶段-赛事进行
+ * 5:决赛阶段-组织决赛
+ * 6:决赛阶段-名单公示
+ * 7:决赛阶段-赛事进行
+ * 8:决赛阶段-赛事结束
+ */ 
 //赛事
 @Entity('matchExt', { comment: '赛事拓展表' })
 export class MatchExt extends BaseModel {
@@ -11,4 +23,6 @@ export class MatchExt extends BaseModel {
   info: Array<any>;
   @Column({ type: 'jsonb', nullable: true, comment: '决赛分数维度设置' })
   finals: Array<any>;
+  @Column({ type: 'integer', nullable: true, comment: '进入决赛人数' })
+  final_persons: number;
 }

+ 0 - 25
src/entity/match/matchLast.entity.ts

@@ -1,25 +0,0 @@
-import { Entity, Column } from 'typeorm';
-import { BaseModel } from '../../frame/BaseModel';
-import dayjs = require('dayjs');
-//赛事
-@Entity('matchLast', { comment: '决赛名单' })
-export class MatchLast extends BaseModel {
-  @Column({ type: 'integer', nullable: true, comment: '赛事id' })
-  match_id: number;
-  @Column({ type: 'integer', nullable: true, comment: '用户id' })
-  user_id: number;
-  @Column({ type: 'integer', nullable: true, comment: '报名id' })
-  reg_id: number;
-  @Column({ type: 'integer', nullable: true, comment: '初赛分数' })
-  first_score: number;
-  @Column({ type: 'integer', nullable: true, comment: '是否确认参加决赛', default: '0' })
-  is_confirm: number
-  @Column({ type: 'timestamp without time zone', nullable: true, comment: '决赛开始时间', transformer: { from: value => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : value), to: value => value } })
-  start_time: Date;
-  @Column({ type: 'integer', nullable: true, comment: '顺序' })
-  order_no: number;
-  @Column({ type: 'integer', nullable: true, comment: '总分数' })
-  score: number;
-  @Column({ type: 'jsonb', nullable: true, comment: '分数详情' })
-  score_details: object;
-}

+ 19 - 1
src/entity/match/matchRegistration.entity.ts

@@ -1,6 +1,12 @@
 import { Entity, Column } from 'typeorm';
 import { BaseModel } from '../../frame/BaseModel';
 import dayjs = require('dayjs');
+/**
+ * 选手状态"
+ * 0:待审核: 报名阶段: 报名信息审核中; 初赛阶段:进入初赛,但没有开始比赛; 决赛阶段: 进入决赛,但未确认是否参加决赛;
+ * 1:已通过: 报名阶段: 报名信息已通过审核; 初赛阶段:进入初赛,已通过初赛(有分,且已被选中参加决赛); 决赛阶段: 进入决赛,已确认且完成决赛(确认参加,有分)
+ * 2:已退回: 报名阶段: 报名信息有问题,被主办方退回的状态; 初赛阶段:进入初赛,但未通过初赛(有分,但未被选中参加决赛); 决赛阶段: 进入决赛,但是不参加决赛(不确认)
+ */
 //赛事
 @Entity('matchRegistration', { comment: '赛事报名' })
 export class MatchRegistration extends BaseModel {
@@ -10,7 +16,9 @@ export class MatchRegistration extends BaseModel {
   user_id: number;
   @Column({ type: 'jsonb', nullable: true, comment: '报名信息' })
   info: Array<any>;
-  @Column({ type: 'character varying', nullable: true, comment: '审核状态:默认:0--符合要求:-1--已退回', default: '0' })
+  @Column({ type: 'character varying', nullable: true, comment: '流程状态', default: '0' })
+  ext_status: string;
+  @Column({ type: 'character varying', nullable: true, comment: '选手状态', default: '0' })
   status: string;
   @Column({ type: 'character varying', nullable: true, comment: '项目编号: ${match_id}-${user_id}-${该赛事第x位}' })
   no: string;
@@ -22,4 +30,14 @@ export class MatchRegistration extends BaseModel {
   score: number;
   @Column({ type: 'timestamp without time zone', nullable: true, comment: '初赛开始时间', transformer: { from: value => (value ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : value), to: value => value } })
   start_time: Date;
+
+
+  @Column({ type: 'integer', nullable: true, comment: '是否确认参加决赛', default: '0' })
+  final_confirm: number
+  @Column({ type: 'integer', nullable: true, comment: '决赛顺序' })
+  final_order_no: number;
+  @Column({ type: 'integer', nullable: true, comment: '决赛总分数' })
+  final_score: number;
+  @Column({ type: 'jsonb', nullable: true, comment: '决赛分数详情' })
+  final_score_details: object;
 }

+ 8 - 1
src/error/service.error.ts

@@ -59,8 +59,15 @@ export enum ErrorCode {
   MATCH_USER_HAS_REGISTED = 'MATCH_USER_HAS_REGISTED',
   MATCH_NO_PERSON_TO_EXPORT = 'MATCH_NO_PERSON_TO_EXPORT',
   MATCH_NOT_FOUND = 'MATCH_NOT_FOUND',
-
+  MATCH_EXT_IS_BEGINNING = "MATCH_EXT_IS_BEGINNING",
   DATA_NOT_FOUND = 'DATA_NOT_FOUND',
+  MATCH_REG_NEED_MATCH_ID = "MATCH_REG_NEED_MATCH_ID",
+  MATCH_EXT_DATA_NOT_FOUND = "MATCH_EXT_DATA_NOT_FOUND",
+  MATCH_REG_MATCH_IS_NOT_REG = "MATCH_REG_MATCH_IS_NOT_REG",
+  MATCH_STATUS_ERROR = "MATCH_STATUS_ERROR",
+  MATCH_EXT_STATUS_ERROR = "MATCH_EXT_STATUS_ERROR",
+  MATCH_REG_NOT_FOUND = "MATCH_REG_NOT_FOUND",
+  MATCH_REG_STATUS_ERROR ="MATCH_REG_STATUS_ERROR",
 
   // export
   NO_EXPORT_SETTING = 'NO_EXPORT_SETTING',

+ 73 - 1
src/service/match/matchExt.service.ts

@@ -1,10 +1,82 @@
-import { Provide } from '@midwayjs/core';
+import { Inject, Provide } from '@midwayjs/core';
 import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 import { BaseServiceV2 } from '../../frame/BaseServiceV2';
 import { MatchExt } from '../../entity/match/matchExt.entity';
+import { UserService } from '../system/user.service';
+import { get, pick } from 'lodash';
+import { MatchService } from '../platform/match.service';
+import { MatchRegistrationService } from './matchRegistration.service';
+import { ErrorCode, ServiceError } from '../../error/service.error';
 @Provide()
 export class MatchExtService extends BaseServiceV2 {
   @InjectEntityModel(MatchExt)
   model: Repository<MatchExt>;
+  @Inject()
+  matchService: MatchService;
+  @Inject()
+  matchRegService: MatchRegistrationService;
+  @Inject()
+  userService: UserService;
+  /**获取用户信息的属性 */
+  async getUserProps(id, props) {
+    if (!props) return
+    const user = await this.userService.fetch({ id })
+    if (!user) return
+    const obj = pick(user, props)
+    return obj;
+  }
+  /**
+   * 赛事状态检查
+   * @param id 赛事id
+   * @param status 赛事应处于的状态码
+   */
+  async checkMatchStatus(id, status) {
+    const data = await this.matchService.fetch({ id })
+    if (!data) {
+      throw new ServiceError(ErrorCode.MATCH_NOT_FOUND);
+    }
+    const data_status = get(data, 'status');
+    if (status != data_status) {
+      // 抛出异常: 赛事状态不符合要求.
+      throw new ServiceError(ErrorCode.MATCH_STATUS_ERROR)
+    }
+    return true;
+  }
+
+  /**
+   * 赛事拓展状态检查
+   * @param match_id 赛事拓展id
+   * @param status 赛事拓展应处于的状态码
+   */
+  async checkMatchExtStatus(match_id, status) {
+    const data = await this.fetch({ match_id })
+    if (!data) {
+      throw new ServiceError(ErrorCode.MATCH_NOT_FOUND);
+    }
+    const data_status = get(data, 'status');
+    if (status != data_status) {
+      // 抛出异常: 赛事拓展状态不符合要求.
+      throw new ServiceError(ErrorCode.MATCH_EXT_STATUS_ERROR)
+    }
+    return true;
+  }
+  /**
+   * 赛事报名人员状态检查
+   * @param id 赛事报名id
+   * @param status 赛事报名应处于的状态码
+   */
+  async checkMatchRegStatus(id, status) {
+    const data = await this.matchRegService.fetch({ id })
+    if (!data) {
+      throw new ServiceError(ErrorCode.MATCH_REG_NOT_FOUND);
+    }
+    const data_status = get(data, 'status');
+    if (status != data_status) {
+      // 抛出异常: 赛事报名状态不符合要求.
+      throw new ServiceError(ErrorCode.MATCH_REG_STATUS_ERROR)
+    }
+    return true;
+  }
+
 }

+ 0 - 10
src/service/match/matchLast.service.ts

@@ -1,10 +0,0 @@
-import { Provide } from '@midwayjs/core';
-import { InjectEntityModel } from '@midwayjs/typeorm';
-import { Repository } from 'typeorm';
-import { BaseServiceV2 } from '../../frame/BaseServiceV2';
-import { MatchLast } from '../../entity/match/matchLast.entity';
-@Provide()
-export class MatchLastService extends BaseServiceV2 {
-  @InjectEntityModel(MatchLast)
-  model: Repository<MatchLast>;
-}