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

如何在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");

修正后的约束规则

核心问题分析

  1. 原代码用join(Shift.class)会产生重复配对(如shift1&shift2、shift2&shift1),导致逻辑冗余且分组错误。
  2. 缺少提取班次日期、计算时长的必要方法,直接比较LocalDateTime对象的逻辑不成立。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:04:56