This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package org.springblade.job.processor;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.powerjob.worker.core.processor.ProcessResult;
|
||||
import tech.powerjob.worker.core.processor.TaskContext;
|
||||
import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
|
||||
import tech.powerjob.worker.log.OmsLogger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 赛程自动编排定时任务处理器
|
||||
* <p>
|
||||
* 任务说明:
|
||||
* 1. 每10分钟执行一次自动编排
|
||||
* 2. 查询所有未锁定的赛事(schedule_status != 2)
|
||||
* 3. 对每个赛事执行自动编排算法
|
||||
* 4. 更新编排状态和最后编排时间
|
||||
* <p>
|
||||
* 配置方式:
|
||||
* 在PowerJob控制台配置定时任务:
|
||||
* - 任务名称: 赛程自动编排
|
||||
* - 执行类型: BASIC
|
||||
* - 处理器: org.springblade.job.processor.ScheduleAutoArrangeProcessor
|
||||
* - Cron表达式: 0 * /10 * * * ? (每10分钟执行一次)
|
||||
* - 最大实例数: 1 (避免并发)
|
||||
*
|
||||
* @author BladeX
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ScheduleAutoArrangeProcessor implements BasicProcessor {
|
||||
|
||||
private final IMartialScheduleArrangeService scheduleArrangeService;
|
||||
|
||||
@Override
|
||||
public ProcessResult process(TaskContext context) {
|
||||
OmsLogger omsLogger = context.getOmsLogger();
|
||||
omsLogger.info("赛程自动编排任务开始执行...");
|
||||
|
||||
try {
|
||||
// 1. 查询所有未锁定的赛事
|
||||
List<Long> unlockedCompetitions = scheduleArrangeService.getUnlockedCompetitions();
|
||||
|
||||
if (unlockedCompetitions.isEmpty()) {
|
||||
omsLogger.info("没有需要编排的赛事");
|
||||
return new ProcessResult(true, "没有需要编排的赛事");
|
||||
}
|
||||
|
||||
omsLogger.info("找到 {} 个需要编排的赛事: {}", unlockedCompetitions.size(), unlockedCompetitions);
|
||||
|
||||
// 2. 对每个赛事执行自动编排
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
|
||||
for (Long competitionId : unlockedCompetitions) {
|
||||
try {
|
||||
omsLogger.info("开始编排赛事: {}", competitionId);
|
||||
scheduleArrangeService.autoArrange(competitionId);
|
||||
successCount++;
|
||||
omsLogger.info("赛事 {} 编排成功", competitionId);
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
String error = String.format("赛事 %d 编排失败: %s", competitionId, e.getMessage());
|
||||
omsLogger.error(error, e);
|
||||
errorMsg.append(error).append("; ");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 返回执行结果
|
||||
String result = String.format("自动编排任务完成. 成功: %d, 失败: %d. %s",
|
||||
successCount, failCount, errorMsg.toString());
|
||||
|
||||
omsLogger.info(result);
|
||||
|
||||
// 如果有失败的,返回部分成功
|
||||
if (failCount > 0) {
|
||||
return new ProcessResult(true, result);
|
||||
}
|
||||
|
||||
return new ProcessResult(true, result);
|
||||
|
||||
} catch (Exception e) {
|
||||
String errorMsg = "赛程自动编排任务执行失败: " + e.getMessage();
|
||||
omsLogger.error(errorMsg, e);
|
||||
log.error(errorMsg, e);
|
||||
return new ProcessResult(false, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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.entity.MartialAthlete;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialAthleteVO;
|
||||
import org.springblade.modules.martial.service.IMartialAthleteService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -37,12 +38,12 @@ public class MartialAthleteController extends BladeController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页列表
|
||||
* 分页列表(包含关联字段)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页列表", description = "分页查询")
|
||||
public R<IPage<MartialAthlete>> list(MartialAthlete athlete, Query query) {
|
||||
IPage<MartialAthlete> pages = athleteService.page(Condition.getPage(query), Condition.getQueryWrapper(athlete));
|
||||
public R<IPage<MartialAthleteVO>> list(MartialAthlete athlete, Query query) {
|
||||
IPage<MartialAthleteVO> pages = athleteService.selectAthleteVOPage(Condition.getPage(query), athlete);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package org.springblade.modules.martial.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesAttachment;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesChapter;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesContent;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialCompetitionRulesVO;
|
||||
import org.springblade.modules.martial.service.IMartialCompetitionRulesService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 赛事规程 控制器
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/martial/competition/rules")
|
||||
@Tag(name = "赛事规程管理", description = "赛事规程管理接口")
|
||||
public class MartialCompetitionRulesController extends BladeController {
|
||||
|
||||
private final IMartialCompetitionRulesService rulesService;
|
||||
|
||||
/**
|
||||
* 获取赛事规程(小程序端)
|
||||
*/
|
||||
@GetMapping("")
|
||||
@Operation(summary = "获取赛事规程", description = "小程序端获取规程信息")
|
||||
public R<MartialCompetitionRulesVO> getRules(@RequestParam Long competitionId) {
|
||||
MartialCompetitionRulesVO rules = rulesService.getRulesByCompetitionId(competitionId);
|
||||
return R.data(rules);
|
||||
}
|
||||
|
||||
// ==================== 附件管理 ====================
|
||||
|
||||
/**
|
||||
* 获取附件列表
|
||||
*/
|
||||
@GetMapping("/attachment/list")
|
||||
@Operation(summary = "获取附件列表", description = "管理端获取附件列表")
|
||||
public R<List<MartialCompetitionRulesAttachment>> getAttachmentList(@RequestParam Long competitionId) {
|
||||
List<MartialCompetitionRulesAttachment> list = rulesService.getAttachmentList(competitionId);
|
||||
return R.data(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存附件
|
||||
*/
|
||||
@PostMapping("/attachment/save")
|
||||
@Operation(summary = "保存附件", description = "新增或修改附件")
|
||||
public R saveAttachment(@RequestBody MartialCompetitionRulesAttachment attachment) {
|
||||
return R.status(rulesService.saveAttachment(attachment));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除附件
|
||||
*/
|
||||
@PostMapping("/attachment/remove")
|
||||
@Operation(summary = "删除附件", description = "传入附件ID")
|
||||
public R removeAttachment(@RequestParam Long id) {
|
||||
return R.status(rulesService.removeAttachment(id));
|
||||
}
|
||||
|
||||
// ==================== 章节管理 ====================
|
||||
|
||||
/**
|
||||
* 获取章节列表
|
||||
*/
|
||||
@GetMapping("/chapter/list")
|
||||
@Operation(summary = "获取章节列表", description = "管理端获取章节列表")
|
||||
public R<List<MartialCompetitionRulesChapter>> getChapterList(@RequestParam Long competitionId) {
|
||||
List<MartialCompetitionRulesChapter> list = rulesService.getChapterList(competitionId);
|
||||
return R.data(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存章节
|
||||
*/
|
||||
@PostMapping("/chapter/save")
|
||||
@Operation(summary = "保存章节", description = "新增或修改章节")
|
||||
public R saveChapter(@RequestBody MartialCompetitionRulesChapter chapter) {
|
||||
return R.status(rulesService.saveChapter(chapter));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除章节
|
||||
*/
|
||||
@PostMapping("/chapter/remove")
|
||||
@Operation(summary = "删除章节", description = "传入章节ID")
|
||||
public R removeChapter(@RequestParam Long id) {
|
||||
return R.status(rulesService.removeChapter(id));
|
||||
}
|
||||
|
||||
// ==================== 章节内容管理 ====================
|
||||
|
||||
/**
|
||||
* 获取章节内容列表
|
||||
*/
|
||||
@GetMapping("/content/list")
|
||||
@Operation(summary = "获取章节内容列表", description = "管理端获取章节内容")
|
||||
public R<List<MartialCompetitionRulesContent>> getContentList(@RequestParam Long chapterId) {
|
||||
List<MartialCompetitionRulesContent> list = rulesService.getContentList(chapterId);
|
||||
return R.data(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存章节内容
|
||||
*/
|
||||
@PostMapping("/content/save")
|
||||
@Operation(summary = "保存章节内容", description = "新增或修改章节内容")
|
||||
public R saveContent(@RequestBody MartialCompetitionRulesContent content) {
|
||||
return R.status(rulesService.saveContent(content));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存章节内容
|
||||
*/
|
||||
@PostMapping("/content/batch-save")
|
||||
@Operation(summary = "批量保存章节内容", description = "批量保存章节内容")
|
||||
public R batchSaveContents(@RequestBody Map<String, Object> params) {
|
||||
Long chapterId = Long.valueOf(params.get("chapterId").toString());
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> contents = (List<String>) params.get("contents");
|
||||
return R.status(rulesService.batchSaveContents(chapterId, contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除章节内容
|
||||
*/
|
||||
@PostMapping("/content/remove")
|
||||
@Operation(summary = "删除章节内容", description = "传入内容ID")
|
||||
public R removeContent(@RequestParam Long id) {
|
||||
return R.status(rulesService.removeContent(id));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,12 @@ 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.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.Map;
|
||||
|
||||
/**
|
||||
* 裁判邀请码 控制器
|
||||
*
|
||||
@@ -37,12 +40,12 @@ public class MartialJudgeInviteController extends BladeController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页列表
|
||||
* 分页列表(关联裁判信息)
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "分页列表", description = "分页查询")
|
||||
public R<IPage<MartialJudgeInvite>> list(MartialJudgeInvite judgeInvite, Query query) {
|
||||
IPage<MartialJudgeInvite> pages = judgeInviteService.page(Condition.getPage(query), Condition.getQueryWrapper(judgeInvite));
|
||||
@Operation(summary = "分页列表", description = "分页查询,关联裁判信息")
|
||||
public R<IPage<MartialJudgeInviteVO>> list(MartialJudgeInvite judgeInvite, Query query) {
|
||||
IPage<MartialJudgeInviteVO> pages = judgeInviteService.selectJudgeInvitePage(judgeInvite, query);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
@@ -64,4 +67,14 @@ public class MartialJudgeInviteController extends BladeController {
|
||||
return R.status(judgeInviteService.removeByIds(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取邀请统计信息
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "邀请统计", description = "传入赛事ID")
|
||||
public R<Map<String, Object>> statistics(@RequestParam Long competitionId) {
|
||||
Map<String, Object> statistics = judgeInviteService.getInviteStatistics(competitionId);
|
||||
return R.data(statistics);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class MartialMiniController extends BladeController {
|
||||
}
|
||||
|
||||
// 4. 验证比赛编码
|
||||
if (!competition.getCode().equals(dto.getMatchCode())) {
|
||||
if (!competition.getCompetitionCode().equals(dto.getMatchCode())) {
|
||||
return R.fail("比赛编码不匹配");
|
||||
}
|
||||
|
||||
@@ -111,13 +111,13 @@ public class MartialMiniController extends BladeController {
|
||||
vo.setToken(token);
|
||||
vo.setUserRole("chief_judge".equals(invite.getRole()) ? "admin" : "pub");
|
||||
vo.setMatchId(competition.getId());
|
||||
vo.setMatchName(competition.getName());
|
||||
vo.setMatchTime(competition.getStartTime() != null ?
|
||||
competition.getStartTime().toString() : "");
|
||||
vo.setMatchName(competition.getCompetitionName());
|
||||
vo.setMatchTime(competition.getCompetitionStartTime() != null ?
|
||||
competition.getCompetitionStartTime().toString() : "");
|
||||
vo.setJudgeId(judge.getId());
|
||||
vo.setJudgeName(judge.getName());
|
||||
vo.setVenueId(venue != null ? venue.getId() : null);
|
||||
vo.setVenueName(venue != null ? venue.getName() : null);
|
||||
vo.setVenueName(venue != null ? venue.getVenueName() : null);
|
||||
vo.setProjects(projects);
|
||||
|
||||
return R.data(vo);
|
||||
@@ -210,7 +210,7 @@ public class MartialMiniController extends BladeController {
|
||||
projects = projectList.stream().map(project -> {
|
||||
MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
|
||||
info.setProjectId(project.getId());
|
||||
info.setProjectName(project.getName());
|
||||
info.setProjectName(project.getProjectName());
|
||||
return info;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
@@ -228,7 +228,7 @@ public class MartialMiniController extends BladeController {
|
||||
projects = projectList.stream().map(project -> {
|
||||
MiniLoginVO.ProjectInfo info = new MiniLoginVO.ProjectInfo();
|
||||
info.setProjectId(project.getId());
|
||||
info.setProjectName(project.getName());
|
||||
info.setProjectName(project.getProjectName());
|
||||
return info;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.springblade.modules.martial.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.modules.martial.pojo.dto.MoveScheduleGroupDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.SaveScheduleDraftDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO;
|
||||
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
|
||||
import org.springblade.modules.martial.service.IMartialScheduleService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 赛程自动编排 控制器
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/martial/schedule")
|
||||
@Tag(name = "赛程编排管理", description = "赛程自动编排接口")
|
||||
public class MartialScheduleArrangeController extends BladeController {
|
||||
|
||||
private final IMartialScheduleArrangeService scheduleArrangeService;
|
||||
private final IMartialScheduleService scheduleService;
|
||||
|
||||
/**
|
||||
* 获取编排结果
|
||||
*/
|
||||
@GetMapping("/result")
|
||||
@Operation(summary = "获取编排结果", description = "传入赛事ID")
|
||||
public R<ScheduleResultDTO> getScheduleResult(@RequestParam Long competitionId) {
|
||||
try {
|
||||
ScheduleResultDTO result = scheduleService.getScheduleResult(competitionId);
|
||||
return R.data(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取编排结果失败, competitionId: {}", competitionId, e);
|
||||
return R.fail("获取编排结果失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存编排草稿
|
||||
*/
|
||||
@PostMapping("/save-draft")
|
||||
@Operation(summary = "保存编排草稿", description = "传入编排草稿数据")
|
||||
public R saveDraftSchedule(@RequestBody SaveScheduleDraftDTO dto) {
|
||||
try {
|
||||
boolean success = scheduleService.saveDraftSchedule(dto);
|
||||
return success ? R.success("草稿保存成功") : R.fail("草稿保存失败");
|
||||
} catch (Exception e) {
|
||||
log.error("保存编排草稿失败", e);
|
||||
return R.fail("保存编排草稿失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成编排并锁定
|
||||
*/
|
||||
@PostMapping("/save-and-lock")
|
||||
@Operation(summary = "完成编排并锁定", description = "传入赛事ID")
|
||||
public R saveAndLock(@RequestBody SaveScheduleDraftDTO dto) {
|
||||
try {
|
||||
// 获取当前登录用户
|
||||
BladeUser user = AuthUtil.getUser();
|
||||
String userId = user != null ? user.getUserName() : "system";
|
||||
|
||||
boolean success = scheduleService.saveAndLockSchedule(dto.getCompetitionId());
|
||||
if (success) {
|
||||
// 调用原有的锁定逻辑
|
||||
scheduleArrangeService.saveAndLock(dto.getCompetitionId(), userId);
|
||||
return R.success("编排已完成并锁定");
|
||||
} else {
|
||||
return R.fail("编排锁定失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("保存并锁定编排失败", e);
|
||||
return R.fail("保存并锁定编排失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发自动编排(测试用)
|
||||
*/
|
||||
@PostMapping("/auto-arrange")
|
||||
@Operation(summary = "手动触发自动编排", description = "传入赛事ID,仅用于测试")
|
||||
public R autoArrange(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Long competitionId = Long.valueOf(String.valueOf(params.get("competitionId")));
|
||||
scheduleArrangeService.autoArrange(competitionId);
|
||||
return R.success("自动编排完成");
|
||||
} catch (Exception e) {
|
||||
log.error("自动编排失败", e);
|
||||
return R.fail("自动编排失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动赛程分组
|
||||
*/
|
||||
@PostMapping("/move-group")
|
||||
@Operation(summary = "移动赛程分组", description = "将分组移动到指定场地和时间段")
|
||||
public R moveGroup(@RequestBody MoveScheduleGroupDTO dto) {
|
||||
try {
|
||||
boolean success = scheduleService.moveScheduleGroup(dto);
|
||||
return success ? R.success("分组移动成功") : R.fail("分组移动失败");
|
||||
} catch (Exception e) {
|
||||
log.error("移动分组失败", e);
|
||||
return R.fail("移动分组失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ 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.SaveScheduleDraftDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialSchedule;
|
||||
import org.springblade.modules.martial.service.IMartialScheduleService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialAthlete;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialAthleteVO;
|
||||
|
||||
/**
|
||||
* Athlete Mapper 接口
|
||||
@@ -10,4 +13,13 @@ import org.springblade.modules.martial.pojo.entity.MartialAthlete;
|
||||
*/
|
||||
public interface MartialAthleteMapper extends BaseMapper<MartialAthlete> {
|
||||
|
||||
/**
|
||||
* 分页查询参赛选手(包含关联字段)
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param athlete 查询条件
|
||||
* @return 参赛选手VO分页数据
|
||||
*/
|
||||
IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, @Param("athlete") MartialAthlete athlete);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,4 +2,44 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialAthleteMapper">
|
||||
|
||||
<!-- 分页查询参赛选手(包含关联字段) -->
|
||||
<select id="selectAthleteVOPage" resultType="org.springblade.modules.martial.pojo.vo.MartialAthleteVO">
|
||||
SELECT
|
||||
a.*,
|
||||
c.competition_name as competitionName,
|
||||
p.project_name as projectName
|
||||
FROM martial_athlete a
|
||||
LEFT JOIN martial_competition c ON a.competition_id = c.id AND c.is_deleted = 0
|
||||
LEFT JOIN martial_project p ON a.project_id = p.id AND p.is_deleted = 0
|
||||
WHERE a.is_deleted = 0
|
||||
<if test="athlete.competitionId != null">
|
||||
AND a.competition_id = #{athlete.competitionId}
|
||||
</if>
|
||||
<if test="athlete.projectId != null">
|
||||
AND a.project_id = #{athlete.projectId}
|
||||
</if>
|
||||
<if test="athlete.playerName != null and athlete.playerName != ''">
|
||||
AND a.player_name LIKE CONCAT('%', #{athlete.playerName}, '%')
|
||||
</if>
|
||||
<if test="athlete.playerNo != null and athlete.playerNo != ''">
|
||||
AND a.player_no = #{athlete.playerNo}
|
||||
</if>
|
||||
<if test="athlete.gender != null">
|
||||
AND a.gender = #{athlete.gender}
|
||||
</if>
|
||||
<if test="athlete.organization != null and athlete.organization != ''">
|
||||
AND a.organization LIKE CONCAT('%', #{athlete.organization}, '%')
|
||||
</if>
|
||||
<if test="athlete.category != null and athlete.category != ''">
|
||||
AND a.category = #{athlete.category}
|
||||
</if>
|
||||
<if test="athlete.registrationStatus != null">
|
||||
AND a.registration_status = #{athlete.registrationStatus}
|
||||
</if>
|
||||
<if test="athlete.competitionStatus != null">
|
||||
AND a.competition_status = #{athlete.competitionStatus}
|
||||
</if>
|
||||
ORDER BY a.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesAttachment;
|
||||
|
||||
/**
|
||||
* 赛事规程附件 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialCompetitionRulesAttachmentMapper extends BaseMapper<MartialCompetitionRulesAttachment> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesChapter;
|
||||
|
||||
/**
|
||||
* 赛事规程章节 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialCompetitionRulesChapterMapper extends BaseMapper<MartialCompetitionRulesChapter> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesContent;
|
||||
|
||||
/**
|
||||
* 赛事规程内容 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialCompetitionRulesContentMapper extends BaseMapper<MartialCompetitionRulesContent> {
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO;
|
||||
|
||||
/**
|
||||
* JudgeInvite Mapper 接口
|
||||
@@ -10,4 +13,13 @@ import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
*/
|
||||
public interface MartialJudgeInviteMapper extends BaseMapper<MartialJudgeInvite> {
|
||||
|
||||
/**
|
||||
* 分页查询裁判邀请列表(关联裁判信息)
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param judgeInvite 查询条件
|
||||
* @return 裁判邀请VO分页列表
|
||||
*/
|
||||
IPage<MartialJudgeInviteVO> selectJudgeInvitePage(IPage<MartialJudgeInviteVO> page, @Param("judgeInvite") MartialJudgeInvite judgeInvite);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,4 +2,99 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialJudgeInviteMapper">
|
||||
|
||||
<!-- 裁判邀请VO结果映射 -->
|
||||
<resultMap id="judgeInviteVOResultMap" type="org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO">
|
||||
<id column="id" property="id"/>
|
||||
<result column="competition_id" property="competitionId"/>
|
||||
<result column="judge_id" property="judgeId"/>
|
||||
<result column="invite_code" property="inviteCode"/>
|
||||
<result column="role" property="role"/>
|
||||
<result column="venue_id" property="venueId"/>
|
||||
<result column="projects" property="projects"/>
|
||||
<result column="expire_time" property="expireTime"/>
|
||||
<result column="is_used" property="isUsed"/>
|
||||
<result column="use_time" property="useTime"/>
|
||||
<result column="device_info" property="deviceInfo"/>
|
||||
<result column="login_ip" property="loginIp"/>
|
||||
<result column="access_token" property="accessToken"/>
|
||||
<result column="token_expire_time" property="tokenExpireTime"/>
|
||||
<result column="invite_status" property="inviteStatus"/>
|
||||
<result column="invite_time" property="inviteTime"/>
|
||||
<result column="reply_time" property="replyTime"/>
|
||||
<result column="reply_note" property="replyNote"/>
|
||||
<result column="contact_phone" property="contactPhone"/>
|
||||
<result column="contact_email" property="contactEmail"/>
|
||||
<result column="invite_message" property="inviteMessage"/>
|
||||
<result column="cancel_reason" property="cancelReason"/>
|
||||
<!-- 关联的裁判信息 -->
|
||||
<result column="judge_name" property="judgeName"/>
|
||||
<result column="judge_level" property="judgeLevel"/>
|
||||
<!-- 关联的赛事信息 -->
|
||||
<result column="competition_name" property="competitionName"/>
|
||||
<!-- 基础字段 -->
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_dept" property="createDept"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 分页查询裁判邀请列表(关联裁判信息) -->
|
||||
<select id="selectJudgeInvitePage" resultMap="judgeInviteVOResultMap">
|
||||
SELECT
|
||||
ji.id,
|
||||
ji.competition_id,
|
||||
ji.judge_id,
|
||||
ji.invite_code,
|
||||
ji.role,
|
||||
ji.venue_id,
|
||||
ji.projects,
|
||||
ji.expire_time,
|
||||
ji.is_used,
|
||||
ji.use_time,
|
||||
ji.device_info,
|
||||
ji.login_ip,
|
||||
ji.access_token,
|
||||
ji.token_expire_time,
|
||||
ji.invite_status,
|
||||
ji.invite_time,
|
||||
ji.reply_time,
|
||||
ji.reply_note,
|
||||
ji.contact_phone,
|
||||
ji.contact_email,
|
||||
ji.invite_message,
|
||||
ji.cancel_reason,
|
||||
ji.create_user,
|
||||
ji.create_dept,
|
||||
ji.create_time,
|
||||
ji.update_user,
|
||||
ji.update_time,
|
||||
ji.status,
|
||||
ji.is_deleted,
|
||||
j.name AS judge_name,
|
||||
j.level AS judge_level,
|
||||
c.competition_name
|
||||
FROM
|
||||
martial_judge_invite ji
|
||||
LEFT JOIN martial_judge j ON ji.judge_id = j.id
|
||||
LEFT JOIN martial_competition c ON ji.competition_id = c.id
|
||||
WHERE
|
||||
ji.is_deleted = 0
|
||||
<if test="judgeInvite.competitionId != null">
|
||||
AND ji.competition_id = #{judgeInvite.competitionId}
|
||||
</if>
|
||||
<if test="judgeInvite.inviteStatus != null">
|
||||
AND ji.invite_status = #{judgeInvite.inviteStatus}
|
||||
</if>
|
||||
<if test="judgeInvite.judgeName != null and judgeInvite.judgeName != ''">
|
||||
AND j.name LIKE CONCAT('%', #{judgeInvite.judgeName}, '%')
|
||||
</if>
|
||||
<if test="judgeInvite.judgeLevel != null and judgeInvite.judgeLevel != ''">
|
||||
AND j.level = #{judgeInvite.judgeLevel}
|
||||
</if>
|
||||
ORDER BY ji.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialScheduleDetail;
|
||||
|
||||
/**
|
||||
* 赛程编排明细 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialScheduleDetailMapper extends BaseMapper<MartialScheduleDetail> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialScheduleDetailMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialScheduleGroup;
|
||||
import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 赛程编排分组 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialScheduleGroupMapper extends BaseMapper<MartialScheduleGroup> {
|
||||
|
||||
/**
|
||||
* 查询赛程编排的完整详情(一次性JOIN查询,优化性能)
|
||||
*
|
||||
* @param competitionId 比赛ID
|
||||
* @return 分组详情列表
|
||||
*/
|
||||
List<ScheduleGroupDetailVO> selectScheduleGroupDetails(@Param("competitionId") Long competitionId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialScheduleGroupMapper">
|
||||
|
||||
<!-- 优化的一次性JOIN查询,获取完整的赛程编排数据 -->
|
||||
<select id="selectScheduleGroupDetails" resultType="org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO">
|
||||
SELECT
|
||||
g.id AS groupId,
|
||||
g.group_name AS groupName,
|
||||
g.category AS category,
|
||||
g.project_type AS projectType,
|
||||
g.total_teams AS totalTeams,
|
||||
g.total_participants AS totalParticipants,
|
||||
g.display_order AS displayOrder,
|
||||
d.id AS detailId,
|
||||
d.venue_id AS venueId,
|
||||
d.venue_name AS venueName,
|
||||
d.time_slot AS timeSlot,
|
||||
d.time_slot_index AS timeSlotIndex,
|
||||
p.id AS participantId,
|
||||
p.organization AS organization,
|
||||
p.check_in_status AS checkInStatus,
|
||||
p.schedule_status AS scheduleStatus,
|
||||
p.performance_order AS performanceOrder
|
||||
FROM
|
||||
martial_schedule_group g
|
||||
LEFT JOIN
|
||||
martial_schedule_detail d ON g.id = d.schedule_group_id AND d.is_deleted = 0
|
||||
LEFT JOIN
|
||||
martial_schedule_participant p ON g.id = p.schedule_group_id AND p.is_deleted = 0
|
||||
WHERE
|
||||
g.competition_id = #{competitionId}
|
||||
AND g.is_deleted = 0
|
||||
ORDER BY
|
||||
g.display_order ASC,
|
||||
p.performance_order ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialScheduleParticipant;
|
||||
|
||||
/**
|
||||
* 赛程编排参赛者关联 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialScheduleParticipantMapper extends BaseMapper<MartialScheduleParticipant> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialScheduleParticipantMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialScheduleStatus;
|
||||
|
||||
/**
|
||||
* 赛程编排状态 Mapper 接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface MartialScheduleStatusMapper extends BaseMapper<MartialScheduleStatus> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.modules.martial.mapper.MartialScheduleStatusMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 竞赛分组DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "竞赛分组DTO")
|
||||
public class CompetitionGroupDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 分组ID
|
||||
*/
|
||||
@Schema(description = "分组ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 分组标题
|
||||
*/
|
||||
@Schema(description = "分组标题")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 类型:集体/单人/双人
|
||||
*/
|
||||
@Schema(description = "类型:集体/单人/双人")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 队伍数量
|
||||
*/
|
||||
@Schema(description = "队伍数量")
|
||||
private String count;
|
||||
|
||||
/**
|
||||
* 分组编号
|
||||
*/
|
||||
@Schema(description = "分组编号")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 当前所属场地ID
|
||||
*/
|
||||
@Schema(description = "当前所属场地ID")
|
||||
private Long venueId;
|
||||
|
||||
/**
|
||||
* 场地名称
|
||||
*/
|
||||
@Schema(description = "场地名称")
|
||||
private String venueName;
|
||||
|
||||
/**
|
||||
* 时间段
|
||||
*/
|
||||
@Schema(description = "时间段")
|
||||
private String timeSlot;
|
||||
|
||||
/**
|
||||
* 时间段索引
|
||||
*/
|
||||
@Schema(description = "时间段索引")
|
||||
private Integer timeSlotIndex;
|
||||
|
||||
/**
|
||||
* 参赛人员列表
|
||||
*/
|
||||
@Schema(description = "参赛人员列表")
|
||||
private List<ParticipantDTO> participants;
|
||||
|
||||
}
|
||||
@@ -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 = "移动赛程分组请求")
|
||||
public class MoveScheduleGroupDTO {
|
||||
|
||||
/**
|
||||
* 分组ID
|
||||
*/
|
||||
@Schema(description = "分组ID")
|
||||
private Long groupId;
|
||||
|
||||
/**
|
||||
* 目标场地ID
|
||||
*/
|
||||
@Schema(description = "目标场地ID")
|
||||
private Long targetVenueId;
|
||||
|
||||
/**
|
||||
* 目标时间段索引
|
||||
*/
|
||||
@Schema(description = "目标时间段索引(0=第1天上午,1=第1天下午,2=第2天上午...)")
|
||||
private Integer targetTimeSlotIndex;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 参赛人员DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "参赛人员DTO")
|
||||
public class ParticipantDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 参赛人员ID
|
||||
*/
|
||||
@Schema(description = "参赛人员ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 学校/单位
|
||||
*/
|
||||
@Schema(description = "学校/单位")
|
||||
private String schoolUnit;
|
||||
|
||||
/**
|
||||
* 状态:未签到/已签到/异常
|
||||
*/
|
||||
@Schema(description = "状态:未签到/已签到/异常")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
@Schema(description = "排序")
|
||||
private Integer sortOrder;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 保存编排草稿DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "保存编排草稿DTO")
|
||||
public class SaveScheduleDraftDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 是否为草稿
|
||||
*/
|
||||
@Schema(description = "是否为草稿")
|
||||
private Boolean isDraft;
|
||||
|
||||
/**
|
||||
* 竞赛分组数据
|
||||
*/
|
||||
@Schema(description = "竞赛分组数据")
|
||||
private List<CompetitionGroupDTO> competitionGroups;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springblade.modules.martial.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 赛程编排结果DTO
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "赛程编排结果DTO")
|
||||
public class ScheduleResultDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 是否为草稿状态
|
||||
*/
|
||||
@Schema(description = "是否为草稿状态")
|
||||
private Boolean isDraft;
|
||||
|
||||
/**
|
||||
* 是否已完成编排
|
||||
*/
|
||||
@Schema(description = "是否已完成编排")
|
||||
private Boolean isCompleted;
|
||||
|
||||
/**
|
||||
* 竞赛分组列表
|
||||
*/
|
||||
@Schema(description = "竞赛分组列表")
|
||||
private List<CompetitionGroupDTO> competitionGroups;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
/**
|
||||
* 赛事规程附件实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_competition_rules_attachment")
|
||||
@Schema(description = "赛事规程附件")
|
||||
public class MartialCompetitionRulesAttachment extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
@Schema(description = "文件名称")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件URL
|
||||
*/
|
||||
@Schema(description = "文件URL")
|
||||
private String fileUrl;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
@Schema(description = "文件大小(字节)")
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
@Schema(description = "文件类型(pdf/doc/docx/xls/xlsx等)")
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
@Schema(description = "排序序号")
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 状态(1-启用 0-禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1-启用 0-禁用)")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
/**
|
||||
* 赛事规程章节实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_competition_rules_chapter")
|
||||
@Schema(description = "赛事规程章节")
|
||||
public class MartialCompetitionRulesChapter extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 章节编号
|
||||
*/
|
||||
@Schema(description = "章节编号(如:第一章)")
|
||||
private String chapterNumber;
|
||||
|
||||
/**
|
||||
* 章节标题
|
||||
*/
|
||||
@Schema(description = "章节标题")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
@Schema(description = "排序序号")
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 状态(1-启用 0-禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1-启用 0-禁用)")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
/**
|
||||
* 赛事规程内容实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_competition_rules_content")
|
||||
@Schema(description = "赛事规程内容")
|
||||
public class MartialCompetitionRulesContent extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 章节ID
|
||||
*/
|
||||
@Schema(description = "章节ID")
|
||||
private Long chapterId;
|
||||
|
||||
/**
|
||||
* 规程内容
|
||||
*/
|
||||
@Schema(description = "规程内容")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
@Schema(description = "排序序号")
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 状态(1-启用 0-禁用)
|
||||
*/
|
||||
@Schema(description = "状态(1-启用 0-禁用)")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -115,4 +115,52 @@ public class MartialJudgeInvite extends TenantEntity {
|
||||
@Schema(description = "token过期时间")
|
||||
private LocalDateTime tokenExpireTime;
|
||||
|
||||
/**
|
||||
* 邀请状态(0-待回复,1-已接受,2-已拒绝,3-已取消)
|
||||
*/
|
||||
@Schema(description = "邀请状态")
|
||||
private Integer inviteStatus;
|
||||
|
||||
/**
|
||||
* 邀请时间
|
||||
*/
|
||||
@Schema(description = "邀请时间")
|
||||
private LocalDateTime inviteTime;
|
||||
|
||||
/**
|
||||
* 回复时间
|
||||
*/
|
||||
@Schema(description = "回复时间")
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
/**
|
||||
* 回复备注
|
||||
*/
|
||||
@Schema(description = "回复备注")
|
||||
private String replyNote;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@Schema(description = "联系电话")
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 联系邮箱
|
||||
*/
|
||||
@Schema(description = "联系邮箱")
|
||||
private String contactEmail;
|
||||
|
||||
/**
|
||||
* 邀请消息
|
||||
*/
|
||||
@Schema(description = "邀请消息")
|
||||
private String inviteMessage;
|
||||
|
||||
/**
|
||||
* 取消原因
|
||||
*/
|
||||
@Schema(description = "取消原因")
|
||||
private String cancelReason;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 赛程编排明细实体类(场地时间段分配)
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_schedule_detail")
|
||||
@Schema(description = "赛程编排明细(场地时间段分配)")
|
||||
public class MartialScheduleDetail extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 分组ID
|
||||
*/
|
||||
@Schema(description = "分组ID")
|
||||
private Long scheduleGroupId;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 场地ID
|
||||
*/
|
||||
@Schema(description = "场地ID")
|
||||
private Long venueId;
|
||||
|
||||
/**
|
||||
* 场地名称
|
||||
*/
|
||||
@Schema(description = "场地名称")
|
||||
private String venueName;
|
||||
|
||||
/**
|
||||
* 比赛日期
|
||||
*/
|
||||
@Schema(description = "比赛日期")
|
||||
private LocalDate scheduleDate;
|
||||
|
||||
/**
|
||||
* 时间段(morning/afternoon)
|
||||
*/
|
||||
@Schema(description = "时间段(morning/afternoon)")
|
||||
private String timePeriod;
|
||||
|
||||
/**
|
||||
* 时间点(08:30/13:30)
|
||||
*/
|
||||
@Schema(description = "时间点(08:30/13:30)")
|
||||
private String timeSlot;
|
||||
|
||||
/**
|
||||
* 时间段索引(0=第1天上午,1=第1天下午,2=第2天上午,...)
|
||||
*/
|
||||
@Schema(description = "时间段索引")
|
||||
private Integer timeSlotIndex;
|
||||
|
||||
/**
|
||||
* 预计开始时间
|
||||
*/
|
||||
@Schema(description = "预计开始时间")
|
||||
private LocalDateTime estimatedStartTime;
|
||||
|
||||
/**
|
||||
* 预计结束时间
|
||||
*/
|
||||
@Schema(description = "预计结束时间")
|
||||
private LocalDateTime estimatedEndTime;
|
||||
|
||||
/**
|
||||
* 预计时长(分钟)
|
||||
*/
|
||||
@Schema(description = "预计时长(分钟)")
|
||||
private Integer estimatedDuration;
|
||||
|
||||
/**
|
||||
* 参赛人数
|
||||
*/
|
||||
@Schema(description = "参赛人数")
|
||||
private Integer participantCount;
|
||||
|
||||
/**
|
||||
* 场内顺序
|
||||
*/
|
||||
@Schema(description = "场内顺序")
|
||||
private Integer sortOrder;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
/**
|
||||
* 赛程编排分组实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_schedule_group")
|
||||
@Schema(description = "赛程编排分组")
|
||||
public class MartialScheduleGroup extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 分组名称(如:太极拳男组)
|
||||
*/
|
||||
@Schema(description = "分组名称")
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 项目ID
|
||||
*/
|
||||
@Schema(description = "项目ID")
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
/**
|
||||
* 组别(成年组、少年组等)
|
||||
*/
|
||||
@Schema(description = "组别")
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 项目类型(1=个人 2=集体)
|
||||
*/
|
||||
@Schema(description = "项目类型(1=个人 2=集体)")
|
||||
private Integer projectType;
|
||||
|
||||
/**
|
||||
* 显示顺序(集体项目优先,数字越小越靠前)
|
||||
*/
|
||||
@Schema(description = "显示顺序")
|
||||
private Integer displayOrder;
|
||||
|
||||
/**
|
||||
* 总参赛人数
|
||||
*/
|
||||
@Schema(description = "总参赛人数")
|
||||
private Integer totalParticipants;
|
||||
|
||||
/**
|
||||
* 总队伍数(仅集体项目)
|
||||
*/
|
||||
@Schema(description = "总队伍数")
|
||||
private Integer totalTeams;
|
||||
|
||||
/**
|
||||
* 预计时长(分钟)
|
||||
*/
|
||||
@Schema(description = "预计时长(分钟)")
|
||||
private Integer estimatedDuration;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
/**
|
||||
* 赛程编排参赛者关联实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_schedule_participant")
|
||||
@Schema(description = "赛程编排参赛者关联")
|
||||
public class MartialScheduleParticipant extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 编排明细ID
|
||||
*/
|
||||
@Schema(description = "编排明细ID")
|
||||
private Long scheduleDetailId;
|
||||
|
||||
/**
|
||||
* 分组ID
|
||||
*/
|
||||
@Schema(description = "分组ID")
|
||||
private Long scheduleGroupId;
|
||||
|
||||
/**
|
||||
* 参赛者ID(关联martial_athlete表)
|
||||
*/
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 签到状态:未签到/已签到/异常
|
||||
*/
|
||||
@Schema(description = "签到状态:未签到/已签到/异常")
|
||||
private String checkInStatus;
|
||||
|
||||
/**
|
||||
* 编排状态:draft/completed
|
||||
*/
|
||||
@Schema(description = "编排状态:draft/completed")
|
||||
private String scheduleStatus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.core.tenant.mp.TenantEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 赛程编排状态实体类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("martial_schedule_status")
|
||||
@Schema(description = "赛程编排状态")
|
||||
public class MartialScheduleStatus extends TenantEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID(唯一)
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 编排状态(0=未编排 1=编排中 2=已保存锁定)
|
||||
*/
|
||||
@Schema(description = "编排状态(0=未编排 1=编排中 2=已保存锁定)")
|
||||
private Integer scheduleStatus;
|
||||
|
||||
/**
|
||||
* 最后自动编排时间
|
||||
*/
|
||||
@Schema(description = "最后自动编排时间")
|
||||
private LocalDateTime lastAutoScheduleTime;
|
||||
|
||||
/**
|
||||
* 锁定时间
|
||||
*/
|
||||
@Schema(description = "锁定时间")
|
||||
private LocalDateTime lockedTime;
|
||||
|
||||
/**
|
||||
* 锁定人
|
||||
*/
|
||||
@Schema(description = "锁定人")
|
||||
private String lockedBy;
|
||||
|
||||
/**
|
||||
* 总分组数
|
||||
*/
|
||||
@Schema(description = "总分组数")
|
||||
private Integer totalGroups;
|
||||
|
||||
/**
|
||||
* 总参赛人数
|
||||
*/
|
||||
@Schema(description = "总参赛人数")
|
||||
private Integer totalParticipants;
|
||||
|
||||
}
|
||||
@@ -53,12 +53,6 @@ public class MartialVenue extends TenantEntity {
|
||||
@Schema(description = "场地编码")
|
||||
private String venueCode;
|
||||
|
||||
/**
|
||||
* 场地位置
|
||||
*/
|
||||
@Schema(description = "场地位置")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 容纳人数
|
||||
*/
|
||||
@@ -66,9 +60,21 @@ public class MartialVenue extends TenantEntity {
|
||||
private Integer capacity;
|
||||
|
||||
/**
|
||||
* 设施说明
|
||||
* 位置/地点
|
||||
*/
|
||||
@Schema(description = "设施说明")
|
||||
@Schema(description = "位置")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 场地设施
|
||||
*/
|
||||
@Schema(description = "场地设施")
|
||||
private String facilities;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用,1-启用)
|
||||
*/
|
||||
@Schema(description = "状态")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.pojo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 赛事规程视图对象
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "赛事规程")
|
||||
public class MartialCompetitionRulesVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 赛事ID
|
||||
*/
|
||||
@Schema(description = "赛事ID")
|
||||
private Long competitionId;
|
||||
|
||||
/**
|
||||
* 赛事名称
|
||||
*/
|
||||
@Schema(description = "赛事名称")
|
||||
private String competitionName;
|
||||
|
||||
/**
|
||||
* 附件列表
|
||||
*/
|
||||
@Schema(description = "附件列表")
|
||||
private List<AttachmentVO> attachments;
|
||||
|
||||
/**
|
||||
* 章节列表
|
||||
*/
|
||||
@Schema(description = "章节列表")
|
||||
private List<ChapterVO> chapters;
|
||||
|
||||
/**
|
||||
* 附件视图对象
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "附件信息")
|
||||
public static class AttachmentVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "附件ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "文件名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "文件名称(别名)")
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "文件URL")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "文件URL(别名)")
|
||||
private String fileUrl;
|
||||
|
||||
@Schema(description = "文件大小(字节)")
|
||||
private Long size;
|
||||
|
||||
@Schema(description = "文件大小(别名)")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "文件类型")
|
||||
private String fileType;
|
||||
|
||||
@Schema(description = "上传时间")
|
||||
private String uploadTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 章节视图对象
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "章节信息")
|
||||
public static class ChapterVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "章节ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "章节编号")
|
||||
private String chapterNumber;
|
||||
|
||||
@Schema(description = "章节编号(别名)")
|
||||
private String number;
|
||||
|
||||
@Schema(description = "章节标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "章节标题(别名)")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序序号")
|
||||
private Integer order;
|
||||
|
||||
@Schema(description = "章节内容列表")
|
||||
private List<String> contents;
|
||||
|
||||
@Schema(description = "章节内容列表(别名)")
|
||||
private List<String> items;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.springblade.modules.martial.pojo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 裁判邀请码视图对象
|
||||
* 包含裁判的详细信息
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "裁判邀请码视图对象")
|
||||
public class MartialJudgeInviteVO extends MartialJudgeInvite {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 裁判姓名
|
||||
*/
|
||||
@Schema(description = "裁判姓名")
|
||||
private String judgeName;
|
||||
|
||||
/**
|
||||
* 裁判等级
|
||||
*/
|
||||
@Schema(description = "裁判等级")
|
||||
private String judgeLevel;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@Schema(description = "联系电话")
|
||||
private String contactPhone;
|
||||
|
||||
/**
|
||||
* 联系邮箱
|
||||
*/
|
||||
@Schema(description = "联系邮箱")
|
||||
private String contactEmail;
|
||||
|
||||
/**
|
||||
* 邀请状态(0-待回复,1-已接受,2-已拒绝,3-已取消)
|
||||
*/
|
||||
@Schema(description = "邀请状态")
|
||||
private Integer inviteStatus;
|
||||
|
||||
/**
|
||||
* 邀请时间
|
||||
*/
|
||||
@Schema(description = "邀请时间")
|
||||
private LocalDateTime inviteTime;
|
||||
|
||||
/**
|
||||
* 回复时间
|
||||
*/
|
||||
@Schema(description = "回复时间")
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
/**
|
||||
* 回复备注
|
||||
*/
|
||||
@Schema(description = "回复备注")
|
||||
private String replyNote;
|
||||
|
||||
/**
|
||||
* 赛事名称
|
||||
*/
|
||||
@Schema(description = "赛事名称")
|
||||
private String competitionName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springblade.modules.martial.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 赛程编排分组详情VO(用于优化查询)
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Data
|
||||
public class ScheduleGroupDetailVO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// === 分组信息 ===
|
||||
private Long groupId;
|
||||
private String groupName;
|
||||
private String category;
|
||||
private Integer projectType;
|
||||
private Integer totalTeams;
|
||||
private Integer totalParticipants;
|
||||
private Integer displayOrder;
|
||||
|
||||
// === 编排明细信息 ===
|
||||
private Long detailId;
|
||||
private Long venueId;
|
||||
private String venueName;
|
||||
private String timeSlot;
|
||||
private Integer timeSlotIndex; // 时间段索引(0=第1天上午,1=第1天下午,2=第2天上午,...)
|
||||
|
||||
// === 参赛者信息 ===
|
||||
private Long participantId;
|
||||
private String organization;
|
||||
private String checkInStatus;
|
||||
private String scheduleStatus;
|
||||
private Integer performanceOrder;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.service;
|
||||
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesAttachment;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesChapter;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesContent;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialCompetitionRulesVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 赛事规程服务类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface IMartialCompetitionRulesService {
|
||||
|
||||
/**
|
||||
* 获取赛事规程(小程序端)
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 规程信息
|
||||
*/
|
||||
MartialCompetitionRulesVO getRulesByCompetitionId(Long competitionId);
|
||||
|
||||
/**
|
||||
* 获取附件列表
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 附件列表
|
||||
*/
|
||||
List<MartialCompetitionRulesAttachment> getAttachmentList(Long competitionId);
|
||||
|
||||
/**
|
||||
* 保存附件
|
||||
*
|
||||
* @param attachment 附件信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveAttachment(MartialCompetitionRulesAttachment attachment);
|
||||
|
||||
/**
|
||||
* 删除附件
|
||||
*
|
||||
* @param id 附件ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean removeAttachment(Long id);
|
||||
|
||||
/**
|
||||
* 获取章节列表
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 章节列表
|
||||
*/
|
||||
List<MartialCompetitionRulesChapter> getChapterList(Long competitionId);
|
||||
|
||||
/**
|
||||
* 保存章节
|
||||
*
|
||||
* @param chapter 章节信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveChapter(MartialCompetitionRulesChapter chapter);
|
||||
|
||||
/**
|
||||
* 删除章节
|
||||
*
|
||||
* @param id 章节ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean removeChapter(Long id);
|
||||
|
||||
/**
|
||||
* 获取章节内容列表
|
||||
*
|
||||
* @param chapterId 章节ID
|
||||
* @return 内容列表
|
||||
*/
|
||||
List<MartialCompetitionRulesContent> getContentList(Long chapterId);
|
||||
|
||||
/**
|
||||
* 保存章节内容
|
||||
*
|
||||
* @param content 内容信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveContent(MartialCompetitionRulesContent content);
|
||||
|
||||
/**
|
||||
* 删除章节内容
|
||||
*
|
||||
* @param id 内容ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean removeContent(Long id);
|
||||
|
||||
/**
|
||||
* 批量保存章节内容
|
||||
*
|
||||
* @param chapterId 章节ID
|
||||
* @param contents 内容列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean batchSaveContents(Long chapterId, List<String> contents);
|
||||
|
||||
}
|
||||
@@ -1,7 +1,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.entity.MartialJudgeInvite;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialJudgeInviteVO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JudgeInvite 服务类
|
||||
@@ -10,4 +15,21 @@ import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
|
||||
*/
|
||||
public interface IMartialJudgeInviteService extends IService<MartialJudgeInvite> {
|
||||
|
||||
/**
|
||||
* 分页查询裁判邀请列表(关联裁判信息)
|
||||
*
|
||||
* @param judgeInvite 查询条件
|
||||
* @param query 分页参数
|
||||
* @return 裁判邀请VO分页列表
|
||||
*/
|
||||
IPage<MartialJudgeInviteVO> selectJudgeInvitePage(MartialJudgeInvite judgeInvite, Query query);
|
||||
|
||||
/**
|
||||
* 获取邀请统计信息
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 统计信息
|
||||
*/
|
||||
Map<String, Object> getInviteStatistics(Long competitionId);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 赛程自动编排服务接口
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public interface IMartialScheduleArrangeService {
|
||||
|
||||
/**
|
||||
* 自动编排赛程
|
||||
* @param competitionId 赛事ID
|
||||
*/
|
||||
void autoArrange(Long competitionId);
|
||||
|
||||
/**
|
||||
* 获取未锁定的赛事列表
|
||||
* @return 赛事ID列表
|
||||
*/
|
||||
List<Long> getUnlockedCompetitions();
|
||||
|
||||
/**
|
||||
* 保存并锁定编排
|
||||
* @param competitionId 赛事ID
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
void saveAndLock(Long competitionId, String userId);
|
||||
|
||||
/**
|
||||
* 获取编排结果
|
||||
* @param competitionId 赛事ID
|
||||
* @return 编排数据
|
||||
*/
|
||||
Map<String, Object> getScheduleResult(Long competitionId);
|
||||
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package org.springblade.modules.martial.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.modules.martial.excel.ScheduleExportExcel;
|
||||
import org.springblade.modules.martial.pojo.dto.MoveScheduleGroupDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.SaveScheduleDraftDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialSchedule;
|
||||
|
||||
import java.util.List;
|
||||
@@ -18,4 +21,32 @@ public interface IMartialScheduleService extends IService<MartialSchedule> {
|
||||
*/
|
||||
List<ScheduleExportExcel> exportSchedule(Long competitionId);
|
||||
|
||||
/**
|
||||
* 获取赛程编排结果
|
||||
* @param competitionId 赛事ID
|
||||
* @return 赛程编排结果
|
||||
*/
|
||||
ScheduleResultDTO getScheduleResult(Long competitionId);
|
||||
|
||||
/**
|
||||
* 保存编排草稿
|
||||
* @param dto 编排草稿数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveDraftSchedule(SaveScheduleDraftDTO dto);
|
||||
|
||||
/**
|
||||
* 完成编排并锁定
|
||||
* @param competitionId 赛事ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveAndLockSchedule(Long competitionId);
|
||||
|
||||
/**
|
||||
* 移动赛程分组到指定场地和时间段
|
||||
* @param dto 移动请求数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean moveScheduleGroup(MoveScheduleGroupDTO dto);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.modules.martial.mapper.MartialCompetitionMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialCompetitionRulesAttachmentMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialCompetitionRulesChapterMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialCompetitionRulesContentMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetition;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesAttachment;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesChapter;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialCompetitionRulesContent;
|
||||
import org.springblade.modules.martial.pojo.vo.MartialCompetitionRulesVO;
|
||||
import org.springblade.modules.martial.service.IMartialCompetitionRulesService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 赛事规程服务实现类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MartialCompetitionRulesServiceImpl implements IMartialCompetitionRulesService {
|
||||
|
||||
private final MartialCompetitionMapper competitionMapper;
|
||||
private final MartialCompetitionRulesAttachmentMapper attachmentMapper;
|
||||
private final MartialCompetitionRulesChapterMapper chapterMapper;
|
||||
private final MartialCompetitionRulesContentMapper contentMapper;
|
||||
|
||||
@Override
|
||||
public MartialCompetitionRulesVO getRulesByCompetitionId(Long competitionId) {
|
||||
MartialCompetitionRulesVO vo = new MartialCompetitionRulesVO();
|
||||
vo.setCompetitionId(competitionId);
|
||||
|
||||
// 获取赛事信息
|
||||
MartialCompetition competition = competitionMapper.selectById(competitionId);
|
||||
if (competition != null) {
|
||||
vo.setCompetitionName(competition.getCompetitionName());
|
||||
}
|
||||
|
||||
// 获取附件列表
|
||||
List<MartialCompetitionRulesAttachment> attachments = getAttachmentList(competitionId);
|
||||
List<MartialCompetitionRulesVO.AttachmentVO> attachmentVOList = attachments.stream()
|
||||
.map(this::convertToAttachmentVO)
|
||||
.collect(Collectors.toList());
|
||||
vo.setAttachments(attachmentVOList);
|
||||
|
||||
// 获取章节列表
|
||||
List<MartialCompetitionRulesChapter> chapters = getChapterList(competitionId);
|
||||
List<MartialCompetitionRulesVO.ChapterVO> chapterVOList = new ArrayList<>();
|
||||
for (MartialCompetitionRulesChapter chapter : chapters) {
|
||||
MartialCompetitionRulesVO.ChapterVO chapterVO = convertToChapterVO(chapter);
|
||||
// 获取章节内容
|
||||
List<MartialCompetitionRulesContent> contents = getContentList(chapter.getId());
|
||||
List<String> contentList = contents.stream()
|
||||
.map(MartialCompetitionRulesContent::getContent)
|
||||
.collect(Collectors.toList());
|
||||
chapterVO.setContents(contentList);
|
||||
chapterVO.setItems(contentList); // 别名
|
||||
chapterVOList.add(chapterVO);
|
||||
}
|
||||
vo.setChapters(chapterVOList);
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MartialCompetitionRulesAttachment> getAttachmentList(Long competitionId) {
|
||||
LambdaQueryWrapper<MartialCompetitionRulesAttachment> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialCompetitionRulesAttachment::getCompetitionId, competitionId)
|
||||
.eq(MartialCompetitionRulesAttachment::getStatus, 1)
|
||||
.orderByAsc(MartialCompetitionRulesAttachment::getOrderNum);
|
||||
return attachmentMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveAttachment(MartialCompetitionRulesAttachment attachment) {
|
||||
if (attachment.getId() == null) {
|
||||
return attachmentMapper.insert(attachment) > 0;
|
||||
} else {
|
||||
return attachmentMapper.updateById(attachment) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAttachment(Long id) {
|
||||
return attachmentMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MartialCompetitionRulesChapter> getChapterList(Long competitionId) {
|
||||
LambdaQueryWrapper<MartialCompetitionRulesChapter> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialCompetitionRulesChapter::getCompetitionId, competitionId)
|
||||
.eq(MartialCompetitionRulesChapter::getStatus, 1)
|
||||
.orderByAsc(MartialCompetitionRulesChapter::getOrderNum);
|
||||
return chapterMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveChapter(MartialCompetitionRulesChapter chapter) {
|
||||
if (chapter.getId() == null) {
|
||||
return chapterMapper.insert(chapter) > 0;
|
||||
} else {
|
||||
return chapterMapper.updateById(chapter) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removeChapter(Long id) {
|
||||
// 删除章节下的所有内容
|
||||
LambdaQueryWrapper<MartialCompetitionRulesContent> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialCompetitionRulesContent::getChapterId, id);
|
||||
contentMapper.delete(wrapper);
|
||||
// 删除章节
|
||||
return chapterMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MartialCompetitionRulesContent> getContentList(Long chapterId) {
|
||||
LambdaQueryWrapper<MartialCompetitionRulesContent> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialCompetitionRulesContent::getChapterId, chapterId)
|
||||
.eq(MartialCompetitionRulesContent::getStatus, 1)
|
||||
.orderByAsc(MartialCompetitionRulesContent::getOrderNum);
|
||||
return contentMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveContent(MartialCompetitionRulesContent content) {
|
||||
if (content.getId() == null) {
|
||||
return contentMapper.insert(content) > 0;
|
||||
} else {
|
||||
return contentMapper.updateById(content) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeContent(Long id) {
|
||||
return contentMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean batchSaveContents(Long chapterId, List<String> contents) {
|
||||
// 先删除原有内容
|
||||
LambdaQueryWrapper<MartialCompetitionRulesContent> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialCompetitionRulesContent::getChapterId, chapterId);
|
||||
contentMapper.delete(wrapper);
|
||||
|
||||
// 批量插入新内容
|
||||
for (int i = 0; i < contents.size(); i++) {
|
||||
MartialCompetitionRulesContent content = new MartialCompetitionRulesContent();
|
||||
content.setChapterId(chapterId);
|
||||
content.setContent(contents.get(i));
|
||||
content.setOrderNum(i + 1);
|
||||
content.setStatus(1);
|
||||
contentMapper.insert(content);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为附件VO
|
||||
*/
|
||||
private MartialCompetitionRulesVO.AttachmentVO convertToAttachmentVO(MartialCompetitionRulesAttachment attachment) {
|
||||
MartialCompetitionRulesVO.AttachmentVO vo = new MartialCompetitionRulesVO.AttachmentVO();
|
||||
vo.setId(attachment.getId());
|
||||
vo.setName(attachment.getFileName());
|
||||
vo.setFileName(attachment.getFileName());
|
||||
vo.setUrl(attachment.getFileUrl());
|
||||
vo.setFileUrl(attachment.getFileUrl());
|
||||
vo.setSize(attachment.getFileSize());
|
||||
vo.setFileSize(attachment.getFileSize());
|
||||
vo.setFileType(attachment.getFileType());
|
||||
vo.setUploadTime(DateUtil.format(attachment.getCreateTime(), DateUtil.PATTERN_DATETIME));
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为章节VO
|
||||
*/
|
||||
private MartialCompetitionRulesVO.ChapterVO convertToChapterVO(MartialCompetitionRulesChapter chapter) {
|
||||
MartialCompetitionRulesVO.ChapterVO vo = new MartialCompetitionRulesVO.ChapterVO();
|
||||
vo.setId(chapter.getId());
|
||||
vo.setChapterNumber(chapter.getChapterNumber());
|
||||
vo.setNumber(chapter.getChapterNumber());
|
||||
vo.setTitle(chapter.getTitle());
|
||||
vo.setName(chapter.getTitle());
|
||||
vo.setOrder(chapter.getOrderNum());
|
||||
return vo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
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.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JudgeInvite 服务实现类
|
||||
*
|
||||
@@ -14,4 +23,45 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class MartialJudgeInviteServiceImpl extends ServiceImpl<MartialJudgeInviteMapper, MartialJudgeInvite> implements IMartialJudgeInviteService {
|
||||
|
||||
@Override
|
||||
public IPage<MartialJudgeInviteVO> selectJudgeInvitePage(MartialJudgeInvite judgeInvite, Query query) {
|
||||
IPage<MartialJudgeInviteVO> page = Condition.getPage(query);
|
||||
return baseMapper.selectJudgeInvitePage(page, judgeInvite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getInviteStatistics(Long competitionId) {
|
||||
Map<String, Object> statistics = new HashMap<>();
|
||||
|
||||
LambdaQueryWrapper<MartialJudgeInvite> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialJudgeInvite::getCompetitionId, competitionId);
|
||||
|
||||
// 总邀请数
|
||||
long totalInvites = this.count(wrapper);
|
||||
statistics.put("totalInvites", totalInvites);
|
||||
|
||||
// 待回复数量
|
||||
LambdaQueryWrapper<MartialJudgeInvite> pendingWrapper = new LambdaQueryWrapper<>();
|
||||
pendingWrapper.eq(MartialJudgeInvite::getCompetitionId, competitionId)
|
||||
.eq(MartialJudgeInvite::getInviteStatus, 0);
|
||||
long pendingCount = this.count(pendingWrapper);
|
||||
statistics.put("pendingCount", pendingCount);
|
||||
|
||||
// 已接受数量
|
||||
LambdaQueryWrapper<MartialJudgeInvite> acceptedWrapper = new LambdaQueryWrapper<>();
|
||||
acceptedWrapper.eq(MartialJudgeInvite::getCompetitionId, competitionId)
|
||||
.eq(MartialJudgeInvite::getInviteStatus, 1);
|
||||
long acceptedCount = this.count(acceptedWrapper);
|
||||
statistics.put("acceptedCount", acceptedCount);
|
||||
|
||||
// 已拒绝数量
|
||||
LambdaQueryWrapper<MartialJudgeInvite> rejectedWrapper = new LambdaQueryWrapper<>();
|
||||
rejectedWrapper.eq(MartialJudgeInvite::getCompetitionId, competitionId)
|
||||
.eq(MartialJudgeInvite::getInviteStatus, 2);
|
||||
long rejectedCount = this.count(rejectedWrapper);
|
||||
statistics.put("rejectedCount", rejectedCount);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,933 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.modules.martial.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.modules.martial.mapper.*;
|
||||
import org.springblade.modules.martial.pojo.entity.*;
|
||||
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 赛程自动编排服务实现类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrangeService {
|
||||
|
||||
private final MartialScheduleStatusMapper scheduleStatusMapper;
|
||||
private final MartialScheduleGroupMapper scheduleGroupMapper;
|
||||
private final MartialScheduleDetailMapper scheduleDetailMapper;
|
||||
private final MartialScheduleParticipantMapper scheduleParticipantMapper;
|
||||
private final MartialAthleteMapper athleteMapper;
|
||||
private final MartialCompetitionMapper competitionMapper;
|
||||
private final MartialVenueMapper venueMapper;
|
||||
private final MartialProjectMapper projectMapper;
|
||||
|
||||
@Override
|
||||
public List<Long> getUnlockedCompetitions() {
|
||||
// 查询所有未锁定的赛事(schedule_status != 2)
|
||||
LambdaQueryWrapper<MartialScheduleStatus> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.ne(MartialScheduleStatus::getScheduleStatus, 2)
|
||||
.eq(MartialScheduleStatus::getIsDeleted, 0);
|
||||
|
||||
List<MartialScheduleStatus> statusList = scheduleStatusMapper.selectList(wrapper);
|
||||
return statusList.stream()
|
||||
.map(MartialScheduleStatus::getCompetitionId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void autoArrange(Long competitionId) {
|
||||
log.info("开始自动编排赛程, competitionId: {}", competitionId);
|
||||
|
||||
try {
|
||||
// 1. 检查赛事状态
|
||||
MartialScheduleStatus status = getOrCreateScheduleStatus(competitionId);
|
||||
if (status.getScheduleStatus() == 2) {
|
||||
log.info("赛事已锁定,跳过自动编排, competitionId: {}", competitionId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 加载赛事信息
|
||||
MartialCompetition competition = competitionMapper.selectById(competitionId);
|
||||
if (competition == null) {
|
||||
log.error("赛事不存在, competitionId: {}", competitionId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 加载场地列表
|
||||
List<MartialVenue> venues = loadVenues(competitionId);
|
||||
if (venues.isEmpty()) {
|
||||
log.warn("赛事没有配置场地, competitionId: {}", competitionId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 加载参赛者列表
|
||||
List<MartialAthlete> athletes = loadAthletes(competitionId);
|
||||
if (athletes.isEmpty()) {
|
||||
log.warn("赛事没有参赛者, competitionId: {}", competitionId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 生成时间段网格
|
||||
List<TimeSlot> timeSlots = generateTimeSlots(competition);
|
||||
|
||||
// 6. 自动分组
|
||||
List<ScheduleGroupData> groups = autoGroupParticipants(athletes);
|
||||
|
||||
// 7. 验证容量是否足够
|
||||
validateCapacity(groups, venues, timeSlots);
|
||||
|
||||
// 8. 分配场地和时间段(轮询均匀分配)
|
||||
assignVenueAndTimeSlot(groups, venues, timeSlots);
|
||||
|
||||
// 8. 清空旧的编排数据
|
||||
clearOldScheduleData(competitionId);
|
||||
|
||||
// 9. 保存新的编排结果
|
||||
saveScheduleData(competitionId, groups);
|
||||
|
||||
// 10. 更新编排状态
|
||||
status.setScheduleStatus(1);
|
||||
status.setLastAutoScheduleTime(LocalDateTime.now());
|
||||
status.setTotalGroups(groups.size());
|
||||
status.setTotalParticipants(athletes.size());
|
||||
status.setUpdateTime(new Date());
|
||||
scheduleStatusMapper.updateById(status);
|
||||
|
||||
log.info("自动编排完成, competitionId: {}, 分组数: {}, 参赛人数: {}",
|
||||
competitionId, groups.size(), athletes.size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("自动编排失败, competitionId: {}", competitionId, e);
|
||||
throw new RuntimeException("自动编排失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveAndLock(Long competitionId, String userId) {
|
||||
log.info("保存并锁定编排, competitionId: {}, userId: {}", competitionId, userId);
|
||||
|
||||
MartialScheduleStatus status = getOrCreateScheduleStatus(competitionId);
|
||||
status.setScheduleStatus(2);
|
||||
status.setLockedTime(LocalDateTime.now());
|
||||
status.setLockedBy(userId);
|
||||
status.setUpdateTime(new Date());
|
||||
|
||||
scheduleStatusMapper.updateById(status);
|
||||
|
||||
log.info("编排已保存并锁定, competitionId: {}", competitionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getScheduleResult(Long competitionId) {
|
||||
// 获取编排状态
|
||||
MartialScheduleStatus status = getOrCreateScheduleStatus(competitionId);
|
||||
|
||||
// 获取分组列表
|
||||
LambdaQueryWrapper<MartialScheduleGroup> groupWrapper = new LambdaQueryWrapper<>();
|
||||
groupWrapper.eq(MartialScheduleGroup::getCompetitionId, competitionId)
|
||||
.eq(MartialScheduleGroup::getIsDeleted, 0)
|
||||
.orderByAsc(MartialScheduleGroup::getDisplayOrder);
|
||||
|
||||
List<MartialScheduleGroup> groups = scheduleGroupMapper.selectList(groupWrapper);
|
||||
|
||||
// 构建前端需要的数据结构
|
||||
List<Map<String, Object>> scheduleGroups = new ArrayList<>();
|
||||
|
||||
for (MartialScheduleGroup group : groups) {
|
||||
Map<String, Object> groupData = new HashMap<>();
|
||||
groupData.put("id", group.getId());
|
||||
groupData.put("groupName", group.getGroupName());
|
||||
groupData.put("projectType", group.getProjectType());
|
||||
groupData.put("displayOrder", group.getDisplayOrder());
|
||||
groupData.put("totalParticipants", group.getTotalParticipants());
|
||||
groupData.put("totalTeams", group.getTotalTeams());
|
||||
|
||||
// 获取该分组的场地时间段详情
|
||||
LambdaQueryWrapper<MartialScheduleDetail> detailWrapper = new LambdaQueryWrapper<>();
|
||||
detailWrapper.eq(MartialScheduleDetail::getScheduleGroupId, group.getId())
|
||||
.eq(MartialScheduleDetail::getIsDeleted, 0);
|
||||
List<MartialScheduleDetail> details = scheduleDetailMapper.selectList(detailWrapper);
|
||||
|
||||
List<Map<String, Object>> scheduleDetails = new ArrayList<>();
|
||||
for (MartialScheduleDetail detail : details) {
|
||||
Map<String, Object> detailData = new HashMap<>();
|
||||
detailData.put("venueId", detail.getVenueId());
|
||||
detailData.put("venueName", detail.getVenueName());
|
||||
detailData.put("scheduleDate", detail.getScheduleDate());
|
||||
detailData.put("timeSlot", detail.getTimeSlot());
|
||||
detailData.put("timePeriod", detail.getTimePeriod());
|
||||
scheduleDetails.add(detailData);
|
||||
}
|
||||
groupData.put("scheduleDetails", scheduleDetails);
|
||||
|
||||
// 获取该分组的参赛者
|
||||
LambdaQueryWrapper<MartialScheduleParticipant> participantWrapper = new LambdaQueryWrapper<>();
|
||||
participantWrapper.eq(MartialScheduleParticipant::getScheduleGroupId, group.getId())
|
||||
.eq(MartialScheduleParticipant::getIsDeleted, 0);
|
||||
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(participantWrapper);
|
||||
|
||||
if (group.getProjectType() == 2) {
|
||||
// 集体项目:按单位分组
|
||||
Map<String, List<MartialScheduleParticipant>> orgGroupMap = participants.stream()
|
||||
.collect(Collectors.groupingBy(MartialScheduleParticipant::getOrganization));
|
||||
|
||||
List<Map<String, Object>> organizationGroups = new ArrayList<>();
|
||||
for (Map.Entry<String, List<MartialScheduleParticipant>> entry : orgGroupMap.entrySet()) {
|
||||
Map<String, Object> orgGroup = new HashMap<>();
|
||||
orgGroup.put("organization", entry.getKey());
|
||||
|
||||
List<Map<String, Object>> orgParticipants = new ArrayList<>();
|
||||
for (MartialScheduleParticipant p : entry.getValue()) {
|
||||
Map<String, Object> pData = new HashMap<>();
|
||||
pData.put("playerName", p.getPlayerName());
|
||||
orgParticipants.add(pData);
|
||||
}
|
||||
orgGroup.put("participants", orgParticipants);
|
||||
|
||||
// 获取该单位的场地时间段
|
||||
orgGroup.put("scheduleDetails", scheduleDetails);
|
||||
|
||||
organizationGroups.add(orgGroup);
|
||||
}
|
||||
groupData.put("organizationGroups", organizationGroups);
|
||||
|
||||
} else {
|
||||
// 个人项目:直接列出参赛者
|
||||
List<Map<String, Object>> individualParticipants = new ArrayList<>();
|
||||
for (MartialScheduleParticipant p : participants) {
|
||||
Map<String, Object> pData = new HashMap<>();
|
||||
pData.put("id", p.getParticipantId());
|
||||
pData.put("organization", p.getOrganization());
|
||||
pData.put("playerName", p.getPlayerName());
|
||||
|
||||
// 获取该参赛者的场地时间段
|
||||
LambdaQueryWrapper<MartialScheduleDetail> pDetailWrapper = new LambdaQueryWrapper<>();
|
||||
pDetailWrapper.eq(MartialScheduleDetail::getId, p.getScheduleDetailId())
|
||||
.eq(MartialScheduleDetail::getIsDeleted, 0)
|
||||
.last("LIMIT 1");
|
||||
MartialScheduleDetail pDetail = scheduleDetailMapper.selectOne(pDetailWrapper);
|
||||
|
||||
if (pDetail != null) {
|
||||
Map<String, Object> scheduleDetail = new HashMap<>();
|
||||
scheduleDetail.put("venueId", pDetail.getVenueId());
|
||||
scheduleDetail.put("venueName", pDetail.getVenueName());
|
||||
scheduleDetail.put("scheduleDate", pDetail.getScheduleDate());
|
||||
scheduleDetail.put("timeSlot", pDetail.getTimeSlot());
|
||||
pData.put("scheduleDetail", scheduleDetail);
|
||||
}
|
||||
|
||||
individualParticipants.add(pData);
|
||||
}
|
||||
groupData.put("participants", individualParticipants);
|
||||
}
|
||||
|
||||
scheduleGroups.add(groupData);
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("scheduleStatus", status.getScheduleStatus());
|
||||
result.put("lastAutoScheduleTime", status.getLastAutoScheduleTime());
|
||||
result.put("totalGroups", status.getTotalGroups());
|
||||
result.put("totalParticipants", status.getTotalParticipants());
|
||||
result.put("scheduleGroups", scheduleGroups);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 私有辅助方法 ====================
|
||||
|
||||
private MartialScheduleStatus getOrCreateScheduleStatus(Long competitionId) {
|
||||
LambdaQueryWrapper<MartialScheduleStatus> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialScheduleStatus::getCompetitionId, competitionId)
|
||||
.eq(MartialScheduleStatus::getIsDeleted, 0)
|
||||
.last("LIMIT 1");
|
||||
|
||||
MartialScheduleStatus status = scheduleStatusMapper.selectOne(wrapper);
|
||||
|
||||
if (status == null) {
|
||||
status = new MartialScheduleStatus();
|
||||
status.setCompetitionId(competitionId);
|
||||
status.setScheduleStatus(0);
|
||||
status.setTotalGroups(0);
|
||||
status.setTotalParticipants(0);
|
||||
status.setCreateTime(new Date());
|
||||
scheduleStatusMapper.insert(status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private List<MartialVenue> loadVenues(Long competitionId) {
|
||||
LambdaQueryWrapper<MartialVenue> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialVenue::getCompetitionId, competitionId)
|
||||
.eq(MartialVenue::getIsDeleted, 0);
|
||||
return venueMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private List<MartialAthlete> loadAthletes(Long competitionId) {
|
||||
LambdaQueryWrapper<MartialAthlete> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(MartialAthlete::getCompetitionId, competitionId)
|
||||
.eq(MartialAthlete::getIsDeleted, 0);
|
||||
return athleteMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private List<TimeSlot> generateTimeSlots(MartialCompetition competition) {
|
||||
List<TimeSlot> timeSlots = new ArrayList<>();
|
||||
|
||||
LocalDateTime startTime = competition.getCompetitionStartTime();
|
||||
LocalDateTime endTime = competition.getCompetitionEndTime();
|
||||
|
||||
if (startTime == null || endTime == null) {
|
||||
log.warn("赛事时间信息不完整, 使用默认时间段");
|
||||
return timeSlots;
|
||||
}
|
||||
|
||||
LocalDate currentDate = startTime.toLocalDate();
|
||||
LocalDate endDate = endTime.toLocalDate();
|
||||
|
||||
while (!currentDate.isAfter(endDate)) {
|
||||
// 上午时段 (08:00-12:00, 共240分钟)
|
||||
TimeSlot morning = new TimeSlot();
|
||||
morning.setDate(currentDate);
|
||||
morning.setPeriod("morning");
|
||||
morning.setStartTime("08:30");
|
||||
morning.setCapacity(480); // 上午480分钟(8小时,允许并发/灵活安排)
|
||||
timeSlots.add(morning);
|
||||
|
||||
// 下午时段 (13:00-18:00, 共300分钟)
|
||||
TimeSlot afternoon = new TimeSlot();
|
||||
afternoon.setDate(currentDate);
|
||||
afternoon.setPeriod("afternoon");
|
||||
afternoon.setStartTime("13:30");
|
||||
afternoon.setCapacity(480); // 下午480分钟(8小时,允许并发/灵活安排)
|
||||
timeSlots.add(afternoon);
|
||||
|
||||
currentDate = currentDate.plusDays(1);
|
||||
}
|
||||
|
||||
return timeSlots;
|
||||
}
|
||||
|
||||
private List<ScheduleGroupData> autoGroupParticipants(List<MartialAthlete> athletes) {
|
||||
List<ScheduleGroupData> groups = new ArrayList<>();
|
||||
int displayOrder = 1;
|
||||
|
||||
// 先加载所有项目信息(用于获取项目类型和名称)
|
||||
Set<Long> projectIds = athletes.stream()
|
||||
.map(MartialAthlete::getProjectId)
|
||||
.filter(id -> id != null)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<Long, MartialProject> projectMap = new HashMap<>();
|
||||
for (Long projectId : projectIds) {
|
||||
MartialProject project = projectMapper.selectById(projectId);
|
||||
if (project != null) {
|
||||
projectMap.put(projectId, project);
|
||||
}
|
||||
}
|
||||
|
||||
// 分离集体和个人项目(根据项目表的type字段: 1=个人, 2=双人, 3=集体)
|
||||
List<MartialAthlete> teamAthletes = athletes.stream()
|
||||
.filter(a -> {
|
||||
MartialProject project = projectMap.get(a.getProjectId());
|
||||
return project != null && (project.getType() == 2 || project.getType() == 3);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<MartialAthlete> individualAthletes = athletes.stream()
|
||||
.filter(a -> {
|
||||
MartialProject project = projectMap.get(a.getProjectId());
|
||||
return project != null && project.getType() == 1;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 集体项目分组:按"项目ID_组别"分组
|
||||
Map<String, List<MartialAthlete>> teamGroupMap = teamAthletes.stream()
|
||||
.collect(Collectors.groupingBy(a ->
|
||||
a.getProjectId() + "_" + Func.toStr(a.getCategory(), "未分组")
|
||||
));
|
||||
|
||||
for (Map.Entry<String, List<MartialAthlete>> entry : teamGroupMap.entrySet()) {
|
||||
List<MartialAthlete> members = entry.getValue();
|
||||
if (members.isEmpty()) continue;
|
||||
|
||||
MartialAthlete first = members.get(0);
|
||||
MartialProject project = projectMap.get(first.getProjectId());
|
||||
|
||||
// 统计队伍数(按单位分组)
|
||||
long teamCount = members.stream()
|
||||
.map(MartialAthlete::getOrganization)
|
||||
.filter(org -> org != null && !org.isEmpty())
|
||||
.distinct()
|
||||
.count();
|
||||
|
||||
// 跳过没有项目信息的分组
|
||||
if (project == null) {
|
||||
log.warn("项目不存在, projectId: {}, 跳过该分组", first.getProjectId());
|
||||
continue;
|
||||
}
|
||||
|
||||
ScheduleGroupData group = new ScheduleGroupData();
|
||||
String projectName = project.getProjectName();
|
||||
group.setGroupName(projectName + " " + (first.getCategory() != null ? first.getCategory() : "未分组"));
|
||||
group.setProjectId(first.getProjectId());
|
||||
group.setProjectType(project.getType() == 3 ? 2 : 1); // type=3映射为projectType=2(集体)
|
||||
group.setDisplayOrder(displayOrder++);
|
||||
group.setTotalParticipants(members.size());
|
||||
group.setTotalTeams((int) teamCount);
|
||||
group.setAthletes(members);
|
||||
|
||||
// 计算预计时长:使用项目的 estimatedDuration
|
||||
// 如果项目设置了时长,使用 队伍数 × 项目时长
|
||||
// 否则使用默认计算: 队伍数 × 5分钟 + 间隔时间
|
||||
int duration;
|
||||
if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) {
|
||||
duration = (int) teamCount * project.getEstimatedDuration();
|
||||
log.debug("集体项目 '{}': 使用项目时长 {}分钟/队, {}队, 总计{}分钟",
|
||||
projectName, project.getEstimatedDuration(), teamCount, duration);
|
||||
} else {
|
||||
duration = (int) teamCount * 5 + Math.max(0, (int) teamCount - 1) * 2;
|
||||
log.debug("集体项目 '{}': 使用默认时长, {}队, 总计{}分钟", projectName, teamCount, duration);
|
||||
}
|
||||
group.setEstimatedDuration(duration);
|
||||
|
||||
groups.add(group);
|
||||
}
|
||||
|
||||
// 个人项目分组:按"项目ID_组别"分组
|
||||
Map<String, List<MartialAthlete>> individualGroupMap = individualAthletes.stream()
|
||||
.collect(Collectors.groupingBy(a ->
|
||||
a.getProjectId() + "_" + Func.toStr(a.getCategory(), "未分组")
|
||||
));
|
||||
|
||||
for (Map.Entry<String, List<MartialAthlete>> entry : individualGroupMap.entrySet()) {
|
||||
List<MartialAthlete> members = entry.getValue();
|
||||
if (members.isEmpty()) continue;
|
||||
|
||||
MartialAthlete first = members.get(0);
|
||||
MartialProject project = projectMap.get(first.getProjectId());
|
||||
|
||||
// 跳过没有项目信息的分组
|
||||
if (project == null) {
|
||||
log.warn("项目不存在, projectId: {}, 跳过该分组", first.getProjectId());
|
||||
continue;
|
||||
}
|
||||
|
||||
String projectName = project.getProjectName();
|
||||
String categoryName = first.getCategory() != null ? first.getCategory() : "未分组";
|
||||
|
||||
// 计算单人时长
|
||||
int durationPerPerson = 5; // 默认5分钟/人
|
||||
if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) {
|
||||
durationPerPerson = project.getEstimatedDuration();
|
||||
}
|
||||
|
||||
// 自动拆分大组:如果人数过多,拆分成多个小组
|
||||
// 改进策略:根据人数动态拆分,确保能充分利用所有时间槽
|
||||
// 目标:让分组数量接近可用时间槽数量,实现均匀分配
|
||||
int maxPeoplePerGroup;
|
||||
if (members.size() <= 35) {
|
||||
// 35人以内不拆分
|
||||
maxPeoplePerGroup = members.size();
|
||||
} else {
|
||||
// 超过35人,按每组30-35人拆分
|
||||
maxPeoplePerGroup = 33; // 每组33人左右
|
||||
}
|
||||
|
||||
if (members.size() <= maxPeoplePerGroup) {
|
||||
// 人数不多,不需要拆分
|
||||
ScheduleGroupData group = new ScheduleGroupData();
|
||||
group.setGroupName(projectName + " " + categoryName);
|
||||
group.setProjectId(first.getProjectId());
|
||||
group.setProjectType(1);
|
||||
group.setDisplayOrder(displayOrder++);
|
||||
group.setTotalParticipants(members.size());
|
||||
group.setAthletes(members);
|
||||
|
||||
int duration = members.size() * durationPerPerson;
|
||||
group.setEstimatedDuration(duration);
|
||||
|
||||
log.debug("个人项目 '{}': {}人, {}分钟/人, 总计{}分钟",
|
||||
projectName + " " + categoryName, members.size(), durationPerPerson, duration);
|
||||
|
||||
groups.add(group);
|
||||
} else {
|
||||
// 人数太多,需要拆分成多个小组
|
||||
int totalPeople = members.size();
|
||||
int numSubGroups = (int) Math.ceil((double) totalPeople / maxPeoplePerGroup);
|
||||
|
||||
log.info("个人项目 '{}' 有{}人,将拆分成{}个小组(每组最多{}人)",
|
||||
projectName + " " + categoryName, totalPeople, numSubGroups, maxPeoplePerGroup);
|
||||
|
||||
for (int i = 0; i < numSubGroups; i++) {
|
||||
int startIdx = i * maxPeoplePerGroup;
|
||||
int endIdx = Math.min(startIdx + maxPeoplePerGroup, totalPeople);
|
||||
List<MartialAthlete> subGroupMembers = members.subList(startIdx, endIdx);
|
||||
|
||||
ScheduleGroupData group = new ScheduleGroupData();
|
||||
group.setGroupName(projectName + " " + categoryName + " 第" + (i + 1) + "组");
|
||||
group.setProjectId(first.getProjectId());
|
||||
group.setProjectType(1);
|
||||
group.setDisplayOrder(displayOrder++);
|
||||
group.setTotalParticipants(subGroupMembers.size());
|
||||
group.setAthletes(new ArrayList<>(subGroupMembers));
|
||||
|
||||
int duration = subGroupMembers.size() * durationPerPerson;
|
||||
group.setEstimatedDuration(duration);
|
||||
|
||||
log.debug(" 拆分子组 '第{}组': {}人, 总计{}分钟",
|
||||
i + 1, subGroupMembers.size(), duration);
|
||||
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证时间窗口容量是否足够容纳所有分组
|
||||
*/
|
||||
private void validateCapacity(List<ScheduleGroupData> groups,
|
||||
List<MartialVenue> venues,
|
||||
List<TimeSlot> timeSlots) {
|
||||
// 计算总需求时长
|
||||
int totalDuration = groups.stream()
|
||||
.mapToInt(ScheduleGroupData::getEstimatedDuration)
|
||||
.sum();
|
||||
|
||||
// 计算总可用容量
|
||||
int totalCapacity = venues.size() * timeSlots.size() * (timeSlots.isEmpty() ? 0 : timeSlots.get(0).getCapacity());
|
||||
|
||||
log.info("=== 容量验证 ===");
|
||||
log.info("分组总需求时长: {} 分钟", totalDuration);
|
||||
log.info("总可用容量: {} 分钟 ({}个场地 × {}个时段 × {}分钟/时段)",
|
||||
totalCapacity, venues.size(), timeSlots.size(),
|
||||
timeSlots.isEmpty() ? 0 : timeSlots.get(0).getCapacity());
|
||||
|
||||
if (totalDuration > totalCapacity) {
|
||||
String errorMsg = String.format(
|
||||
"时间窗口容量不足! 需要 %d 分钟, 但只有 %d 分钟可用 (缺口: %d 分钟)",
|
||||
totalDuration, totalCapacity, totalDuration - totalCapacity
|
||||
);
|
||||
log.error(errorMsg);
|
||||
throw new RuntimeException(errorMsg);
|
||||
}
|
||||
|
||||
double utilizationRate = totalCapacity > 0 ? (totalDuration * 100.0 / totalCapacity) : 0;
|
||||
log.info("预计容量利用率: {}%", (int)utilizationRate);
|
||||
|
||||
if (utilizationRate > 90) {
|
||||
log.warn("⚠️ 容量利用率超过90%,可能导致分配困难,建议增加场地或延长比赛时间");
|
||||
}
|
||||
}
|
||||
|
||||
private void assignVenueAndTimeSlot(List<ScheduleGroupData> groups,
|
||||
List<MartialVenue> venues,
|
||||
List<TimeSlot> timeSlots) {
|
||||
log.info("=== 开始分配场地和时间段 ===");
|
||||
log.info("场地数量: {}, 时间段数量: {}, 分组数量: {}", venues.size(), timeSlots.size(), groups.size());
|
||||
|
||||
// 创建所有槽位(场地 × 时间段组合)
|
||||
List<SlotInfo> slots = new ArrayList<>();
|
||||
int timeSlotIndex = 0; // 时间段索引,对应前端的 timeSlotIndex
|
||||
for (TimeSlot timeSlot : timeSlots) { // 先遍历时间段
|
||||
for (MartialVenue venue : venues) { // 再遍历场地
|
||||
SlotInfo slot = new SlotInfo();
|
||||
slot.timeSlotIndex = timeSlotIndex; // 同一时间段的所有场地,共享相同的 timeSlotIndex
|
||||
slot.venueId = venue.getId();
|
||||
slot.venueName = venue.getVenueName();
|
||||
slot.date = timeSlot.getDate();
|
||||
slot.timeSlot = timeSlot.getStartTime();
|
||||
slot.period = timeSlot.getPeriod();
|
||||
slot.capacity = timeSlot.getCapacity();
|
||||
slot.currentLoad = 0;
|
||||
slots.add(slot);
|
||||
}
|
||||
timeSlotIndex++; // 每个时间段结束后,索引+1
|
||||
}
|
||||
|
||||
log.info("总共初始化了 {} 个场地×时间段组合", slots.size());
|
||||
|
||||
// 按预计时长降序排序(先安排时间长的)
|
||||
groups.sort((a, b) -> b.getEstimatedDuration() - a.getEstimatedDuration());
|
||||
|
||||
// 使用轮询算法进行均匀分配
|
||||
int assignedCount = 0;
|
||||
int currentSlotIndex = 0;
|
||||
|
||||
for (ScheduleGroupData group : groups) {
|
||||
SlotInfo bestSlot = null;
|
||||
int attempts = 0;
|
||||
int maxAttempts = slots.size();
|
||||
|
||||
// 第一轮:从当前槽位开始轮询,寻找第一个容量足够的槽位
|
||||
int startIndex = currentSlotIndex;
|
||||
while (attempts < maxAttempts) {
|
||||
SlotInfo slot = slots.get(currentSlotIndex);
|
||||
|
||||
// 检查容量是否足够
|
||||
if (slot.currentLoad + group.getEstimatedDuration() <= slot.capacity) {
|
||||
bestSlot = slot;
|
||||
break;
|
||||
}
|
||||
|
||||
// 尝试下一个槽位
|
||||
currentSlotIndex = (currentSlotIndex + 1) % slots.size();
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// 第二轮:如果没有找到容量足够的槽位,选择负载最小的槽位(允许超载)
|
||||
if (bestSlot == null) {
|
||||
log.warn("分组 '{}' 需要{}分钟,超过单个槽位容量{}分钟,将选择负载最小的槽位(允许超载)",
|
||||
group.getGroupName(), group.getEstimatedDuration(), slots.get(0).capacity);
|
||||
|
||||
bestSlot = slots.stream()
|
||||
.min((a, b) -> Integer.compare(a.currentLoad, b.currentLoad))
|
||||
.orElse(slots.get(0));
|
||||
|
||||
// 设置currentSlotIndex到bestSlot的位置
|
||||
for (int i = 0; i < slots.size(); i++) {
|
||||
if (slots.get(i) == bestSlot) {
|
||||
currentSlotIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分配到选定的槽位
|
||||
group.setAssignedVenueId(bestSlot.venueId);
|
||||
group.setAssignedVenueName(bestSlot.venueName);
|
||||
group.setAssignedDate(bestSlot.date);
|
||||
group.setAssignedTimeSlot(bestSlot.timeSlot);
|
||||
group.setAssignedTimeSlotIndex(bestSlot.timeSlotIndex); // 保存时间段索引
|
||||
group.setAssignedTimePeriod(bestSlot.period);
|
||||
|
||||
// 更新槽位负载
|
||||
bestSlot.currentLoad += group.getEstimatedDuration();
|
||||
assignedCount++;
|
||||
|
||||
log.info("分组 '{}' 分配到: 场地ID={}, 场地名={}, 日期={}, 时间段={}, 预计时长={}分钟, 新负载={}分钟 ({}%)",
|
||||
group.getGroupName(), bestSlot.venueId, bestSlot.venueName, bestSlot.date, bestSlot.timeSlot,
|
||||
group.getEstimatedDuration(), bestSlot.currentLoad,
|
||||
(int)(bestSlot.currentLoad * 100.0 / bestSlot.capacity));
|
||||
|
||||
// 移动到下一个槽位(轮询)
|
||||
currentSlotIndex = (currentSlotIndex + 1) % slots.size();
|
||||
}
|
||||
|
||||
log.info("=== 分配完成: {}/{} 个分组成功分配 ===", assignedCount, groups.size());
|
||||
|
||||
// 输出每个槽位的负载情况
|
||||
log.info("=== 各槽位负载统计 ===");
|
||||
for (SlotInfo slot : slots) {
|
||||
if (slot.currentLoad > 0) {
|
||||
log.info("场地={}, 日期={}, 时段={}, 负载={}/{}分钟 ({}%)",
|
||||
slot.venueName, slot.date, slot.timeSlot, slot.currentLoad, slot.capacity,
|
||||
(int)(slot.currentLoad * 100.0 / slot.capacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 槽位信息内部类
|
||||
private static class SlotInfo {
|
||||
int timeSlotIndex; // 时间段索引 (0=第1天上午, 1=第1天下午, 2=第2天上午, ...)
|
||||
Long venueId;
|
||||
String venueName;
|
||||
LocalDate date;
|
||||
String timeSlot;
|
||||
String period;
|
||||
int capacity;
|
||||
int currentLoad;
|
||||
}
|
||||
|
||||
private void clearOldScheduleData(Long competitionId) {
|
||||
// 删除旧的编排数据
|
||||
LambdaQueryWrapper<MartialScheduleGroup> groupWrapper = new LambdaQueryWrapper<>();
|
||||
groupWrapper.eq(MartialScheduleGroup::getCompetitionId, competitionId);
|
||||
|
||||
// 先查询出所有分组ID,然后再删除
|
||||
List<Long> groupIds = scheduleGroupMapper.selectList(groupWrapper).stream()
|
||||
.map(MartialScheduleGroup::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 删除参赛者关联(必须在删除分组之前)
|
||||
if (groupIds != null && !groupIds.isEmpty()) {
|
||||
LambdaQueryWrapper<MartialScheduleParticipant> participantWrapper = new LambdaQueryWrapper<>();
|
||||
participantWrapper.in(MartialScheduleParticipant::getScheduleGroupId, groupIds);
|
||||
scheduleParticipantMapper.delete(participantWrapper);
|
||||
}
|
||||
|
||||
// 删除场地时间段详情
|
||||
LambdaQueryWrapper<MartialScheduleDetail> detailWrapper = new LambdaQueryWrapper<>();
|
||||
detailWrapper.eq(MartialScheduleDetail::getCompetitionId, competitionId);
|
||||
scheduleDetailMapper.delete(detailWrapper);
|
||||
|
||||
// 最后删除分组
|
||||
scheduleGroupMapper.delete(groupWrapper);
|
||||
}
|
||||
|
||||
private void saveScheduleData(Long competitionId, List<ScheduleGroupData> groups) {
|
||||
for (ScheduleGroupData groupData : groups) {
|
||||
// 保存分组
|
||||
MartialScheduleGroup group = new MartialScheduleGroup();
|
||||
group.setCompetitionId(competitionId);
|
||||
group.setGroupName(groupData.getGroupName());
|
||||
group.setProjectId(groupData.getProjectId());
|
||||
group.setProjectName(groupData.getGroupName());
|
||||
group.setProjectType(groupData.getProjectType());
|
||||
group.setDisplayOrder(groupData.getDisplayOrder());
|
||||
group.setTotalParticipants(groupData.getTotalParticipants());
|
||||
group.setTotalTeams(groupData.getTotalTeams());
|
||||
group.setEstimatedDuration(groupData.getEstimatedDuration());
|
||||
group.setCreateTime(new Date());
|
||||
scheduleGroupMapper.insert(group);
|
||||
|
||||
Long groupId = group.getId();
|
||||
|
||||
// 保存场地时间段详情
|
||||
if (groupData.getAssignedVenueId() != null) {
|
||||
MartialScheduleDetail detail = new MartialScheduleDetail();
|
||||
detail.setScheduleGroupId(groupId);
|
||||
detail.setCompetitionId(competitionId);
|
||||
detail.setVenueId(groupData.getAssignedVenueId());
|
||||
detail.setVenueName(groupData.getAssignedVenueName());
|
||||
detail.setScheduleDate(groupData.getAssignedDate());
|
||||
detail.setTimePeriod(groupData.getAssignedTimePeriod());
|
||||
detail.setTimeSlot(groupData.getAssignedTimeSlot());
|
||||
detail.setTimeSlotIndex(groupData.getAssignedTimeSlotIndex()); // 保存时间段索引
|
||||
detail.setEstimatedDuration(groupData.getEstimatedDuration());
|
||||
detail.setParticipantCount(groupData.getTotalParticipants());
|
||||
detail.setCreateTime(new Date());
|
||||
scheduleDetailMapper.insert(detail);
|
||||
|
||||
Long detailId = detail.getId();
|
||||
|
||||
// 保存参赛者关联
|
||||
int order = 1;
|
||||
for (MartialAthlete athlete : groupData.getAthletes()) {
|
||||
MartialScheduleParticipant participant = new MartialScheduleParticipant();
|
||||
participant.setScheduleDetailId(detailId);
|
||||
participant.setScheduleGroupId(groupId);
|
||||
participant.setParticipantId(athlete.getId());
|
||||
participant.setOrganization(athlete.getOrganization());
|
||||
participant.setPlayerName(athlete.getPlayerName());
|
||||
participant.setProjectName(groupData.getGroupName());
|
||||
participant.setCategory(athlete.getCategory());
|
||||
participant.setPerformanceOrder(order++);
|
||||
participant.setCreateTime(new Date());
|
||||
scheduleParticipantMapper.insert(participant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 内部数据类 ====================
|
||||
|
||||
private static class TimeSlot {
|
||||
private LocalDate date;
|
||||
private String period; // morning/afternoon
|
||||
private String startTime; // 08:30/13:30
|
||||
private Integer capacity; // 容量(分钟)
|
||||
|
||||
// Getters and Setters
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public void setPeriod(String period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public String getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(String startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Integer getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public void setCapacity(Integer capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScheduleGroupData {
|
||||
private String groupName;
|
||||
private Long projectId;
|
||||
private Integer projectType;
|
||||
private Integer displayOrder;
|
||||
private Integer totalParticipants;
|
||||
private Integer totalTeams;
|
||||
private Integer estimatedDuration;
|
||||
private List<MartialAthlete> athletes;
|
||||
|
||||
// 分配结果
|
||||
private Long assignedVenueId;
|
||||
private String assignedVenueName;
|
||||
private LocalDate assignedDate;
|
||||
private String assignedTimePeriod;
|
||||
private String assignedTimeSlot;
|
||||
private Integer assignedTimeSlotIndex; // 时间段索引
|
||||
|
||||
// Getters and Setters
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public Integer getProjectType() {
|
||||
return projectType;
|
||||
}
|
||||
|
||||
public void setProjectType(Integer projectType) {
|
||||
this.projectType = projectType;
|
||||
}
|
||||
|
||||
public Integer getDisplayOrder() {
|
||||
return displayOrder;
|
||||
}
|
||||
|
||||
public void setDisplayOrder(Integer displayOrder) {
|
||||
this.displayOrder = displayOrder;
|
||||
}
|
||||
|
||||
public Integer getTotalParticipants() {
|
||||
return totalParticipants;
|
||||
}
|
||||
|
||||
public void setTotalParticipants(Integer totalParticipants) {
|
||||
this.totalParticipants = totalParticipants;
|
||||
}
|
||||
|
||||
public Integer getTotalTeams() {
|
||||
return totalTeams;
|
||||
}
|
||||
|
||||
public void setTotalTeams(Integer totalTeams) {
|
||||
this.totalTeams = totalTeams;
|
||||
}
|
||||
|
||||
public Integer getEstimatedDuration() {
|
||||
return estimatedDuration;
|
||||
}
|
||||
|
||||
public void setEstimatedDuration(Integer estimatedDuration) {
|
||||
this.estimatedDuration = estimatedDuration;
|
||||
}
|
||||
|
||||
public List<MartialAthlete> getAthletes() {
|
||||
return athletes;
|
||||
}
|
||||
|
||||
public void setAthletes(List<MartialAthlete> athletes) {
|
||||
this.athletes = athletes;
|
||||
}
|
||||
|
||||
public Long getAssignedVenueId() {
|
||||
return assignedVenueId;
|
||||
}
|
||||
|
||||
public void setAssignedVenueId(Long assignedVenueId) {
|
||||
this.assignedVenueId = assignedVenueId;
|
||||
}
|
||||
|
||||
public String getAssignedVenueName() {
|
||||
return assignedVenueName;
|
||||
}
|
||||
|
||||
public void setAssignedVenueName(String assignedVenueName) {
|
||||
this.assignedVenueName = assignedVenueName;
|
||||
}
|
||||
|
||||
public LocalDate getAssignedDate() {
|
||||
return assignedDate;
|
||||
}
|
||||
|
||||
public void setAssignedDate(LocalDate assignedDate) {
|
||||
this.assignedDate = assignedDate;
|
||||
}
|
||||
|
||||
public String getAssignedTimePeriod() {
|
||||
return assignedTimePeriod;
|
||||
}
|
||||
|
||||
public void setAssignedTimePeriod(String assignedTimePeriod) {
|
||||
this.assignedTimePeriod = assignedTimePeriod;
|
||||
}
|
||||
|
||||
public String getAssignedTimeSlot() {
|
||||
return assignedTimeSlot;
|
||||
}
|
||||
|
||||
public void setAssignedTimeSlot(String assignedTimeSlot) {
|
||||
this.assignedTimeSlot = assignedTimeSlot;
|
||||
}
|
||||
|
||||
public Integer getAssignedTimeSlotIndex() {
|
||||
return assignedTimeSlotIndex;
|
||||
}
|
||||
|
||||
public void setAssignedTimeSlotIndex(Integer assignedTimeSlotIndex) {
|
||||
this.assignedTimeSlotIndex = assignedTimeSlotIndex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,15 +3,25 @@ package org.springblade.modules.martial.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springblade.modules.martial.excel.ScheduleExportExcel;
|
||||
import org.springblade.modules.martial.mapper.MartialScheduleDetailMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialScheduleGroupMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialScheduleParticipantMapper;
|
||||
import org.springblade.modules.martial.pojo.dto.CompetitionGroupDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.ParticipantDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.SaveScheduleDraftDTO;
|
||||
import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO;
|
||||
import org.springblade.modules.martial.pojo.entity.*;
|
||||
import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
|
||||
import org.springblade.modules.martial.mapper.MartialScheduleMapper;
|
||||
import org.springblade.modules.martial.service.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Schedule 服务实现类
|
||||
@@ -33,6 +43,15 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
|
||||
@Autowired
|
||||
private IMartialVenueService venueService;
|
||||
|
||||
@Autowired
|
||||
private MartialScheduleGroupMapper scheduleGroupMapper;
|
||||
|
||||
@Autowired
|
||||
private MartialScheduleDetailMapper scheduleDetailMapper;
|
||||
|
||||
@Autowired
|
||||
private MartialScheduleParticipantMapper scheduleParticipantMapper;
|
||||
|
||||
/**
|
||||
* Task 3.3: 导出赛程表
|
||||
*
|
||||
@@ -120,4 +139,316 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
|
||||
return exportList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取赛程编排结果(优化版本:使用单次JOIN查询)
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 赛程编排结果
|
||||
*/
|
||||
@Override
|
||||
public ScheduleResultDTO getScheduleResult(Long competitionId) {
|
||||
ScheduleResultDTO result = new ScheduleResultDTO();
|
||||
|
||||
// 使用优化的一次性JOIN查询获取所有数据
|
||||
List<ScheduleGroupDetailVO> details = scheduleGroupMapper.selectScheduleGroupDetails(competitionId);
|
||||
|
||||
if (details.isEmpty()) {
|
||||
result.setIsDraft(true);
|
||||
result.setIsCompleted(false);
|
||||
result.setCompetitionGroups(new ArrayList<>());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 按分组ID分组数据
|
||||
Map<Long, List<ScheduleGroupDetailVO>> groupMap = details.stream()
|
||||
.collect(Collectors.groupingBy(ScheduleGroupDetailVO::getGroupId));
|
||||
|
||||
// 检查编排状态
|
||||
boolean isCompleted = details.stream()
|
||||
.anyMatch(d -> "completed".equals(d.getScheduleStatus()));
|
||||
boolean isDraft = !isCompleted;
|
||||
|
||||
result.setIsDraft(isDraft);
|
||||
result.setIsCompleted(isCompleted);
|
||||
|
||||
// 组装数据
|
||||
List<CompetitionGroupDTO> groupDTOs = new ArrayList<>();
|
||||
for (Map.Entry<Long, List<ScheduleGroupDetailVO>> entry : groupMap.entrySet()) {
|
||||
List<ScheduleGroupDetailVO> groupDetails = entry.getValue();
|
||||
if (groupDetails.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取第一条记录作为分组信息(同一分组的记录,分组信息是相同的)
|
||||
ScheduleGroupDetailVO firstDetail = groupDetails.get(0);
|
||||
|
||||
CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
|
||||
groupDTO.setId(firstDetail.getGroupId());
|
||||
groupDTO.setTitle(firstDetail.getGroupName());
|
||||
groupDTO.setCode(firstDetail.getCategory());
|
||||
|
||||
// 设置类型
|
||||
if (firstDetail.getProjectType() != null) {
|
||||
switch (firstDetail.getProjectType()) {
|
||||
case 1:
|
||||
groupDTO.setType("单人");
|
||||
break;
|
||||
case 2:
|
||||
groupDTO.setType("集体");
|
||||
break;
|
||||
default:
|
||||
groupDTO.setType("其他");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置队伍数量
|
||||
if (firstDetail.getTotalTeams() != null && firstDetail.getTotalTeams() > 0) {
|
||||
groupDTO.setCount(firstDetail.getTotalTeams() + "队");
|
||||
} else if (firstDetail.getTotalParticipants() != null) {
|
||||
groupDTO.setCount(firstDetail.getTotalParticipants() + "人");
|
||||
}
|
||||
|
||||
// 设置场地和时间段信息
|
||||
groupDTO.setVenueId(firstDetail.getVenueId());
|
||||
groupDTO.setVenueName(firstDetail.getVenueName());
|
||||
groupDTO.setTimeSlot(firstDetail.getTimeSlot());
|
||||
groupDTO.setTimeSlotIndex(firstDetail.getTimeSlotIndex() != null ? firstDetail.getTimeSlotIndex() : 0); // 直接从数据库读取
|
||||
|
||||
// 获取参赛者列表
|
||||
List<ParticipantDTO> participantDTOs = groupDetails.stream()
|
||||
.filter(d -> d.getParticipantId() != null) // 过滤掉没有参赛者的记录
|
||||
.map(d -> {
|
||||
ParticipantDTO dto = new ParticipantDTO();
|
||||
dto.setId(d.getParticipantId());
|
||||
dto.setSchoolUnit(d.getOrganization());
|
||||
dto.setStatus(d.getCheckInStatus() != null ? d.getCheckInStatus() : "未签到");
|
||||
dto.setSortOrder(d.getPerformanceOrder());
|
||||
return dto;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
groupDTO.setParticipants(participantDTOs);
|
||||
groupDTOs.add(groupDTO);
|
||||
}
|
||||
|
||||
// 按 displayOrder 排序
|
||||
groupDTOs.sort(Comparator.comparing(g -> {
|
||||
ScheduleGroupDetailVO detail = details.stream()
|
||||
.filter(d -> d.getGroupId().equals(g.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return detail != null ? detail.getDisplayOrder() : 999;
|
||||
}));
|
||||
|
||||
result.setCompetitionGroups(groupDTOs);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存编排草稿
|
||||
*
|
||||
* @param dto 编排草稿数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveDraftSchedule(SaveScheduleDraftDTO dto) {
|
||||
if (dto.getCompetitionGroups() == null || dto.getCompetitionGroups().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CompetitionGroupDTO groupDTO : dto.getCompetitionGroups()) {
|
||||
// 1. 更新或创建编排明细
|
||||
MartialScheduleDetail detail = scheduleDetailMapper.selectOne(
|
||||
new QueryWrapper<MartialScheduleDetail>()
|
||||
.eq("schedule_group_id", groupDTO.getId())
|
||||
.eq("is_deleted", 0)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
|
||||
if (detail == null) {
|
||||
detail = new MartialScheduleDetail();
|
||||
detail.setScheduleGroupId(groupDTO.getId());
|
||||
detail.setCompetitionId(dto.getCompetitionId());
|
||||
}
|
||||
|
||||
detail.setVenueId(groupDTO.getVenueId());
|
||||
detail.setVenueName(groupDTO.getVenueName());
|
||||
detail.setTimeSlot(groupDTO.getTimeSlot());
|
||||
|
||||
// 解析日期
|
||||
if (groupDTO.getTimeSlot() != null && groupDTO.getTimeSlot().contains("年")) {
|
||||
try {
|
||||
String dateStr = groupDTO.getTimeSlot().split(" ")[0];
|
||||
dateStr = dateStr.replace("年", "-").replace("月", "-").replace("日", "");
|
||||
detail.setScheduleDate(LocalDate.parse(dateStr));
|
||||
} catch (Exception e) {
|
||||
// 日期解析失败,忽略
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.getId() == null) {
|
||||
scheduleDetailMapper.insert(detail);
|
||||
} else {
|
||||
scheduleDetailMapper.updateById(detail);
|
||||
}
|
||||
|
||||
// 2. 更新参赛者信息
|
||||
if (groupDTO.getParticipants() != null) {
|
||||
for (ParticipantDTO participantDTO : groupDTO.getParticipants()) {
|
||||
MartialScheduleParticipant participant = scheduleParticipantMapper.selectById(participantDTO.getId());
|
||||
if (participant != null) {
|
||||
participant.setCheckInStatus(participantDTO.getStatus());
|
||||
participant.setPerformanceOrder(participantDTO.getSortOrder());
|
||||
participant.setScheduleStatus("draft");
|
||||
scheduleParticipantMapper.updateById(participant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成编排并锁定
|
||||
*
|
||||
* @param competitionId 赛事ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveAndLockSchedule(Long competitionId) {
|
||||
// 1. 查询所有分组
|
||||
List<MartialScheduleGroup> groups = scheduleGroupMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleGroup>()
|
||||
.eq("competition_id", competitionId)
|
||||
.eq("is_deleted", 0)
|
||||
);
|
||||
|
||||
if (groups.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 获取所有分组ID
|
||||
List<Long> groupIds = groups.stream().map(MartialScheduleGroup::getId).collect(Collectors.toList());
|
||||
|
||||
// 3. 更新所有参赛者的编排状态为completed
|
||||
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleParticipant>()
|
||||
.in("schedule_group_id", groupIds)
|
||||
.eq("is_deleted", 0)
|
||||
);
|
||||
|
||||
for (MartialScheduleParticipant participant : participants) {
|
||||
participant.setScheduleStatus("completed");
|
||||
scheduleParticipantMapper.updateById(participant);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间段字符串计算 timeSlotIndex
|
||||
* 按照时间从早到晚分配索引: 08:30=0, 13:30=1, 其他时间按字符串顺序排序
|
||||
*
|
||||
* @param timeSlot 时间段字符串 (如 "08:30", "13:30")
|
||||
* @return 时间段索引
|
||||
*/
|
||||
private int calculateTimeSlotIndex(String timeSlot) {
|
||||
if (timeSlot == null || timeSlot.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 标准时间段映射
|
||||
switch (timeSlot.trim()) {
|
||||
case "08:30":
|
||||
return 0; // 上午场
|
||||
case "13:30":
|
||||
return 1; // 下午场
|
||||
default:
|
||||
// 其他时间段,尝试解析并分配索引
|
||||
// 如果是上午(< 12:00)返回0,下午(>= 12:00)返回1
|
||||
try {
|
||||
String[] parts = timeSlot.trim().split(":");
|
||||
if (parts.length >= 1) {
|
||||
int hour = Integer.parseInt(parts[0]);
|
||||
return hour < 12 ? 0 : 1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 解析失败,返回默认值
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动赛程分组到指定场地和时间段
|
||||
*
|
||||
* @param dto 移动请求数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean moveScheduleGroup(org.springblade.modules.martial.pojo.dto.MoveScheduleGroupDTO dto) {
|
||||
// 1. 查询分组信息
|
||||
MartialScheduleGroup group = scheduleGroupMapper.selectById(dto.getGroupId());
|
||||
if (group == null) {
|
||||
throw new RuntimeException("分组不存在");
|
||||
}
|
||||
|
||||
// 2. 查询该分组的详情记录
|
||||
List<MartialScheduleDetail> details = scheduleDetailMapper.selectList(
|
||||
new QueryWrapper<MartialScheduleDetail>()
|
||||
.eq("schedule_group_id", dto.getGroupId())
|
||||
.eq("is_deleted", 0)
|
||||
);
|
||||
|
||||
if (details.isEmpty()) {
|
||||
throw new RuntimeException("分组详情不存在");
|
||||
}
|
||||
|
||||
// 3. 查询目标场地信息
|
||||
MartialVenue targetVenue = venueService.getById(dto.getTargetVenueId());
|
||||
if (targetVenue == null) {
|
||||
throw new RuntimeException("目标场地不存在");
|
||||
}
|
||||
|
||||
// 4. 根据时间段索引计算日期和时间
|
||||
// 假设: 0=第1天上午, 1=第1天下午, 2=第2天上午, 3=第2天下午...
|
||||
// 需要从赛事信息中获取起始日期
|
||||
int dayOffset = dto.getTargetTimeSlotIndex() / 2; // 每天2个时段
|
||||
boolean isAfternoon = dto.getTargetTimeSlotIndex() % 2 == 1;
|
||||
String timeSlot = isAfternoon ? "13:30" : "08:30";
|
||||
|
||||
// 获取赛事起始日期(从第一个detail中获取)
|
||||
LocalDate baseDate = details.get(0).getScheduleDate();
|
||||
if (baseDate == null) {
|
||||
throw new RuntimeException("无法确定赛事起始日期");
|
||||
}
|
||||
|
||||
// 计算目标日期(从起始日期开始,加上dayOffset天的偏移)
|
||||
// 如果当前detail的日期早于base date,需要调整
|
||||
LocalDate minDate = details.stream()
|
||||
.map(MartialScheduleDetail::getScheduleDate)
|
||||
.filter(Objects::nonNull)
|
||||
.min(LocalDate::compareTo)
|
||||
.orElse(baseDate);
|
||||
|
||||
LocalDate targetDate = minDate.plusDays(dayOffset);
|
||||
|
||||
// 5. 更新所有detail记录
|
||||
for (MartialScheduleDetail detail : details) {
|
||||
detail.setVenueId(dto.getTargetVenueId());
|
||||
detail.setVenueName(targetVenue.getVenueName());
|
||||
detail.setScheduleDate(targetDate);
|
||||
detail.setTimeSlot(timeSlot);
|
||||
detail.setTimeSlotIndex(dto.getTargetTimeSlotIndex());
|
||||
scheduleDetailMapper.updateById(detail);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user