如何在TimeFold约束规则中使用sum聚合函数?员工排班场景求助
问题
定制TimeFold员工排班场景,定义了Shift和Employee类,需求为:当同一员工被分配同一天的多个班次时,汇总班次总时长(分钟数)并以此数值进行惩罚。现有规则代码存在错误,请求修正。
Shift类代码
public class Shift { @PlanningId private String id; private LocalDateTime start; private LocalDateTime end; private String location; private String requiredSkill; @PlanningVariable(allowsUnassigned = true) private Employee employee; private HashMap<String, HardMediumSoftScore> scoreMap = new HashMap<String, HardMediumSoftScore>(); // 补充计算班次时长的方法 public long getDurationInMinutes() { return Duration.between(start, end).toMinutes(); } // 补充提取班次所属日期的方法 public LocalDate getDate() { return start.toLocalDate(); } }
Employee类代码
public class Employee { @PlanningId private String name; private Set<String> skills; private Contract contracts; }
错误的规则代码
constraintFactory.forEach(Shift.class) .join(Shift.class) .filter((shift1, shift2) -> shift1.getEmployee() != null && shift2.getEmployee() != null) .filter((shift1, shift2) -> shift1.getEmployee() == shift2.getEmployee()) .filter((shift1, shift2)-> shift1.getStartDate() == shift2.getStartDate()) .groupBy((shift1, shift2)->sum(shift1.getDuration())) // 该行报错 .penalize(HardMediumSoftScore.ONE_MEDIUM) // 需要用计算出的总时长作为惩罚值 .indictWith((shift1, shift2)->List.of(new RuleShiftKey(shift1.getId(), "Rule1"))) // 需要获取班次对象的ID .asConstraint("Daywise score filter");
修正后的约束规则
核心问题分析
- 原代码用
join(Shift.class)会产生重复配对(如shift1&shift2、shift2&shift1),导致逻辑冗余且分组错误。 - 缺少提取班次日期、计算时长的必要方法,直接比较
LocalDateTime对象的逻辑不成立。 groupBy用法不符合TimeFold约束API规范,未正确按员工+日期维度分组聚合。
正确代码实现
constraintFactory.forEach(Shift.class) .filter(shift -> shift.getEmployee() != null) // 仅处理已分配员工的班次 .groupBy( Shift::getEmployee, // 分组维度1:员工 Shift::getDate, // 分组维度2:班次所属日期 sum(Shift::getDurationInMinutes), // 聚合:该员工当日总班次时长 toList() // 保留分组内的所有班次对象,用于后续关联 ) .filter((employee, date, totalMinutes, shifts) -> totalMinutes > 0) // 过滤无有效时长的分组(可选) .penalize( "Daywise total shift duration penalty", HardMediumSoftScore.ONE_MEDIUM, (employee, date, totalMinutes, shifts) -> totalMinutes // 用总时长作为惩罚权重 ) .indictWith((employee, date, totalMinutes, shifts) -> shifts.stream() .map(shift -> new RuleShiftKey(shift.getId(), "Rule1")) .collect(Collectors.toList()) ) // 关联分组内所有涉及的班次 .asConstraint("Daywise score filter");
关键修改说明
- 补充必要方法:在
Shift类中添加getDurationInMinutes()计算单班次时长,getDate()提取班次所属日期(从LocalDateTime转为LocalDate)。 - 优化分组逻辑:通过
groupBy直接按员工+日期维度分组,同时聚合总时长并保留分组内的班次对象,避免重复配对问题。 - 绑定惩罚权重:将聚合得到的总时长作为惩罚的权重值,满足需求。
- 关联班次对象:在
indictWith中通过分组保留的班次列表,生成对应的RuleShiftKey。
内容的提问来源于stack exchange,提问作者I. Ahmed
相关产品推荐
相关产品推荐

