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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 02:48:10