This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package org.springblade.modules.auth.controller;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.common.cache.CacheNames;
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springblade.core.redis.cache.BladeRedis;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 验证码控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping(AppConstant.APPLICATION_AUTH_NAME + "/captcha")
|
||||
@Tag(name = "验证码", description = "验证码")
|
||||
public class CaptchaController {
|
||||
|
||||
private final BladeRedis bladeRedis;
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*/
|
||||
@PostMapping("/send")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "发送短信验证码", description = "传入手机号")
|
||||
public R send(@Parameter(description = "手机号", required = true) @RequestParam String phone) {
|
||||
// 验证手机号格式
|
||||
if (StringUtil.isBlank(phone)) {
|
||||
return R.fail("手机号不能为空");
|
||||
}
|
||||
|
||||
if (!phone.matches("^1[3-9]\\d{9}$")) {
|
||||
return R.fail("手机号格式不正确");
|
||||
}
|
||||
|
||||
// 检查是否频繁发送
|
||||
String cacheKey = CacheNames.CAPTCHA_KEY + phone;
|
||||
String existCode = bladeRedis.get(cacheKey);
|
||||
if (StringUtil.isNotBlank(existCode)) {
|
||||
return R.fail("验证码已发送,请稍后再试");
|
||||
}
|
||||
|
||||
// 生成6位随机验证码
|
||||
String code = generateCode(6);
|
||||
|
||||
// 存储验证码到Redis,有效期5分钟
|
||||
bladeRedis.setEx(cacheKey, code, Duration.ofMinutes(5));
|
||||
|
||||
// TODO: 实际项目中应该调用短信服务发送验证码
|
||||
// 这里仅做演示,直接返回验证码(生产环境应该删除)
|
||||
System.out.println("发送验证码到手机号: " + phone + ", 验证码: " + code);
|
||||
|
||||
return R.success("验证码发送成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机验证码
|
||||
*
|
||||
* @param length 验证码长度
|
||||
* @return 验证码
|
||||
*/
|
||||
private String generateCode(int length) {
|
||||
Random random = new Random();
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
code.append(random.nextInt(10));
|
||||
}
|
||||
return code.toString();
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,14 @@ import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.modules.martial.pojo.dto.BatchGenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.GenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO;
|
||||
import org.springblade.modules.martial.service.IMartialJudgeInviteService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -77,4 +80,53 @@ public class MartialJudgeInviteController extends BladeController {
|
||||
return R.data(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成邀请码
|
||||
*/
|
||||
@PostMapping("/generate")
|
||||
@Operation(summary = "生成邀请码", description = "为评委生成邀请码")
|
||||
public R<MartialJudgeInvite> generateInviteCode(@RequestBody GenerateInviteDTO dto) {
|
||||
MartialJudgeInvite invite = judgeInviteService.generateInviteCode(dto);
|
||||
return R.data(invite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成邀请码
|
||||
*/
|
||||
@PostMapping("/generate/batch")
|
||||
@Operation(summary = "批量生成邀请码", description = "为多个评委批量生成邀请码")
|
||||
public R<List<MartialJudgeInvite>> batchGenerateInviteCode(@RequestBody BatchGenerateInviteDTO dto) {
|
||||
List<MartialJudgeInvite> invites = judgeInviteService.batchGenerateInviteCode(dto);
|
||||
return R.data(invites);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成邀请码
|
||||
*/
|
||||
@PutMapping("/regenerate/{inviteId}")
|
||||
@Operation(summary = "重新生成邀请码", description = "重新生成邀请码(旧码失效)")
|
||||
public R<MartialJudgeInvite> regenerateInviteCode(@PathVariable Long inviteId) {
|
||||
MartialJudgeInvite invite = judgeInviteService.regenerateInviteCode(inviteId);
|
||||
return R.data(invite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询评委的邀请码
|
||||
*/
|
||||
@GetMapping("/byJudge")
|
||||
@Operation(summary = "查询评委邀请码", description = "根据评委ID和赛事ID查询邀请码")
|
||||
public R<MartialJudgeInvite> getInviteByJudge(
|
||||
@RequestParam Long competitionId,
|
||||
@RequestParam Long judgeId
|
||||
) {
|
||||
MartialJudgeInvite invite = judgeInviteService.lambdaQuery()
|
||||
.eq(MartialJudgeInvite::getCompetitionId, competitionId)
|
||||
.eq(MartialJudgeInvite::getJudgeId, judgeId)
|
||||
.eq(MartialJudgeInvite::getIsDeleted, 0)
|
||||
.orderByDesc(MartialJudgeInvite::getCreateTime)
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
return R.data(invite);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -118,4 +118,53 @@ public class MartialScheduleArrangeController extends BladeController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调度数据
|
||||
*/
|
||||
@GetMapping("/dispatch-data")
|
||||
@Operation(summary = "获取调度数据", description = "获取指定场地和时间段的调度数据")
|
||||
public R<org.springblade.modules.martial.pojo.vo.DispatchDataVO> getDispatchData(
|
||||
@RequestParam Long competitionId,
|
||||
@RequestParam Long venueId,
|
||||
@RequestParam Integer timeSlotIndex) {
|
||||
try {
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO data =
|
||||
scheduleService.getDispatchData(competitionId, venueId, timeSlotIndex);
|
||||
return R.data(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取调度数据失败", e);
|
||||
return R.fail("获取调度数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整出场顺序
|
||||
*/
|
||||
@PostMapping("/adjust-order")
|
||||
@Operation(summary = "调整出场顺序", description = "调整参赛者的出场顺序")
|
||||
public R adjustOrder(@RequestBody org.springblade.modules.martial.pojo.dto.AdjustOrderDTO dto) {
|
||||
try {
|
||||
boolean success = scheduleService.adjustOrder(dto);
|
||||
return success ? R.success("顺序调整成功") : R.fail("顺序调整失败");
|
||||
} catch (Exception e) {
|
||||
log.error("调整顺序失败", e);
|
||||
return R.fail("调整顺序失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存调度
|
||||
*/
|
||||
@PostMapping("/save-dispatch")
|
||||
@Operation(summary = "批量保存调度", description = "批量保存调度调整")
|
||||
public R saveDispatch(@RequestBody org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto) {
|
||||
try {
|
||||
boolean success = scheduleService.saveDispatch(dto);
|
||||
return success ? R.success("调度保存成功") : R.fail("调度保存失败");
|
||||
} catch (Exception e) {
|
||||
log.error("保存调度失败", e);
|
||||
return R.fail("保存调度失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 调整出场顺序DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "调整出场顺序DTO")
|
||||
public class AdjustOrderDTO {
|
||||
|
||||
/**
|
||||
* 编排明细ID
|
||||
*/
|
||||
@Schema(description = "编排明细ID")
|
||||
private Long detailId;
|
||||
|
||||
/**
|
||||
* 参赛者记录ID
|
||||
*/
|
||||
@Schema(description = "参赛者记录ID")
|
||||
private Long participantId;
|
||||
|
||||
/**
|
||||
* 调整动作
|
||||
*/
|
||||
@Schema(description = "调整动作(move_up=上移, move_down=下移, swap=交换)")
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 目标顺序(交换时使用)
|
||||
*/
|
||||
@Schema(description = "目标顺序(交换时使用)")
|
||||
private Integer targetOrder;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量生成邀请码DTO
|
||||
*
|
||||
* @author Blade
|
||||
* @since 2025-12-12
|
||||
*/
|
||||
@Data
|
||||
@Schema(description ="批量生成邀请码DTO")
|
||||
public class BatchGenerateInviteDTO {
|
||||
|
||||
@Schema(description = "赛事ID", required = true)
|
||||
private Long competitionId;
|
||||
|
||||
@Schema(description = "评委ID列表", required = true)
|
||||
private List<Long> judgeIds;
|
||||
|
||||
@Schema(description = "角色:judge-普通评委,chief_judge-裁判长")
|
||||
private String role = "judge";
|
||||
|
||||
@Schema(description = "过期天数(默认30天)")
|
||||
private Integer expireDays = 30;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 调度数据查询DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "调度数据查询DTO")
|
||||
public class DispatchDataDTO {
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 场地ID
|
||||
*/
|
||||
@Schema(description = "场地ID")
|
||||
private Long venueId;
|
||||
|
||||
/**
|
||||
* 时间段索引
|
||||
*/
|
||||
@Schema(description = "时间段索引(0=第1天上午,1=第1天下午...)")
|
||||
private Integer timeSlotIndex;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生成邀请码DTO
|
||||
*
|
||||
* @author Blade
|
||||
* @since 2025-12-12
|
||||
*/
|
||||
@Data
|
||||
@Schema(description ="生成邀请码DTO")
|
||||
public class GenerateInviteDTO {
|
||||
|
||||
@Schema(description = "赛事ID", required = true)
|
||||
private Long competitionId;
|
||||
|
||||
@Schema(description = "评委ID", required = true)
|
||||
private Long judgeId;
|
||||
|
||||
@Schema(description = "角色:judge-普通评委,chief_judge-裁判长", required = true)
|
||||
private String role;
|
||||
|
||||
@Schema(description = "分配场地ID(普通评委必填)")
|
||||
private Long venueId;
|
||||
|
||||
@Schema(description = "分配项目列表(JSON数组字符串)")
|
||||
private String projects;
|
||||
|
||||
@Schema(description = "过期天数(默认30天)")
|
||||
private Integer expireDays = 30;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保存调度DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "保存调度DTO")
|
||||
public class SaveDispatchDTO {
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 调整列表
|
||||
*/
|
||||
@Schema(description = "调整列表")
|
||||
private List<DetailAdjustment> adjustments;
|
||||
|
||||
/**
|
||||
* 明细调整
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "明细调整")
|
||||
public static class DetailAdjustment {
|
||||
|
||||
/**
|
||||
* 编排明细ID
|
||||
*/
|
||||
@Schema(description = "编排明细ID")
|
||||
private Long detailId;
|
||||
|
||||
/**
|
||||
* 参赛者列表
|
||||
*/
|
||||
@Schema(description = "参赛者列表")
|
||||
private List<ParticipantOrder> participants;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 参赛者顺序
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "参赛者顺序")
|
||||
public static class ParticipantOrder {
|
||||
|
||||
/**
|
||||
* 参赛者记录ID
|
||||
*/
|
||||
@Schema(description = "参赛者记录ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 出场顺序
|
||||
*/
|
||||
@Schema(description = "出场顺序")
|
||||
private Integer performanceOrder;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package org.springblade.modules.martial.pojo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 调度数据VO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "调度数据VO")
|
||||
public class DispatchDataVO {
|
||||
|
||||
/**
|
||||
* 分组列表
|
||||
*/
|
||||
@Schema(description = "分组列表")
|
||||
private List<DispatchGroup> groups;
|
||||
|
||||
/**
|
||||
* 调度分组
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "调度分组")
|
||||
public static class DispatchGroup {
|
||||
|
||||
/**
|
||||
* 分组ID
|
||||
*/
|
||||
@Schema(description = "分组ID")
|
||||
private Long groupId;
|
||||
|
||||
/**
|
||||
* 分组名称
|
||||
*/
|
||||
@Schema(description = "分组名称")
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 编排明细ID
|
||||
*/
|
||||
@Schema(description = "编排明细ID")
|
||||
private Long detailId;
|
||||
|
||||
/**
|
||||
* 项目类型
|
||||
*/
|
||||
@Schema(description = "项目类型(1=个人 2=集体)")
|
||||
private Integer projectType;
|
||||
|
||||
/**
|
||||
* 参赛者列表
|
||||
*/
|
||||
@Schema(description = "参赛者列表")
|
||||
private List<DispatchParticipant> participants;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度参赛者
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "调度参赛者")
|
||||
public static class DispatchParticipant {
|
||||
|
||||
/**
|
||||
* 参赛者记录ID
|
||||
*/
|
||||
@Schema(description = "参赛者记录ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 参赛者ID
|
||||
*/
|
||||
@Schema(description = "参赛者ID")
|
||||
private Long participantId;
|
||||
|
||||
/**
|
||||
* 单位名称
|
||||
*/
|
||||
@Schema(description = "单位名称")
|
||||
private String organization;
|
||||
|
||||
/**
|
||||
* 选手姓名
|
||||
*/
|
||||
@Schema(description = "选手姓名")
|
||||
private String playerName;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
/**
|
||||
* 组别
|
||||
*/
|
||||
@Schema(description = "组别")
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 出场顺序
|
||||
*/
|
||||
@Schema(description = "出场顺序")
|
||||
private Integer performanceOrder;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,9 +3,12 @@ package org.springblade.modules.martial.service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.modules.martial.pojo.dto.BatchGenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.GenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -32,4 +35,35 @@ public interface IMartialJudgeInviteService extends IService<MartialJudgeInvite>
|
||||
*/
|
||||
Map<String, Object> getInviteStatistics(Long competitionId);
|
||||
|
||||
/**
|
||||
* 生成邀请码
|
||||
*
|
||||
* @param dto 生成邀请码DTO
|
||||
* @return 邀请记录
|
||||
*/
|
||||
MartialJudgeInvite generateInviteCode(GenerateInviteDTO dto);
|
||||
|
||||
/**
|
||||
* 批量生成邀请码
|
||||
*
|
||||
* @param dto 批量生成邀请码DTO
|
||||
* @return 邀请记录列表
|
||||
*/
|
||||
List<MartialJudgeInvite> batchGenerateInviteCode(BatchGenerateInviteDTO dto);
|
||||
|
||||
/**
|
||||
* 重新生成邀请码
|
||||
*
|
||||
* @param inviteId 邀请记录ID
|
||||
* @return 新的邀请记录
|
||||
*/
|
||||
MartialJudgeInvite regenerateInviteCode(Long inviteId);
|
||||
|
||||
/**
|
||||
* 生成唯一邀请码
|
||||
*
|
||||
* @return 邀请码
|
||||
*/
|
||||
String generateUniqueInviteCode();
|
||||
|
||||
}
|
||||
|
||||
@@ -49,4 +49,27 @@ public interface IMartialScheduleService extends IService<MartialSchedule> {
|
||||
*/
|
||||
boolean moveScheduleGroup(MoveScheduleGroupDTO dto);
|
||||
|
||||
/**
|
||||
* 获取调度数据
|
||||
* @param competitionId 赛事ID
|
||||
* @param venueId 场地ID
|
||||
* @param timeSlotIndex 时间段索引
|
||||
* @return 调度数据
|
||||
*/
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO getDispatchData(Long competitionId, Long venueId, Integer timeSlotIndex);
|
||||
|
||||
/**
|
||||
* 调整出场顺序
|
||||
* @param dto 调整请求数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean adjustOrder(org.springblade.modules.martial.pojo.dto.AdjustOrderDTO dto);
|
||||
|
||||
/**
|
||||
* 批量保存调度
|
||||
* @param dto 保存调度数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveDispatch(org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,24 +2,34 @@ package org.springblade.modules.martial.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.modules.martial.pojo.dto.BatchGenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.GenerateInviteDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
import org.springblade.modules.martial.mapper.MartialJudgeInviteMapper;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO;
|
||||
import org.springblade.modules.martial.service.IMartialJudgeInviteService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* JudgeInvite 服务实现类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MartialJudgeInviteServiceImpl extends ServiceImpl<MartialJudgeInviteMapper, MartialJudgeInvite> implements IMartialJudgeInviteService {
|
||||
|
||||
@@ -64,4 +74,126 @@ public class MartialJudgeInviteServiceImpl extends ServiceImpl<MartialJudgeInvit
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MartialJudgeInvite generateInviteCode(GenerateInviteDTO dto) {
|
||||
// 1. 检查是否已存在有效邀请码
|
||||
MartialJudgeInvite existInvite = this.getOne(
|
||||
Wrappers.<MartialJudgeInvite>lambdaQuery()
|
||||
.eq(MartialJudgeInvite::getCompetitionId, dto.getCompetitionId())
|
||||
.eq(MartialJudgeInvite::getJudgeId, dto.getJudgeId())
|
||||
.eq(MartialJudgeInvite::getStatus, 1)
|
||||
.eq(MartialJudgeInvite::getIsDeleted, 0)
|
||||
.gt(MartialJudgeInvite::getExpireTime, LocalDateTime.now())
|
||||
);
|
||||
|
||||
if (existInvite != null) {
|
||||
throw new ServiceException("该评委已有有效邀请码:" + existInvite.getInviteCode());
|
||||
}
|
||||
|
||||
// 2. 生成唯一邀请码
|
||||
String inviteCode = generateUniqueInviteCode();
|
||||
|
||||
// 3. 创建邀请记录
|
||||
MartialJudgeInvite invite = new MartialJudgeInvite();
|
||||
invite.setCompetitionId(dto.getCompetitionId());
|
||||
invite.setJudgeId(dto.getJudgeId());
|
||||
invite.setInviteCode(inviteCode);
|
||||
invite.setRole(dto.getRole());
|
||||
invite.setVenueId(dto.getVenueId());
|
||||
invite.setProjects(dto.getProjects());
|
||||
invite.setExpireTime(LocalDateTime.now().plusDays(dto.getExpireDays()));
|
||||
invite.setIsUsed(0);
|
||||
invite.setStatus(1);
|
||||
|
||||
boolean success = this.save(invite);
|
||||
if (!success) {
|
||||
throw new ServiceException("生成邀请码失败");
|
||||
}
|
||||
|
||||
log.info("为评委{}生成邀请码成功:{}", dto.getJudgeId(), inviteCode);
|
||||
return invite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateUniqueInviteCode() {
|
||||
String inviteCode;
|
||||
int maxRetry = 10;
|
||||
int retry = 0;
|
||||
|
||||
do {
|
||||
// 生成6位随机字符串(大写字母+数字)
|
||||
inviteCode = UUID.randomUUID().toString()
|
||||
.replaceAll("-", "")
|
||||
.substring(0, 6)
|
||||
.toUpperCase();
|
||||
|
||||
// 检查是否重复
|
||||
long count = this.count(
|
||||
Wrappers.<MartialJudgeInvite>lambdaQuery()
|
||||
.eq(MartialJudgeInvite::getInviteCode, inviteCode)
|
||||
.eq(MartialJudgeInvite::getIsDeleted, 0)
|
||||
);
|
||||
|
||||
if (count == 0) {
|
||||
return inviteCode;
|
||||
}
|
||||
|
||||
retry++;
|
||||
} while (retry < maxRetry);
|
||||
|
||||
throw new ServiceException("生成邀请码失败,请重试");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MartialJudgeInvite> batchGenerateInviteCode(BatchGenerateInviteDTO dto) {
|
||||
List<MartialJudgeInvite> invites = new ArrayList<>();
|
||||
List<String> failedJudges = new ArrayList<>();
|
||||
|
||||
for (Long judgeId : dto.getJudgeIds()) {
|
||||
try {
|
||||
GenerateInviteDTO generateDto = new GenerateInviteDTO();
|
||||
generateDto.setCompetitionId(dto.getCompetitionId());
|
||||
generateDto.setJudgeId(judgeId);
|
||||
generateDto.setRole(dto.getRole());
|
||||
generateDto.setExpireDays(dto.getExpireDays());
|
||||
|
||||
MartialJudgeInvite invite = generateInviteCode(generateDto);
|
||||
invites.add(invite);
|
||||
} catch (Exception e) {
|
||||
log.warn("为评委{}生成邀请码失败:{}", judgeId, e.getMessage());
|
||||
failedJudges.add(judgeId.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (!failedJudges.isEmpty()) {
|
||||
log.info("批量生成完成,失败的评委ID:{}", String.join(",", failedJudges));
|
||||
}
|
||||
|
||||
return invites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MartialJudgeInvite regenerateInviteCode(Long inviteId) {
|
||||
// 1. 查询原邀请记录
|
||||
MartialJudgeInvite oldInvite = this.getById(inviteId);
|
||||
if (oldInvite == null) {
|
||||
throw new ServiceException("邀请记录不存在");
|
||||
}
|
||||
|
||||
// 2. 禁用旧邀请码
|
||||
oldInvite.setStatus(0);
|
||||
this.updateById(oldInvite);
|
||||
|
||||
// 3. 生成新邀请码
|
||||
GenerateInviteDTO dto = new GenerateInviteDTO();
|
||||
dto.setCompetitionId(oldInvite.getCompetitionId());
|
||||
dto.setJudgeId(oldInvite.getJudgeId());
|
||||
dto.setRole(oldInvite.getRole());
|
||||
dto.setVenueId(oldInvite.getVenueId());
|
||||
dto.setProjects(oldInvite.getProjects());
|
||||
|
||||
log.info("重新生成邀请码,旧邀请码:{},评委ID:{}", oldInvite.getInviteCode(), oldInvite.getJudgeId());
|
||||
return generateInviteCode(dto);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -451,4 +451,158 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springblade.modules.martial.pojo.vo.DispatchDataVO getDispatchData(Long competitionId, Long venueId, Integer timeSlotIndex) {
|
||||
// 1. 查询指定场地和时间段的编排明细
|
||||
List<MartialScheduleDetail> details = scheduleDetailMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleDetail>()
|
||||
.eq("competition_id", competitionId)
|
||||
.eq("venue_id", venueId)
|
||||
.eq("time_slot_index", timeSlotIndex)
|
||||
.eq("is_deleted", 0)
|
||||
.orderByAsc("sort_order")
|
||||
);
|
||||
|
||||
if (details.isEmpty()) {
|
||||
// 返回空数据
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO vo = new org.springblade.modules.martial.pojo.vo.DispatchDataVO();
|
||||
vo.setGroups(new ArrayList<>());
|
||||
return vo;
|
||||
}
|
||||
|
||||
// 2. 构建返回数据
|
||||
List<org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchGroup> groups = new ArrayList<>();
|
||||
|
||||
for (MartialScheduleDetail detail : details) {
|
||||
// 查询分组信息
|
||||
MartialScheduleGroup group = scheduleGroupMapper.selectById(detail.getScheduleGroupId());
|
||||
if (group == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查询该明细下的所有参赛者
|
||||
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleParticipant>()
|
||||
.eq("schedule_detail_id", detail.getId())
|
||||
.eq("is_deleted", 0)
|
||||
.orderByAsc("performance_order")
|
||||
);
|
||||
|
||||
// 转换为VO
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchGroup groupVO =
|
||||
new org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchGroup();
|
||||
groupVO.setGroupId(group.getId());
|
||||
groupVO.setGroupName(group.getGroupName());
|
||||
groupVO.setDetailId(detail.getId());
|
||||
groupVO.setProjectType(group.getProjectType());
|
||||
|
||||
List<org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchParticipant> participantVOs =
|
||||
participants.stream().map(p -> {
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchParticipant pVO =
|
||||
new org.springblade.modules.martial.pojo.vo.DispatchDataVO.DispatchParticipant();
|
||||
pVO.setId(p.getId());
|
||||
pVO.setParticipantId(p.getParticipantId());
|
||||
pVO.setOrganization(p.getOrganization());
|
||||
pVO.setPlayerName(p.getPlayerName());
|
||||
pVO.setProjectName(p.getProjectName());
|
||||
pVO.setCategory(p.getCategory());
|
||||
pVO.setPerformanceOrder(p.getPerformanceOrder());
|
||||
return pVO;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
groupVO.setParticipants(participantVOs);
|
||||
groups.add(groupVO);
|
||||
}
|
||||
|
||||
org.springblade.modules.martial.pojo.vo.DispatchDataVO result =
|
||||
new org.springblade.modules.martial.pojo.vo.DispatchDataVO();
|
||||
result.setGroups(groups);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean adjustOrder(org.springblade.modules.martial.pojo.dto.AdjustOrderDTO dto) {
|
||||
// 1. 查询当前参赛者
|
||||
MartialScheduleParticipant current = scheduleParticipantMapper.selectById(dto.getParticipantId());
|
||||
if (current == null) {
|
||||
throw new RuntimeException("参赛者不存在");
|
||||
}
|
||||
|
||||
// 2. 查询同一明细下的所有参赛者
|
||||
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleParticipant>()
|
||||
.eq("schedule_detail_id", dto.getDetailId())
|
||||
.eq("is_deleted", 0)
|
||||
.orderByAsc("performance_order")
|
||||
);
|
||||
|
||||
// 3. 根据动作调整顺序
|
||||
int currentIndex = -1;
|
||||
for (int i = 0; i < participants.size(); i++) {
|
||||
if (participants.get(i).getId().equals(dto.getParticipantId())) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentIndex == -1) {
|
||||
throw new RuntimeException("未找到参赛者");
|
||||
}
|
||||
|
||||
// 4. 执行调整
|
||||
if ("move_up".equals(dto.getAction())) {
|
||||
// 上移
|
||||
if (currentIndex == 0) {
|
||||
throw new RuntimeException("已经是第一个,无法上移");
|
||||
}
|
||||
// 交换位置
|
||||
Collections.swap(participants, currentIndex, currentIndex - 1);
|
||||
} else if ("move_down".equals(dto.getAction())) {
|
||||
// 下移
|
||||
if (currentIndex == participants.size() - 1) {
|
||||
throw new RuntimeException("已经是最后一个,无法下移");
|
||||
}
|
||||
// 交换位置
|
||||
Collections.swap(participants, currentIndex, currentIndex + 1);
|
||||
} else if ("swap".equals(dto.getAction())) {
|
||||
// 交换到指定位置
|
||||
if (dto.getTargetOrder() == null || dto.getTargetOrder() < 1 || dto.getTargetOrder() > participants.size()) {
|
||||
throw new RuntimeException("目标顺序无效");
|
||||
}
|
||||
int targetIndex = dto.getTargetOrder() - 1;
|
||||
Collections.swap(participants, currentIndex, targetIndex);
|
||||
}
|
||||
|
||||
// 5. 更新所有参赛者的顺序
|
||||
for (int i = 0; i < participants.size(); i++) {
|
||||
MartialScheduleParticipant p = participants.get(i);
|
||||
p.setPerformanceOrder(i + 1);
|
||||
scheduleParticipantMapper.updateById(p);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveDispatch(org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto) {
|
||||
if (dto.getAdjustments() == null || dto.getAdjustments().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 批量更新所有参赛者的出场顺序
|
||||
for (org.springblade.modules.martial.pojo.dto.SaveDispatchDTO.DetailAdjustment adjustment : dto.getAdjustments()) {
|
||||
for (org.springblade.modules.martial.pojo.dto.SaveDispatchDTO.ParticipantOrder po : adjustment.getParticipants()) {
|
||||
MartialScheduleParticipant participant = scheduleParticipantMapper.selectById(po.getId());
|
||||
if (participant != null) {
|
||||
participant.setPerformanceOrder(po.getPerformanceOrder());
|
||||
scheduleParticipantMapper.updateById(participant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.modules.system.pojo.dto.UserRegisterDTO;
|
||||
import org.springblade.modules.system.pojo.entity.User;
|
||||
import org.springblade.modules.system.excel.UserExcel;
|
||||
import org.springblade.modules.system.excel.UserImporter;
|
||||
@@ -304,4 +305,63 @@ public class UserController {
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
@ApiOperationSupport(order = 19)
|
||||
@Operation(summary = "用户注册", description = "传入UserRegisterDTO")
|
||||
public R register(@RequestBody UserRegisterDTO registerDTO) {
|
||||
// 验证验证码
|
||||
String code = registerDTO.getCode();
|
||||
String cacheKey = CacheNames.CAPTCHA_KEY + registerDTO.getPhone();
|
||||
String cachedCode = bladeRedis.get(cacheKey);
|
||||
|
||||
// 支持万能验证码888888用于测试
|
||||
if (!"888888".equals(code)) {
|
||||
if (StringUtil.isBlank(cachedCode)) {
|
||||
return R.fail("验证码已过期,请重新获取");
|
||||
}
|
||||
if (!code.equals(cachedCode)) {
|
||||
return R.fail("验证码错误");
|
||||
}
|
||||
}
|
||||
|
||||
// 验证账号是否已存在
|
||||
User existUser = userService.userByAccount(registerDTO.getTenantId(), registerDTO.getAccount());
|
||||
if (existUser != null) {
|
||||
return R.fail("账号已存在");
|
||||
}
|
||||
|
||||
// 验证手机号是否已注册
|
||||
QueryWrapper<User> phoneQuery = new QueryWrapper<>();
|
||||
phoneQuery.eq("phone", registerDTO.getPhone());
|
||||
phoneQuery.eq("is_deleted", 0);
|
||||
User phoneUser = userService.getOne(phoneQuery);
|
||||
if (phoneUser != null) {
|
||||
return R.fail("手机号已注册");
|
||||
}
|
||||
|
||||
// 构建User对象
|
||||
User user = new User();
|
||||
user.setTenantId(registerDTO.getTenantId());
|
||||
user.setUserType(registerDTO.getUserType());
|
||||
user.setAccount(registerDTO.getAccount());
|
||||
user.setPassword(registerDTO.getPassword());
|
||||
user.setRealName(registerDTO.getRealName());
|
||||
user.setName(registerDTO.getRealName());
|
||||
user.setPhone(registerDTO.getPhone());
|
||||
user.setSex(registerDTO.getSex());
|
||||
|
||||
// 注册用户
|
||||
boolean result = userService.registerUser(user);
|
||||
|
||||
// 删除验证码缓存
|
||||
if (!"888888".equals(code)) {
|
||||
bladeRedis.del(cacheKey);
|
||||
}
|
||||
|
||||
return R.status(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.springblade.modules.system.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户注册DTO
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "用户注册对象")
|
||||
public class UserRegisterDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 用户平台
|
||||
*/
|
||||
@Schema(description = "用户平台")
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
@Schema(description = "账号")
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 真名
|
||||
*/
|
||||
@Schema(description = "真实姓名")
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
@Schema(description = "性别")
|
||||
private Integer sex;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@Schema(description = "验证码")
|
||||
private String code;
|
||||
|
||||
}
|
||||
@@ -368,10 +368,16 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
throw new OAuth2Exception("租户信息错误!");
|
||||
}
|
||||
user.setRealName(user.getName());
|
||||
user.setRoleId(StringPool.MINUS_ONE);
|
||||
// 为新注册用户分配默认的"用户"角色
|
||||
user.setRoleId("1123598816738675202");
|
||||
user.setDeptId(StringPool.MINUS_ONE);
|
||||
user.setPostId(StringPool.MINUS_ONE);
|
||||
return this.submit(user);
|
||||
// 前端已经MD5加密,后端使用hex方法再次加密(与登录验证保持一致)
|
||||
if (Func.isNotEmpty(user.getPassword())) {
|
||||
user.setPassword(DigestUtil.hex(user.getPassword()));
|
||||
}
|
||||
// 直接保存,不调用submit避免重复加密
|
||||
return save(user) && submitUserDept(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -240,6 +240,7 @@ blade:
|
||||
- /blade-desk/notice/submit
|
||||
- /blade-flow/model/submit
|
||||
- /blade-develop/datasource/submit
|
||||
- /blade-system/user/register
|
||||
#安全框架配置
|
||||
secure:
|
||||
#严格模式
|
||||
@@ -252,7 +253,8 @@ blade:
|
||||
- /blade-test/**
|
||||
- /blade-spare/**
|
||||
- /blade-device/**
|
||||
- /**
|
||||
- /blade-system/user/register
|
||||
- /blade-auth/captcha/send
|
||||
#授权认证配置
|
||||
auth:
|
||||
- method: ALL
|
||||
|
||||
Reference in New Issue
Block a user