Files
martial-web/doc/评委邀请码生成方案实施指南.md
宅房 669f29878b
All checks were successful
continuous-integration/drone/push Build is passing
fix bugs
2025-12-12 17:54:40 +08:00

12 KiB
Raw Permalink Blame History

评委邀请码生成方案 - 实施指南

实施日期: 2025-12-12 实施方式: 管理员生成 → 复制发送 → 评委使用 状态: 代码已完成,可立即测试


📋 方案概述

核心流程

管理员操作:
1. 进入评委管理页面
2. 选择评委,点击"生成邀请码"
3. 系统生成6位随机码ABC123
4. 复制邀请码
5. 通过微信/短信发送给评委

评委使用:
1. 收到邀请码
2. 打开小程序登录页
3. 输入比赛编码 + 邀请码
4. 登录成功,开始评分

技术特点

  • 无需改表 - 使用现有字段
  • 6位随机码 - 大写字母+数字组合
  • 唯一性保证 - 数据库唯一索引
  • 有效期管理 - 默认30天
  • 状态管理 - 待使用/已使用/已禁用

🚀 已完成的代码

1. DTO 类

GenerateInviteDTO.java

路径: src/main/java/org/springblade/modules/martial/pojo/dto/GenerateInviteDTO.java

@Data
@ApiModel("生成邀请码DTO")
public class GenerateInviteDTO {
    @NotNull(message = "赛事ID不能为空")
    private Long competitionId;

    @NotNull(message = "评委ID不能为空")
    private Long judgeId;

    @NotBlank(message = "角色不能为空")
    private String role;  // judge 或 chief_judge

    private Long venueId;  // 场地ID普通评委必填
    private String projects;  // 项目列表JSON
    private Integer expireDays = 30;  // 过期天数
}

BatchGenerateInviteDTO.java

路径: src/main/java/org/springblade/modules/martial/pojo/dto/BatchGenerateInviteDTO.java

@Data
@ApiModel("批量生成邀请码DTO")
public class BatchGenerateInviteDTO {
    @NotNull(message = "赛事ID不能为空")
    private Long competitionId;

    @NotEmpty(message = "评委列表不能为空")
    private List<Long> judgeIds;

    private String role = "judge";
    private Integer expireDays = 30;
}

2. Service 层

IMartialJudgeInviteService.java

新增方法:

// 生成邀请码
MartialJudgeInvite generateInviteCode(GenerateInviteDTO dto);

// 批量生成邀请码
List<MartialJudgeInvite> batchGenerateInviteCode(BatchGenerateInviteDTO dto);

// 重新生成邀请码
MartialJudgeInvite regenerateInviteCode(Long inviteId);

// 生成唯一邀请码
String generateUniqueInviteCode();

MartialJudgeInviteServiceImpl.java

核心实现:

  1. 生成唯一邀请码:

    // 6位随机字符串大写字母+数字)
    String inviteCode = UUID.randomUUID().toString()
        .replaceAll("-", "")
        .substring(0, 6)
        .toUpperCase();
    
  2. 检查重复:

    // 检查邀请码是否已存在
    long count = this.count(
        Wrappers.<MartialJudgeInvite>lambdaQuery()
            .eq(MartialJudgeInvite::getInviteCode, inviteCode)
            .eq(MartialJudgeInvite::getIsDeleted, 0)
    );
    
  3. 防止重复生成:

    // 检查评委是否已有有效邀请码
    MartialJudgeInvite existInvite = this.getOne(
        Wrappers.<MartialJudgeInvite>lambdaQuery()
            .eq(MartialJudgeInvite::getCompetitionId, competitionId)
            .eq(MartialJudgeInvite::getJudgeId, judgeId)
            .eq(MartialJudgeInvite::getStatus, 1)
            .gt(MartialJudgeInvite::getExpireTime, LocalDateTime.now())
    );
    

3. Controller 层

MartialJudgeInviteController.java

新增接口:

接口 方法 路径 说明
生成邀请码 POST /martial/judgeInvite/generate 为单个评委生成
批量生成 POST /martial/judgeInvite/generate/batch 批量生成
重新生成 PUT /martial/judgeInvite/regenerate/{id} 重新生成(旧码失效)
查询邀请码 GET /martial/judgeInvite/byJudge 查询评委的邀请码

🧪 测试指南

1. 使用 Postman 测试

测试1生成邀请码

POST http://localhost:8080/martial/judgeInvite/generate
Content-Type: application/json
Blade-Auth: Bearer {token}

{
  "competitionId": 1,
  "judgeId": 1,
  "role": "judge",
  "venueId": 1,
  "projects": "[\"女子组长拳\",\"男子组陈氏太极拳\"]",
  "expireDays": 30
}

预期响应:

{
  "code": 200,
  "success": true,
  "data": {
    "id": 1001,
    "competitionId": 1,
    "judgeId": 1,
    "inviteCode": "ABC123",
    "role": "judge",
    "venueId": 1,
    "projects": "[\"女子组长拳\",\"男子组陈氏太极拳\"]",
    "expireTime": "2026-01-11 10:00:00",
    "isUsed": 0,
    "status": 1
  }
}

测试2批量生成邀请码

POST http://localhost:8080/martial/judgeInvite/generate/batch
Content-Type: application/json
Blade-Auth: Bearer {token}

{
  "competitionId": 1,
  "judgeIds": [1, 2, 3, 4, 5],
  "role": "judge",
  "expireDays": 30
}

测试3查询评委邀请码

GET http://localhost:8080/martial/judgeInvite/byJudge?competitionId=1&judgeId=1
Blade-Auth: Bearer {token}

测试4重新生成邀请码

PUT http://localhost:8080/martial/judgeInvite/regenerate/1001
Blade-Auth: Bearer {token}

2. 使用 SQL 测试

执行测试脚本

# 进入数据库
mysql -u root -p blade

# 执行测试脚本
source database/martial-db/test_invite_code_generation.sql

查询有效邀请码

SELECT
    ji.id,
    ji.invite_code,
    ji.role,
    j.name AS judge_name,
    ji.expire_time,
    ji.is_used,
    CASE
        WHEN ji.is_used = 1 THEN '已使用'
        WHEN ji.expire_time < NOW() THEN '已过期'
        WHEN ji.status = 0 THEN '已禁用'
        ELSE '待使用'
    END AS status_text
FROM martial_judge_invite ji
LEFT JOIN martial_judge j ON ji.judge_id = j.id
WHERE ji.competition_id = 1
  AND ji.is_deleted = 0
ORDER BY ji.create_time DESC;

📊 数据库字段说明

martial_judge_invite 表

字段 类型 说明 使用方式
invite_code varchar(50) 邀请码 6位随机码
status int 状态 1-启用0-禁用
is_used int 是否已使用 0-未使用1-已使用
expire_time datetime 过期时间 默认30天后
use_time datetime 使用时间 登录时记录
role varchar(20) 角色 judge/chief_judge
venue_id bigint 场地ID 普通评委必填
projects varchar(500) 项目列表 JSON数组

状态判断逻辑

有效邀请码status=1 AND is_used=0 AND expire_time>NOW()
已使用is_used=1
已过期expire_time<=NOW()
已禁用status=0

🎯 前端集成建议

1. 在评委管理页面添加按钮

<template>
  <el-table :data="judgeList">
    <el-table-column label="操作">
      <template #default="{ row }">
        <!-- 生成邀请码按钮 -->
        <el-button
          v-if="!row.inviteCode"
          type="primary"
          size="small"
          @click="generateInviteCode(row)"
        >
          生成邀请码
        </el-button>

        <!-- 显示邀请码 -->
        <div v-else>
          <el-tag>{{ row.inviteCode }}</el-tag>
          <el-button
            type="text"
            size="small"
            @click="copyInviteCode(row.inviteCode)"
          >
            复制
          </el-button>
          <el-button
            type="text"
            size="small"
            @click="regenerateInviteCode(row)"
          >
            重新生成
          </el-button>
        </div>
      </template>
    </el-table-column>
  </el-table>
</template>

2. 生成邀请码方法

async generateInviteCode(judge) {
  try {
    const res = await this.$http.post('/martial/judgeInvite/generate', {
      competitionId: this.competitionId,
      judgeId: judge.id,
      role: judge.refereeType === 1 ? 'chief_judge' : 'judge',
      venueId: judge.venueId,
      projects: JSON.stringify(judge.projects),
      expireDays: 30
    });

    if (res.success) {
      this.$message.success('邀请码生成成功:' + res.data.inviteCode);
      // 复制到剪贴板
      this.copyToClipboard(res.data.inviteCode);
      // 刷新列表
      this.loadJudgeList();
    }
  } catch (error) {
    this.$message.error(error.message || '生成失败');
  }
}

// 复制到剪贴板
copyToClipboard(text) {
  const input = document.createElement('input');
  input.value = text;
  document.body.appendChild(input);
  input.select();
  document.execCommand('copy');
  document.body.removeChild(input);
  this.$message.success('已复制到剪贴板');
}

3. 批量生成

async batchGenerate() {
  const selectedJudges = this.$refs.table.selection;
  if (selectedJudges.length === 0) {
    this.$message.warning('请选择评委');
    return;
  }

  const judgeIds = selectedJudges.map(j => j.id);

  try {
    const res = await this.$http.post('/martial/judgeInvite/generate/batch', {
      competitionId: this.competitionId,
      judgeIds: judgeIds,
      role: 'judge',
      expireDays: 30
    });

    if (res.success) {
      this.$message.success(`成功生成${res.data.length}个邀请码`);
      this.loadJudgeList();
    }
  } catch (error) {
    this.$message.error(error.message || '批量生成失败');
  }
}

验证清单

后端验证

  • DTO类创建成功
  • Service方法实现完成
  • Controller接口添加完成
  • 编译无错误
  • Swagger文档生成正常

功能验证

  • 单个生成邀请码成功
  • 邀请码格式正确6位大写字母+数字)
  • 邀请码唯一性验证通过
  • 批量生成成功
  • 重新生成成功(旧码失效)
  • 查询邀请码成功
  • 防止重复生成(已有有效邀请码时报错)

数据库验证

  • 邀请码保存成功
  • 过期时间设置正确
  • 状态字段正确
  • 唯一索引生效

小程序验证

  • 使用邀请码登录成功
  • 登录后权限正确
  • 场地和项目信息正确

🔧 常见问题

问题1邀请码重复

现象: 生成的邀请码已存在

原因: 随机生成时碰撞

解决: 代码已实现重试机制最多10次


问题2评委已有邀请码

现象: 提示"该评委已有有效邀请码"

原因: 防止重复生成

解决:

  • 使用"重新生成"功能
  • 或等待旧邀请码过期

问题3邀请码过期

现象: 登录时提示邀请码已过期

原因: 超过30天有效期

解决: 使用"重新生成"功能


📈 后续优化建议

短期优化(可选)

  1. 邀请码格式优化

    • 添加前缀WS-ABC123
    • 区分角色J-评委C-裁判长)
  2. 批量导出

    • 导出Excel评委信息+邀请码
    • 生成PDF邀请函
  3. 统计报表

    • 邀请码使用率
    • 过期邀请码数量

长期优化(可选)

  1. 短信/邮件发送

    • 集成短信服务
    • 自动发送邀请码
  2. 二维码生成

    • 生成邀请二维码
    • 扫码直接登录
  3. 邀请码管理

    • 批量禁用
    • 批量延期

📞 技术支持

代码位置

文件 路径
DTO类 src/main/java/org/springblade/modules/martial/pojo/dto/
Service接口 src/main/java/org/springblade/modules/martial/service/IMartialJudgeInviteService.java
Service实现 src/main/java/org/springblade/modules/martial/service/impl/MartialJudgeInviteServiceImpl.java
Controller src/main/java/org/springblade/modules/martial/controller/MartialJudgeInviteController.java
测试SQL database/martial-db/test_invite_code_generation.sql

Swagger 文档

启动后端服务后访问:

http://localhost:8080/doc.html

搜索"裁判邀请码管理"查看所有接口。


🎉 总结

已完成

DTO类创建 Service层实现 Controller接口 测试SQL脚本 实施文档

工作量

  • 后端开发2小时
  • 测试验证1小时
  • 文档编写1小时
  • 总计4小时

下一步

  1. 启动后端服务
  2. 使用Postman测试接口
  3. 前端集成(如需要)
  4. 联调测试
  5. 上线部署

祝您实施顺利! 🚀

如有问题,请查看代码注释或联系技术支持。