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

如何通过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");
}

逻辑说明

  1. 遍历起点选择:以Shift为起始,因为核心统计对象是班次的时长数据。
  2. 关联匹配:使用Joiners.equal精准匹配Shift的业务日期与DaySetting的业务日期,确保每个班次关联到正确的日期配置。
  3. 分组汇总:按DaySetting.weekNo分组,通过ConstraintCollectors.sumLong累加该周内所有匹配班次的时长。
  4. 惩罚逻辑:示例中直接以总时长作为惩罚值,实际可根据业务需求调整(比如与周最大允许时长对比后计算惩罚差值)。

注意事项

  • 确保所有Shift的startDate都能匹配到对应的DaySetting,避免统计遗漏。
  • 提前保证DaySetting中同一业务日期的数据唯一性,防止同一班次被重复统计。

内容的提问来源于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 00:32:03