Version: 0.3.6.dev.260223

feat: 🚀 新增智能编排日程接口与算法模块

* 新增智能编排日程接口,实现自动生成周维度课程安排
* 抽离核心算法至 `Logic` 包,统一存放调度与排课相关算法逻辑,优化项目结构分层
* 大多数用例测试通过,当前存在少量边界用例下“排课时间是否充足”的误判问题
* 返回的周视图数据在极端场景下存在数量偏差,待进一步完善边界控制

fix: 🐛 修复批量导入课程接口 500 错误

* 修复批量导入课程接口中未在 `event` 结构体填写时间字段的问题
* 解决因时间字段为空导致的服务端 500 错误,保证数据完整性

refactor: ♻️ 新增入参校验逻辑保障调度稳定性

* 在添加任务类时新增入参校验逻辑
* 避免非法数据进入调度流程,确保自动编排日程接口执行稳定

docs: 📚 更新 README 智能编排算法说明

* 补充智能编排日程算法的设计思路与实现说明

undo: ⚠️ 追加导入课程后缓存未自动失效

* 追加导入课程后未自动删除对应周安排缓存,存在数据不一致风险
* 当前未能稳定复现,计划后续定位缓存失效时序与触发条件问题
This commit is contained in:
LoveLosita
2026-02-23 21:49:46 +08:00
parent 9cf288c49b
commit f934668838
16 changed files with 703 additions and 11 deletions

View File

@@ -78,6 +78,10 @@ func (ss *CourseService) AddUserCourses(ctx context.Context, req model.UserImpor
continue
}
//2.转换为 Schedule_event 切片
st, ed, err := conv.RelativeTimeToRealTime(week, arrangement.DayOfWeek, arrangement.StartSection, arrangement.EndSection)
if err != nil {
return nil, err
}
scheduleEvent := model.ScheduleEvent{
UserID: userID,
Name: course.CourseName,
@@ -85,6 +89,8 @@ func (ss *CourseService) AddUserCourses(ctx context.Context, req model.UserImpor
Type: "course",
RelID: nil,
CanBeEmbedded: course.IsAllowTasks,
StartTime: st,
EndTime: ed,
}
finalScheduleEvents = append(finalScheduleEvents, scheduleEvent)
//3.转换为 Schedule 切片

View File

@@ -8,6 +8,7 @@ import (
"github.com/LoveLosita/smartflow/backend/conv"
"github.com/LoveLosita/smartflow/backend/dao"
"github.com/LoveLosita/smartflow/backend/logic"
"github.com/LoveLosita/smartflow/backend/model"
"github.com/LoveLosita/smartflow/backend/respond"
"github.com/go-redis/redis/v8"
@@ -100,6 +101,7 @@ func (ss *ScheduleService) GetUserWeeklySchedule(ctx context.Context, userID, we
}
//3.转换为前端需要的格式
weeklySchedule := conv.SchedulesToUserWeeklySchedule(schedules)
weeklySchedule.Week = week
//4.将查询结果存入缓存,设置过期时间为一周(或者根据实际情况调整)
err = ss.cacheDAO.SetUserWeeklyScheduleToCache(ctx, userID, weeklySchedule)
return weeklySchedule, nil
@@ -375,3 +377,30 @@ func (ss *ScheduleService) RevocateUserTaskClassItem(ctx context.Context, userID
}
return nil
}
func (ss *ScheduleService) SmartPlanning(ctx context.Context, userID, taskClassID int) ([]model.UserWeekSchedule, error) {
//1.通过任务类id获取任务类详情
taskClass, err := ss.taskClassDAO.GetCompleteTaskClassByID(ctx, taskClassID, userID)
if err != nil {
return nil, err
}
//2.校验任务类的参数是否合法
if taskClass == nil {
return nil, respond.WrongTaskClassID
}
if *taskClass.Mode != "auto" {
return nil, respond.TaskClassModeNotAuto
}
//3.获取任务类安排的时间范围内的全部周数信息(左右边界不足一周的情况也要算作一周)
schedules, err := ss.scheduleDAO.GetUserSchedulesByTimeRange(ctx, userID, conv.CalculateFirstDayOfWeek(*taskClass.StartDate), conv.CalculateLastDayOfWeek(*taskClass.EndDate))
if err != nil {
return nil, err
}
//4.将多个周的信息传入智能排课算法,获取推荐的时间安排(周+周内的天+节次)
result, err := logic.SmartPlanningMainLogic(schedules, taskClass)
if err != nil {
return nil, err
}
//5.将推荐的时间安排转换为前端需要的格式返回
return result, nil
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"log"
"time"
"github.com/LoveLosita/smartflow/backend/conv"
"github.com/LoveLosita/smartflow/backend/dao"
@@ -32,7 +33,27 @@ func NewTaskClassService(taskClassRepo *dao.TaskClassDAO, cacheRepo *dao.CacheDA
// AddOrUpdateTaskClass 为指定用户添加任务类
func (sv *TaskClassService) AddOrUpdateTaskClass(ctx context.Context, req *model.UserAddTaskClassRequest, userID int, method int, targetTaskClassID int) error {
// 1) 先写数据库(事务内)
//1.先校验参数
if req.Mode == "auto" {
if req.StartDate == "" || req.EndDate == "" {
return respond.MissingParamForAutoScheduling
}
st, err := time.Parse("2006-01-02", req.StartDate)
if err != nil {
return respond.WrongParamType
}
ed, err := time.Parse("2006-01-02", req.EndDate)
if err != nil {
return respond.WrongParamType
}
if st.After(ed) {
return respond.InvalidDateRange
}
}
if req.Mode == "" || req.Name == "" || len(req.Items) == 0 {
return respond.MissingParam
}
//2.写数据库(事务内)
if err := sv.taskClassRepo.Transaction(func(txDAO *dao.TaskClassDAO) error {
taskClass, items, err := conv.ProcessUserAddTaskClassRequest(req, userID)
if err != nil {