You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OptaPlanner排班硬约束失效:班次间隔不足未扣硬分问题

问题现象

使用OptaPlanner实现员工班次排班时,定义了硬约束atLeast12HoursBetweenTwoShifts,要求同一员工两次班次间隔不得少于12小时,违规时扣除12-间隔小时数的硬分。但运行后发现员工ID6的班次(2022-11-24 20:00~23:00与2022-11-25 09:00~12:00,间隔10小时)违反规则却未触发扣分,最终得分显示0hard/-105soft。

相关日志片段:

15:24:02.832 [main        ] INFO  Solving started: time spent (43), best score (-25init/0hard/0soft), environment mode (REPRODUCIBLE), move thread count (NONE), random (JDK with seed 0).
15:24:02.881 [main        ] INFO  Construction Heuristic phase (0) ended: time spent (92), best score (0hard/-109soft), score calculation speed (3682/sec), step total (25).
15:24:07.789 [main        ] INFO  Local Search phase (1) ended: time spent (5000), best score (0hard/-105soft), score calculation speed (136883/sec), step total (33460).
15:24:07.790 [main        ] INFO  Solving ended: time spent (5000), best score (0hard/-105soft), score calculation speed (134313/sec), phase total (2), environment mode (REPRODUCIBLE), move thread count (NONE).

违规班次分配:

ShiftAssignment{shiftId='3', employeeId=6, employeeRoles=[Design, Dev], roleRequired='Dev', time=2022-11-24,20:00~23:00}
ShiftAssignment{shiftId='1', employeeId=6, employeeRoles=[Design, Dev], roleRequired='Design', time=2022-11-25,09:00~12:00}
原因分析

核心问题出在Employee类的equals/hashCode实现:

  • 使用@Data注解自动生成的equals和hashCode方法会比较类中所有字段(id、name、roleSet)。
  • 如果数据生成时,给不同ShiftAssignment分配的Employee是不同实例(即使id相同),这两个Employee对象的equals结果为false。
  • 约束atLeast12HoursBetweenTwoShifts中使用Joiners.equal(ShiftAssignment::getEmployee)匹配同一员工的班次,由于equals不匹配,无法识别出这两个班次属于同一员工,因此约束逻辑未触发。
解决方案

1. 修正Employee类的equals和hashCode逻辑

修改Employee类,仅基于id字段生成equals和hashCode,确保同一id的员工被视为同一对象:

@Data
@EqualsAndHashCode(of = "id")
public class Employee {

    private Long id;

    private String name;

    private Set<String> roleSet;

    public Employee(Long id, String name, Set<String> roleSet) {
        this.id = id;
        this.name = name;
        this.roleSet = roleSet;
    }

    public Employee() {
    }
}
  • 使用@EqualsAndHashCode(of = "id")指定仅以id作为equals和hashCode的判断依据,覆盖@Data自动生成的逻辑。

2. 验证约束逻辑(可选)

确认约束中的时间比较逻辑正确:

  • 确保Shift.getEndAt()和Shift.getStartAt()返回的是LocalDateTime类型,Duration.between()能正确计算时间间隔。
  • 检查forEachUniquePair的Join条件,Joiners.lessThanOrEqual确保仅匹配前一班次结束时间早于后一班次开始时间的配对,避免重复计算。

3. 重新运行求解器

修改后重新运行,此时约束会正确识别同一员工的连续班次,违规时会扣除对应的硬分。

内容的提问来源于stack exchange,提问作者Jingyu Xiang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 14:50:34