如何在Timefold Spring Boot中根据前端输入动态选择优化约束
实现Timefold Solver动态应用前端选择的约束
1. 定义约束标识符枚举
给每个约束分配唯一的标识符,方便前端传递参数和后端匹配对应约束:
public enum ConstraintId { ROOM_CONFLICT, TEACHER_CONFLICT, STUDENT_GROUP_CONFLICT, TEACHER_ROOM_STABILITY, TEACHER_TIME_EFFICIENCY, STUDENT_GROUP_SUBJECT_VARIETY }
2. 后端接收前端约束选择
创建接口接收前端传递的约束列表,将其绑定到求解的问题实体的约束配置中:
@RestController @RequestMapping("/timetable/solve") public class TimetableSolverController { private final SolverManager<Timetable, Long> solverManager; public TimetableSolverController(SolverManager<Timetable, Long> solverManager) { this.solverManager = solverManager; } @PostMapping public ResponseEntity<SolverJob<Timetable, Long>> startSolving( @RequestBody SolveRequest request) { // 将选中的约束列表存入时间表实体的约束配置 Timetable timetable = request.getTimetable(); TimetableConstraintConfiguration config = new TimetableConstraintConfiguration(); config.setSelectedConstraints(new HashSet<>(request.getSelectedConstraintIds())); timetable.setConstraintConfiguration(config); SolverJob<Timetable, Long> solverJob = solverManager.solve( request.getTimetableId(), timetable, solvedTimetable -> { // 求解完成后的回调逻辑(可选) }); return ResponseEntity.accepted().body(solverJob); } // 请求DTO public static class SolveRequest { private Long timetableId; private Timetable timetable; private List<ConstraintId> selectedConstraintIds; // Getter和Setter } }
3. 创建约束配置类
使用@ConstraintConfiguration注解标记配置类,让Timefold能够识别并在约束定义中访问配置内容:
@ConstraintConfiguration public class TimetableConstraintConfiguration { private Set<ConstraintId> selectedConstraints; // Getter和Setter }
在你的Timetable实体中添加该配置字段:
public class Timetable { // 原有字段(如课程、教室、教师等) private TimetableConstraintConfiguration constraintConfiguration; // Getter和Setter }
4. 修改ConstraintProvider动态筛选约束
更新TimetableConstraintProvider,根据前端选择的约束列表动态生成要应用的约束数组:
public class TimetableConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { // 获取约束配置 TimetableConstraintConfiguration config = constraintFactory.from(TimetableConstraintConfiguration.class) .uniqueResult(); Set<ConstraintId> selectedConstraints = config.getSelectedConstraints(); List<Constraint> activeConstraints = new ArrayList<>(); // 根据选择添加硬约束 if (selectedConstraints.contains(ConstraintId.ROOM_CONFLICT)) { activeConstraints.add(roomConflict(constraintFactory)); } if (selectedConstraints.contains(ConstraintId.TEACHER_CONFLICT)) { activeConstraints.add(teacherConflict(constraintFactory)); } if (selectedConstraints.contains(ConstraintId.STUDENT_GROUP_CONFLICT)) { activeConstraints.add(studentGroupConflict(constraintFactory)); } // 根据选择添加软约束 if (selectedConstraints.contains(ConstraintId.TEACHER_ROOM_STABILITY)) { activeConstraints.add(teacherRoomStability(constraintFactory)); } if (selectedConstraints.contains(ConstraintId.TEACHER_TIME_EFFICIENCY)) { activeConstraints.add(teacherTimeEfficiency(constraintFactory)); } if (selectedConstraints.contains(ConstraintId.STUDENT_GROUP_SUBJECT_VARIETY)) { activeConstraints.add(studentGroupSubjectVariety(constraintFactory)); } return activeConstraints.toArray(new Constraint[0]); } // 原有约束实现方法保持不变 private Constraint roomConflict(ConstraintFactory constraintFactory) { // 原实现逻辑 return constraintFactory.from(Lesson.class) .join(Lesson.class, Joiners.equal(Lesson::getRoom), Joiners.equal(Lesson::getTimeslot), Joiners.lessThan(Lesson::getId)) .penalize("Room conflict", HardSoftScore.ONE_HARD) .asConstraint(); } // 其他约束方法... }
5. 可选:前端约束选择校验
如果某些硬约束是求解的必要条件(比如必须避免教室冲突),后端可以在接收请求时添加校验逻辑:
// 在startSolving方法中添加校验 Set<ConstraintId> mandatoryHardConstraints = Set.of( ConstraintId.ROOM_CONFLICT, ConstraintId.TEACHER_CONFLICT, ConstraintId.STUDENT_GROUP_CONFLICT); if (!selectedConstraints.containsAll(mandatoryHardConstraints)) { throw new IllegalArgumentException("必须选择所有硬约束以保证求解可行性"); }
内容的提问来源于stack exchange,提问作者Diallo Francis Patrick
相关产品推荐
相关产品推荐

