feat: improve schedule auto-arrange functionality

- Add ScheduleConfig for configurable schedule parameters
- Add updateParticipantCheckInStatus API for exception status persistence
- Use configurable thresholds for group splitting and capacity warning
- Add @Slf4j to MartialScheduleServiceImpl
This commit is contained in:
2025-12-29 15:07:52 +08:00
parent 86e4580e5d
commit 0b5fc9fb71
6 changed files with 123 additions and 4 deletions

View File

@@ -0,0 +1,59 @@
package org.springblade.modules.martial.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 赛程编排配置
*/
@Data
@Component
@ConfigurationProperties(prefix = "martial.schedule")
public class ScheduleConfig {
/**
* 个人项目每组最大人数(超过此数量将自动拆分)
*/
private int maxPeoplePerGroup = 35;
/**
* 拆分后每组目标人数
*/
private int targetPeoplePerGroup = 33;
/**
* 默认每人比赛时长(分钟)
*/
private int defaultDurationPerPerson = 5;
/**
* 容量利用率警告阈值(百分比)
*/
private int capacityWarningThreshold = 90;
/**
* 是否允许容量超载(超载时仍继续编排)
*/
private boolean allowOverload = false;
/**
* 上午时段开始时间
*/
private String morningStartTime = "08:00";
/**
* 上午时段结束时间
*/
private String morningEndTime = "12:00";
/**
* 下午时段开始时间
*/
private String afternoonStartTime = "14:00";
/**
* 下午时段结束时间
*/
private String afternoonEndTime = "18:00";
}

View File

@@ -167,4 +167,22 @@ public class MartialScheduleArrangeController extends BladeController {
}
}
/**
* 更新参赛者签到状态
*/
@PostMapping("/update-check-in-status")
@Operation(summary = "更新签到状态", description = "更新参赛者签到状态:未签到/已签到/异常")
public R updateCheckInStatus(@RequestBody java.util.Map<String, Object> params) {
try {
Long participantId = Long.valueOf(String.valueOf(params.get("participantId")));
String status = String.valueOf(params.get("status"));
boolean success = scheduleService.updateParticipantCheckInStatus(participantId, status);
return success ? R.success("状态更新成功") : R.fail("状态更新失败");
} catch (Exception e) {
log.error("更新签到状态失败", e);
return R.fail("更新签到状态失败: " + e.getMessage());
}
}
}

View File

@@ -72,4 +72,12 @@ public interface IMartialScheduleService extends IService<MartialSchedule> {
*/
boolean saveDispatch(org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto);
/**
* 更新参赛者签到状态
* @param participantId 参赛者ID
* @param status 状态:未签到/已签到/异常
* @return 是否成功
*/
boolean updateParticipantCheckInStatus(Long participantId, String status);
}

View File

@@ -23,6 +23,7 @@ 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.config.ScheduleConfig;
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,6 +51,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
private final MartialCompetitionMapper competitionMapper;
private final MartialVenueMapper venueMapper;
private final MartialProjectMapper projectMapper;
private final ScheduleConfig scheduleConfig;
@Override
public List<Long> getUnlockedCompetitions() {
@@ -450,7 +452,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
String categoryName = first.getCategory() != null ? first.getCategory() : "未分组";
// 计算单人时长
int durationPerPerson = 5; // 默认5分钟/人
int durationPerPerson = scheduleConfig.getDefaultDurationPerPerson();
if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) {
durationPerPerson = project.getEstimatedDuration();
}
@@ -459,12 +461,12 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
// 改进策略:根据人数动态拆分,确保能充分利用所有时间槽
// 目标:让分组数量接近可用时间槽数量,实现均匀分配
int maxPeoplePerGroup;
if (members.size() <= 35) {
if (members.size() <= scheduleConfig.getMaxPeoplePerGroup()) {
// 35人以内不拆分
maxPeoplePerGroup = members.size();
} else {
// 超过35人,按每组30-35人拆分
maxPeoplePerGroup = 33; // 每组33人左右
maxPeoplePerGroup = scheduleConfig.getTargetPeoplePerGroup();
}
if (members.size() <= maxPeoplePerGroup) {
@@ -551,7 +553,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
double utilizationRate = totalCapacity > 0 ? (totalDuration * 100.0 / totalCapacity) : 0;
log.info("预计容量利用率: {}%", (int)utilizationRate);
if (utilizationRate > 90) {
if (utilizationRate > scheduleConfig.getCapacityWarningThreshold()) {
log.warn("⚠️ 容量利用率超过90%,可能导致分配困难,建议增加场地或延长比赛时间");
}
}

View File

@@ -15,6 +15,7 @@ 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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -28,6 +29,7 @@ import java.util.stream.Collectors;
*
* @author BladeX
*/
@Slf4j
@Service
public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMapper, MartialSchedule> implements IMartialScheduleService {
@@ -598,4 +600,25 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateParticipantCheckInStatus(Long participantId, String status) {
if (participantId == null || status == null) {
return false;
}
MartialScheduleParticipant participant = scheduleParticipantMapper.selectById(participantId);
if (participant == null) {
log.warn("参赛者不存在, participantId: {}", participantId);
return false;
}
participant.setCheckInStatus(status);
int result = scheduleParticipantMapper.updateById(participant);
log.info("更新参赛者签到状态: participantId={}, status={}, result={}", participantId, status, result);
return result > 0;
}
}

View File

@@ -332,3 +332,12 @@ blade:
#分库分表配置
sharding:
enabled: false
# 赛程编排配置
martial:
schedule:
max-people-per-group: 35
target-people-per-group: 33
default-duration-per-person: 5
capacity-warning-threshold: 90
allow-overload: false