package conv import ( "fmt" "github.com/LoveLosita/smartflow/backend/model" ) import "sort" func SchedulesToScheduleConflictDetail(schedules []model.Schedule) []model.ScheduleConflictDetail { if len(schedules) == 0 { return []model.ScheduleConflictDetail{} } // 1. 使用 Map 进行逻辑分组 // Key 格式: EventID-Week-Day (防止同一事件在不同天出现时被混为一谈) groups := make(map[string]*model.ScheduleConflictDetail) for _, s := range schedules { key := fmt.Sprintf("%d-%d-%d", s.EventID, s.Week, s.DayOfWeek) if _, ok := groups[key]; !ok { // 初始化该分组 groups[key] = &model.ScheduleConflictDetail{ EventID: s.EventID, Name: s.Event.Name, Location: *s.Event.Location, // 假设字段是 *string Type: s.Event.Type, Week: s.Week, DayOfWeek: s.DayOfWeek, } } // 将当前节次加入数组 groups[key].Sections = append(groups[key].Sections, s.Section) } // 2. 处理每个分组的区间逻辑 res := make([]model.ScheduleConflictDetail, 0, len(groups)) for _, detail := range groups { // 排序节次,例如把 [3, 1, 2] 变成 [1, 2, 3] sort.Ints(detail.Sections) // 最小值即起始,最大值即结束 detail.StartSection = detail.Sections[0] detail.EndSection = detail.Sections[len(detail.Sections)-1] res = append(res, *detail) } // 3. 可选:对结果集按时间排序,让前端收到的 DTO 也是有序的 sort.Slice(res, func(i, j int) bool { if res[i].Week != res[j].Week { return res[i].Week < res[j].Week } if res[i].DayOfWeek != res[j].DayOfWeek { return res[i].DayOfWeek < res[j].DayOfWeek } return res[i].StartSection < res[j].StartSection }) return res } // SectionToTime 映射表:将原子节次转为起始/结束时间点 // 此处以重邮为例 var sectionTimeMap = map[int][2]string{ 1: {"08:00", "08:45"}, 2: {"08:55", "09:40"}, 3: {"10:15", "11:00"}, 4: {"11:10", "11:55"}, 5: {"14:00", "14:45"}, 6: {"14:55", "15:40"}, 7: {"16:15", "17:00"}, 8: {"17:10", "17:55"}, 9: {"19:00", "19:45"}, 10: {"19:55", "20:40"}, 11: {"20:50", "21:35"}, 12: {"21:45", "22:30"}, } func SchedulesToUserTodaySchedule(schedules []model.Schedule) []model.UserTodaySchedule { if len(schedules) == 0 { return []model.UserTodaySchedule{} } // 1. 数据预处理:按 Week-Day 分组 dayGroups := make(map[string][]model.Schedule) for _, s := range schedules { dayKey := fmt.Sprintf("%d-%d", s.Week, s.DayOfWeek) dayGroups[dayKey] = append(dayGroups[dayKey], s) } var result []model.UserTodaySchedule for _, daySchedules := range dayGroups { todayDTO := model.UserTodaySchedule{ Week: daySchedules[0].Week, DayOfWeek: daySchedules[0].DayOfWeek, Events: []model.EventBrief{}, } // 💡 关键点:建立一个 Section 查找表,方便 O(1) 确定某节课是什么 sectionMap := make(map[int]model.Schedule) for _, s := range daySchedules { sectionMap[s.Section] = s } order := 1 // 💡 线性扫描:从第 1 节巡检到第 12 节 for curr := 1; curr <= 12; { if slot, ok := sectionMap[curr]; ok { // === A 场景:当前节次有课 === // 1. 寻找该事件的连续范围(比如 9-12 节连上) // 我们向后探测,直到 EventID 变化或节次断开 end := curr for next := curr + 1; next <= 12; next++ { if nextSlot, exist := sectionMap[next]; exist && nextSlot.EventID == slot.EventID { end = next } else { break } } // 2. 封装 EventBrief brief := model.EventBrief{ ID: slot.EventID, Order: order, Name: slot.Event.Name, Location: *slot.Event.Location, Type: slot.Event.Type, StartTime: sectionTimeMap[curr][0], EndTime: sectionTimeMap[end][1], Span: end - curr + 1, } // 3. 处理嵌入任务 // 只要这几个连续节次里有一个有任务,就带上 for i := curr; i <= end; i++ { if s, exist := sectionMap[i]; exist && s.EmbeddedTask != nil { brief.EmbeddedTaskInfo = model.TaskBrief{ ID: s.EmbeddedTask.ID, Name: *s.EmbeddedTask.Content, Type: "task", } break } } todayDTO.Events = append(todayDTO.Events, brief) // 💡 指针跳跃:直接跳过已处理的节次 curr = end + 1 order++ } else { // === B 场景:当前节次没课(Type = "empty") === // 逻辑:按照学校标准大节(1-2, 3-4...)进行空位合并 // 如果当前是奇数节(1, 3, 5...)且下一节也没课,就合并成一个空块 emptyEnd := curr if curr%2 != 0 && curr < 12 { if _, nextHasClass := sectionMap[curr+1]; !nextHasClass { emptyEnd = curr + 1 } } todayDTO.Events = append(todayDTO.Events, model.EventBrief{ ID: 0, // 空课 ID 为 0 Order: order, Name: "无课", Type: "empty", StartTime: sectionTimeMap[curr][0], EndTime: sectionTimeMap[emptyEnd][1], Location: "休息时间", }) curr = emptyEnd + 1 order++ } } result = append(result, todayDTO) } return result } func SchedulesToUserWeeklySchedule(schedules []model.Schedule) *model.UserWeekSchedule { // 1. 初始化返回结构 (默认取第一条数据的周次) weekDTO := &model.UserWeekSchedule{ Week: schedules[0].Week, Events: []model.WeeklyEventBrief{}, } // 2. 建立 [天][节次] 的快速索引地图 // indexMap[day][section] -> model.Schedule indexMap := make(map[int]map[int]model.Schedule) for d := 1; d <= 7; d++ { indexMap[d] = make(map[int]model.Schedule) } for _, s := range schedules { indexMap[s.DayOfWeek][s.Section] = s } // 3. 线性扫描 1-7 天 for day := 1; day <= 7; day++ { order := 1 // 每一天开始时,内部显示顺序重置 // 4. 线性扫描 1-12 节 for curr := 1; curr <= 12; { // 场景 A:当前槽位有课/有任务 if slot, hasClass := indexMap[day][curr]; hasClass { end := curr // 探测逻辑:合并相同 EventID 的连续节次 (Span 计算) for next := curr + 1; next <= 12; next++ { if nextSlot, exist := indexMap[day][next]; exist && nextSlot.EventID == slot.EventID { end = next } else { break } } span := end - curr + 1 brief := model.WeeklyEventBrief{ ID: slot.EventID, Order: order, DayOfWeek: day, Name: slot.Event.Name, Location: *slot.Event.Location, Type: slot.Event.Type, StartTime: sectionTimeMap[curr][0], // 使用你定义的映射表 EndTime: sectionTimeMap[end][1], Span: span, } // 提取嵌入任务信息 (逻辑同前,探测整个 Span) for i := curr; i <= end; i++ { if s, exist := indexMap[day][i]; exist && s.EmbeddedTask != nil { brief.EmbeddedTaskInfo = model.TaskBrief{ ID: s.EmbeddedTask.ID, Name: *s.EmbeddedTask.Content, Type: "task", } break } } weekDTO.Events = append(weekDTO.Events, brief) curr = end + 1 // 指针跳跃到下一块 order++ } else { // 场景 B:无课 (Type="empty"),进行逻辑合并 emptyEnd := curr // 奇数节起步且下一节也空,则合并为大节 (1-2, 3-4...) if curr%2 != 0 && curr < 12 { if _, nextHasClass := indexMap[day][curr+1]; !nextHasClass { emptyEnd = curr + 1 } } weekDTO.Events = append(weekDTO.Events, model.WeeklyEventBrief{ ID: 0, Order: order, DayOfWeek: day, Name: "无课", Type: "empty", StartTime: sectionTimeMap[curr][0], EndTime: sectionTimeMap[emptyEnd][1], Span: emptyEnd - curr + 1, Location: "", }) curr = emptyEnd + 1 order++ } } } return weekDTO } func SchedulesToRecentCompletedSchedules(schedules []model.Schedule) model.UserRecentCompletedScheduleResponse { // 1. 初始化结果集 result := model.UserRecentCompletedScheduleResponse{ Events: make([]model.RecentCompletedEventBrief, 0), } if len(schedules) == 0 { return result } // 💡 核心:去重地图,key 是 EventID seen := make(map[int]bool) for _, s := range schedules { // 2. 检查这个逻辑事件是否已经添加过 if seen[s.EventID] { continue } // 3. 格式化结束时间 strTime := s.Event.EndTime.Format("2006-01-02 15:04:05") // 4. 构造 Brief temp := model.RecentCompletedEventBrief{ // 注意:这里 ID 必须改用 s.EventID (逻辑事件ID) // 否则如果你传 s.ID,前端拿到的是原子槽位的ID,依然不唯一 ID: s.EventID, Name: s.Event.Name, Type: s.Event.Type, CompletedTime: strTime, } result.Events = append(result.Events, temp) // 5. 标记该事件已处理 seen[s.EventID] = true } return result }