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:
@@ -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";
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,12 @@ public interface IMartialScheduleService extends IService<MartialSchedule> {
|
|||||||
*/
|
*/
|
||||||
boolean saveDispatch(org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto);
|
boolean saveDispatch(org.springblade.modules.martial.pojo.dto.SaveDispatchDTO dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新参赛者签到状态
|
||||||
|
* @param participantId 参赛者ID
|
||||||
|
* @param status 状态:未签到/已签到/异常
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
boolean updateParticipantCheckInStatus(Long participantId, String status);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.joda.time.DateTime;
|
|||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.modules.martial.mapper.*;
|
import org.springblade.modules.martial.mapper.*;
|
||||||
import org.springblade.modules.martial.pojo.entity.*;
|
import org.springblade.modules.martial.pojo.entity.*;
|
||||||
|
import org.springblade.modules.martial.config.ScheduleConfig;
|
||||||
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
|
import org.springblade.modules.martial.service.IMartialScheduleArrangeService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -50,6 +51,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
|
|||||||
private final MartialCompetitionMapper competitionMapper;
|
private final MartialCompetitionMapper competitionMapper;
|
||||||
private final MartialVenueMapper venueMapper;
|
private final MartialVenueMapper venueMapper;
|
||||||
private final MartialProjectMapper projectMapper;
|
private final MartialProjectMapper projectMapper;
|
||||||
|
private final ScheduleConfig scheduleConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> getUnlockedCompetitions() {
|
public List<Long> getUnlockedCompetitions() {
|
||||||
@@ -450,7 +452,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
|
|||||||
String categoryName = first.getCategory() != null ? first.getCategory() : "未分组";
|
String categoryName = first.getCategory() != null ? first.getCategory() : "未分组";
|
||||||
|
|
||||||
// 计算单人时长
|
// 计算单人时长
|
||||||
int durationPerPerson = 5; // 默认5分钟/人
|
int durationPerPerson = scheduleConfig.getDefaultDurationPerPerson();
|
||||||
if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) {
|
if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) {
|
||||||
durationPerPerson = project.getEstimatedDuration();
|
durationPerPerson = project.getEstimatedDuration();
|
||||||
}
|
}
|
||||||
@@ -459,12 +461,12 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
|
|||||||
// 改进策略:根据人数动态拆分,确保能充分利用所有时间槽
|
// 改进策略:根据人数动态拆分,确保能充分利用所有时间槽
|
||||||
// 目标:让分组数量接近可用时间槽数量,实现均匀分配
|
// 目标:让分组数量接近可用时间槽数量,实现均匀分配
|
||||||
int maxPeoplePerGroup;
|
int maxPeoplePerGroup;
|
||||||
if (members.size() <= 35) {
|
if (members.size() <= scheduleConfig.getMaxPeoplePerGroup()) {
|
||||||
// 35人以内不拆分
|
// 35人以内不拆分
|
||||||
maxPeoplePerGroup = members.size();
|
maxPeoplePerGroup = members.size();
|
||||||
} else {
|
} else {
|
||||||
// 超过35人,按每组30-35人拆分
|
// 超过35人,按每组30-35人拆分
|
||||||
maxPeoplePerGroup = 33; // 每组33人左右
|
maxPeoplePerGroup = scheduleConfig.getTargetPeoplePerGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (members.size() <= maxPeoplePerGroup) {
|
if (members.size() <= maxPeoplePerGroup) {
|
||||||
@@ -551,7 +553,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
|
|||||||
double utilizationRate = totalCapacity > 0 ? (totalDuration * 100.0 / totalCapacity) : 0;
|
double utilizationRate = totalCapacity > 0 ? (totalDuration * 100.0 / totalCapacity) : 0;
|
||||||
log.info("预计容量利用率: {}%", (int)utilizationRate);
|
log.info("预计容量利用率: {}%", (int)utilizationRate);
|
||||||
|
|
||||||
if (utilizationRate > 90) {
|
if (utilizationRate > scheduleConfig.getCapacityWarningThreshold()) {
|
||||||
log.warn("⚠️ 容量利用率超过90%,可能导致分配困难,建议增加场地或延长比赛时间");
|
log.warn("⚠️ 容量利用率超过90%,可能导致分配困难,建议增加场地或延长比赛时间");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
|
|||||||
import org.springblade.modules.martial.mapper.MartialScheduleMapper;
|
import org.springblade.modules.martial.mapper.MartialScheduleMapper;
|
||||||
import org.springblade.modules.martial.service.*;
|
import org.springblade.modules.martial.service.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ import java.util.stream.Collectors;
|
|||||||
*
|
*
|
||||||
* @author BladeX
|
* @author BladeX
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMapper, MartialSchedule> implements IMartialScheduleService {
|
public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMapper, MartialSchedule> implements IMartialScheduleService {
|
||||||
|
|
||||||
@@ -598,4 +600,25 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
|
|||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,3 +332,12 @@ blade:
|
|||||||
#分库分表配置
|
#分库分表配置
|
||||||
sharding:
|
sharding:
|
||||||
enabled: false
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user