如何通过OptaPlanner约束关联日期并按WeekNo汇总班次时长?
按WeekNo汇总Shift时长的OptaPlanner约束实现
问题背景
现有两个业务类:
DaySetting:存储业务日期、周编号、月编号Shift:存储班次的开始/结束时间,支持计算班次时长和提取业务日期
需求是:通过DaySetting.businessDate与Shift.startDate匹配,按DaySetting.weekNo汇总对应班次的总时长,并在OptaPlanner约束中实现该逻辑。
类定义
DaySetting类
public class DaySetting { String businessDate; Integer weekNo; Integer monthNo; }
Shift类
public class Shift { @PlanningId private String id; private LocalDateTime start; private LocalDateTime end; public Long getShiftDuration() { long minutes = ChronoUnit.MINUTES.between(start, end); return minutes; } public String getStartDate() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String dateStr = getStart().toLocalDate().format(formatter); return dateStr; } }
现有尝试与问题
原代码未添加日期匹配逻辑,且存在变量名笔误(day.getWeekNo()应为daySetting.getWeekNo()),无法正确关联Shift和对应的WeekNo:
Constraint workingHoursLessThanMaximumInContractPerWeek(ConstraintFactory constraintFactory) { return constraintFactory.forEach(DaySetting.class) .join(Shift.class) .groupBy((daySetting, shift) -> day.getWeekNo(), ConstraintCollectors.sumLong((daySetting, shift) -> shift.getShiftDuration())) // 缺少日期匹配逻辑,无法关联Shift和对应DaySetting的WeekNo .penalize(HardMediumSoftScore.ONE_SOFT, ((weekNo, sumValue))->{ return sumValue; }) .asConstraint("Working Hours Greater Than Maximum In Contract Per Week"); }
解决方案
修改约束逻辑,先以Shift为遍历起点,通过日期相等的条件关联DaySetting,再按DaySetting.weekNo分组汇总时长:
Constraint workingHoursLessThanMaximumInContractPerWeek(ConstraintFactory constraintFactory) { return constraintFactory.forEach(Shift.class) // 通过日期匹配关联Shift和对应的DaySetting .join(DaySetting.class, Joiners.equal(Shift::getStartDate, DaySetting::getBusinessDate)) // 按DaySetting的weekNo分组,汇总该周所有Shift的时长 .groupBy((shift, daySetting) -> daySetting.getWeekNo(), ConstraintCollectors.sumLong((shift, daySetting) -> shift.getShiftDuration())) // 按汇总的时长执行惩罚(可根据实际需求调整惩罚规则) .penalize(HardMediumSoftScore.ONE_SOFT, (weekNo, totalMinutes) -> totalMinutes) .asConstraint("Working Hours Greater Than Maximum In Contract Per Week"); }
逻辑说明
- 遍历起点选择:以
Shift为起始,因为核心统计对象是班次的时长数据。 - 关联匹配:使用
Joiners.equal精准匹配Shift的业务日期与DaySetting的业务日期,确保每个班次关联到正确的日期配置。 - 分组汇总:按
DaySetting.weekNo分组,通过ConstraintCollectors.sumLong累加该周内所有匹配班次的时长。 - 惩罚逻辑:示例中直接以总时长作为惩罚值,实际可根据业务需求调整(比如与周最大允许时长对比后计算惩罚差值)。
注意事项
- 确保所有
Shift的startDate都能匹配到对应的DaySetting,避免统计遗漏。 - 提前保证
DaySetting中同一业务日期的数据唯一性,防止同一班次被重复统计。
内容的提问来源于stack exchange,提问作者I. Ahmed
相关产品推荐
相关产品推荐

