12 KiB
12 KiB
评委邀请码生成方案 - 实施指南
实施日期: 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
核心实现:
-
生成唯一邀请码:
// 6位随机字符串(大写字母+数字) String inviteCode = UUID.randomUUID().toString() .replaceAll("-", "") .substring(0, 6) .toUpperCase(); -
检查重复:
// 检查邀请码是否已存在 long count = this.count( Wrappers.<MartialJudgeInvite>lambdaQuery() .eq(MartialJudgeInvite::getInviteCode, inviteCode) .eq(MartialJudgeInvite::getIsDeleted, 0) ); -
防止重复生成:
// 检查评委是否已有有效邀请码 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天有效期
解决: 使用"重新生成"功能
📈 后续优化建议
短期优化(可选)
-
邀请码格式优化
- 添加前缀(如:WS-ABC123)
- 区分角色(J-评委,C-裁判长)
-
批量导出
- 导出Excel:评委信息+邀请码
- 生成PDF邀请函
-
统计报表
- 邀请码使用率
- 过期邀请码数量
长期优化(可选)
-
短信/邮件发送
- 集成短信服务
- 自动发送邀请码
-
二维码生成
- 生成邀请二维码
- 扫码直接登录
-
邀请码管理
- 批量禁用
- 批量延期
📞 技术支持
代码位置
| 文件 | 路径 |
|---|---|
| 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小时
下一步
- 启动后端服务
- 使用Postman测试接口
- 前端集成(如需要)
- 联调测试
- 上线部署
祝您实施顺利! 🚀
如有问题,请查看代码注释或联系技术支持。