如何避免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
相关产品推荐
相关产品推荐

