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

如何避免Timefold约束参数硬编码?修改失败求技术指引

Timefold动态修改约束参数的可行方案

核心解决方案:使用官方约束参数机制

针对硬编码约束无法动态修改的问题,Timefold官方提供了ConstraintParameters机制,以下是适配Quarkus+Timefold 1.13.0版本的完整步骤:

1. 定义约束参数类

创建承载约束参数的类,使用@ConstraintParameters注解(注意拼写,避免之前的多写字母错误):

import ai.timefold.solver.core.api.score.stream.ConstraintParameters;

@ConstraintParameters
public class EmployeeSchedulingConstraintParams {
    private int minHoursBetweenShifts = 10; // 默认值

    // Getter和Setter
    public int getMinHoursBetweenShifts() {
        return minHoursBetweenShifts;
    }

    public void setMinHoursBetweenShifts(int minHoursBetweenShifts) {
        this.minHoursBetweenShifts = minHoursBetweenShifts;
    }
}

2. 在规划解决方案类中绑定参数

在EmployeeSchedule类中添加约束参数字段,并用@ConstraintConfiguration注解标记:

import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.score.stream.ConstraintConfiguration;

@PlanningSolution
public class EmployeeSchedule {
    // 原有字段(员工、班次等)...

    @ConstraintConfiguration
    private EmployeeSchedulingConstraintParams constraintParams;

    // Getter和Setter
    public EmployeeSchedulingConstraintParams getConstraintParams() {
        return constraintParams;
    }

    public void setConstraintParams(EmployeeSchedulingConstraintParams constraintParams) {
        this.constraintParams = constraintParams;
    }
}

3. 在约束提供者中获取并使用参数

修改EmployeeSchedulingConstraintProvider,从ConstraintFactory中获取参数替换硬编码值:

import ai.timefold.solver.core.api.score.stream.Constraint;
import ai.timefold.solver.core.api.score.stream.ConstraintFactory;
import ai.timefold.solver.core.api.score.stream.ConstraintProvider;
import ai.timefold.solver.core.api.score.stream.Joiners;
import java.time.temporal.ChronoUnit;

public class EmployeeSchedulingConstraintProvider implements ConstraintProvider {

    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[] {
            atLeastMinHoursBetweenShifts(constraintFactory)
        };
    }

    private Constraint atLeastMinHoursBetweenShifts(ConstraintFactory constraintFactory) {
        // 获取约束参数
        EmployeeSchedulingConstraintParams params = constraintFactory.getConstraintConfiguration(EmployeeSchedulingConstraintParams.class);
        int requiredHours = params.getMinHoursBetweenShifts();

        return constraintFactory.from(Shift.class)
                .join(Shift.class,
                        Joiners.equal(Shift::getEmployee),
                        Joiners.lessThan(Shift::getEndTime, Shift::getStartTime))
                .filter((prevShift, nextShift) -> {
                    long hoursBetween = ChronoUnit.HOURS.between(prevShift.getEndTime(), nextShift.getStartTime());
                    return hoursBetween < requiredHours;
                })
                .penalize("At least " + requiredHours + " hours between shifts", HardSoftScore.ONE_HARD)
                .asConstraint();
    }
}

4. 在业务逻辑中动态设置参数

在EmployeeScheduleResource的solve方法中,可正常注入Repository(Quarkus环境支持),加载或手动设置参数后绑定到EmployeeSchedule:

import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
import ai.timefold.solver.core.api.solver.SolverManager;

@Path("/employee-schedule")
public class EmployeeScheduleResource {

    @Inject
    SolverManager<EmployeeSchedule, Long> solverManager;
    @Inject
    YourConstraintRepository constraintRepo; // 自定义参数存储Repository

    @POST
    @Path("/solve")
    public Response solve(EmployeeSchedule employeeSchedule) {
        // 方式1:直接设置参数(例如改为5小时)
        EmployeeSchedulingConstraintParams params = new EmployeeSchedulingConstraintParams();
        params.setMinHoursBetweenShifts(5);

        // 方式2:从数据库加载配置好的参数
        // ConstraintConfig dbConfig = constraintRepo.findById(1L);
        // params.setMinHoursBetweenShifts(dbConfig.getMinHoursBetweenShifts());

        employeeSchedule.setConstraintParams(params);
        SolverJob<EmployeeSchedule, Long> job = solverManager.solve(1L, employeeSchedule);
        // 后续异步返回或同步等待结果处理
        return Response.accepted().build();
    }
}

之前尝试失败原因说明

  • 尝试1:ConstraintProvider由Timefold Solver实例化,不属于Quarkus的CDI容器,无法直接注入Repository,需在Quarkus管理的Resource层获取参数后传递给Solution。
  • 尝试2:仅在EmployeeSchedule添加字段但未使用@ConstraintConfiguration注解,Timefold无法识别该参数,无法传递给ConstraintFactory。
  • 尝试3:存在注解拼写错误(ConstraintPararameters应为ConstraintParameters),且误用注解(Timefold 1.13中使用@ConstraintConfiguration而非@PlanningFactProperty)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 15:57:08