feat: 🗓️ 新增任务块排期能力并完善课程与日程模型

Version: 0.1.1.dev.260207

- 新增并测试通过将任务块排进日程接口 
- 批量导入课程接口增加单双周功能,支持只在单双周上课的课程 📚
- 任务块时间定位逻辑调整为「第几周-周几」模式 🧭

refactor: 🔨 重构时间与日程数据结构

- 完成绝对日期与相对时间的转换逻辑 🔄
  - 后续可根据需求灵活决定时间的传入与输出类型
- 再次重构 schedule 表单结构
  - 拆分为 schedule_event(单)与 schedule(多)
  - 建立前者对后者的一对多关系 🧩

fix: 🐛 大幅调整表结构与业务逻辑,修复大量历史遗留 bug 🔥
This commit is contained in:
LoveLosita
2026-02-07 16:33:30 +08:00
parent 132b7095ac
commit f4bea0576c
17 changed files with 546 additions and 40 deletions

View File

@@ -9,10 +9,11 @@ type UserCheckCourseRequest struct {
Location string `json:"location"`
IsAllowTasks bool `json:"is_allow_tasks"`
Arrangements []struct {
StartWeek int `json:"start_week"`
EndWeek int `json:"end_week"`
DayOfWeek int `json:"day_of_week"`
StartSection int `json:"start_section"`
EndSection int `json:"end_section"`
StartWeek int `json:"start_week"`
EndWeek int `json:"end_week"`
DayOfWeek int `json:"day_of_week"`
StartSection int `json:"start_section"`
EndSection int `json:"end_section"`
WeekType string `json:"week_type"`
} `json:"arrangements"`
}

View File

@@ -1,14 +1,26 @@
package model
type ScheduleEvent struct {
ID int `gorm:"primaryKey;autoIncrement" json:"id"`
UserID int `gorm:"column:user_id;index:idx_user_events;not null" json:"user_id"`
Name string `gorm:"column:name;type:varchar(255);not null;comment:课程或任务名称" json:"name"`
Location *string `gorm:"column:location;type:varchar(255);default:'';comment:地点 (教学楼/会议室)" json:"location"`
Type string `gorm:"column:type;type:enum('course','task');not null;comment:日程类型" json:"type"`
RelID *int `gorm:"column:rel_id;comment:关联原始数据ID (如教务系统的课程ID)" json:"rel_id"`
CanBeEmbedded bool `gorm:"column:can_be_embedded;not null;default:0;comment:是否允许在此时段嵌入其他任务" json:"can_be_embedded"`
}
type Schedule struct {
ID int `gorm:"primaryKey;autoIncrement" json:"id"`
UserID int `gorm:"column:user_id;index" json:"user_id"`
Type string `gorm:"type:enum('course','task');comment:course / task" json:"type"`
RelID int `gorm:"column:rel_id;comment:关联 course_id 或 task_item_id" json:"rel_id"`
EmbeddedTaskID *int `gorm:"column:embedded_task_id;index;comment:若为水课嵌入记录任务ID" json:"embedded_task_id"`
Week int `gorm:"column:week;uniqueIndex:idx_user_slot_atomic,priority:2;comment:次 (1-25)" json:"week"`
DayOfWeek int `gorm:"column:day_of_week;uniqueIndex:idx_user_slot_atomic,priority:3;comment:星期 (1-7)" json:"day_of_week"`
Section int `gorm:"column:section;uniqueIndex:idx_user_slot_atomic,priority:4;comment:原子化节次 (1-12)" json:"section"`
Status string `gorm:"type:enum('normal','interrupted');default:normal" json:"status"`
CanBeEmbedded bool `gorm:"column:can_be_embedded;not null;comment:是否允许嵌入任务" json:"can_be_embedded"`
EventID int `gorm:"column:event_id;index:idx_event_id;not null;comment:关联元数据ID" json:"event_id"`
UserID int `gorm:"column:user_id;uniqueIndex:idx_user_slot_atomic,priority:1;not null;comment:冗余UID方便直接查询" json:"user_id"`
Week int `gorm:"column:week;uniqueIndex:idx_user_slot_atomic,priority:2;not null;comment:周次 (1-25)" json:"week"`
DayOfWeek int `gorm:"column:day_of_week;uniqueIndex:idx_user_slot_atomic,priority:3;not null;comment:星期 (1-7)" json:"day_of_week"`
Section int `gorm:"column:section;uniqueIndex:idx_user_slot_atomic,priority:4;not null;comment:原子化节次 (1-12)" json:"section"`
EmbeddedTaskID *int `gorm:"column:embedded_task_id;comment:若为水课嵌入记录具体的任务项ID" json:"embedded_task_id"`
Status string `gorm:"column:status;type:enum('normal','interrupted');default:'normal';comment:状态: 正常/因故中断" json:"status"`
}
func (ScheduleEvent) TableName() string { return "schedule_events" }
func (Schedule) TableName() string { return "schedules" }

View File

@@ -64,9 +64,10 @@ type UserAddTaskClassItemRequest struct {
// TargetTime 表示任务块的目标时间
type TargetTime struct {
Date string `json:"date"` // 例: 2025-12-22
SectionFrom int `json:"section_from"` // 起始节次
SectionTo int `json:"section_to"` // 结束节次
Week int `json:"week"` // 周次
DayOfWeek int `json:"day_of_week"` // 星期几
SectionFrom int `json:"section_from"` // 起始节次
SectionTo int `json:"section_to"` // 结束节次
}
// UserGetTaskClassesResponse 用于返回用户的任务类列表,展示简要信息
@@ -85,6 +86,14 @@ type TaskClassSummary struct {
TotalSlots int `json:"total_slots"`
}
type UserInsertTaskClassItemToScheduleRequest struct {
Week int `json:"week" binding:"required,min=1"`
DayOfWeek int `json:"day_of_week" binding:"required,min=1,max=7"`
StartSection int `json:"start_section" binding:"required,min=1"`
EndSection int `json:"end_section" binding:"required,min=1,gtefield=StartSection"`
EmbedCourseEventID int `json:"embed_course_event_id"` // 可选,嵌入的课程日程事件 ID
}
// Value 实现 driver.Valuer 接口,负责将 TargetTime 转换为数据库存储的格式
func (t *TargetTime) Value() (driver.Value, error) {
if t == nil {