Version:0.1.0.dev.260205
feat: 🆕 完善course模块功能并优化批量导入接口 - 调整 task-class 模型代码并增加注释,使其更简洁易读 ✍️ - 结构调整:将课程相关接口从 schedule 分类移至独立的 course 分类 🚀 - 修改接口 URL 从 /schedule 更改为 /course 🔄 fix: 🐛 修复批量导入课程接口重复导入相同课程的 bug - 通过在数据库添加唯一约束解决此问题 🔐 - 这只是初步修复,后续会在 sv 层增加重复/时间冲突检测逻辑 ⚠️ - 引导用户修改课表与任务块时间冲突的机制待实现 ⏳ refactor: 🔨 重构 schedule 表单结构 - 将节次管理策略从字符串存储改为原子化存储(如 1-2 节更改为单独两条记录) - 为后续冲突检查与智能排课做准备 🧠 perf: 🚀 优化批量导入课程接口性能 - 通过数据暂存内存中减少数据插入 MySQL 的次数 ⚡
This commit is contained in:
18
backend/model/course.go
Normal file
18
backend/model/course.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
type UserImportCoursesRequest struct {
|
||||
Courses []UserCheckCourseRequest `json:"courses"`
|
||||
}
|
||||
|
||||
type UserCheckCourseRequest struct {
|
||||
CourseName string `json:"course_name"`
|
||||
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"`
|
||||
} `json:"arrangements"`
|
||||
}
|
||||
@@ -3,29 +3,12 @@ package model
|
||||
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"`
|
||||
CanBeEmbedded bool `gorm:"column:can_be_embedded;comment:'是否允许嵌入水课'" json:"can_be_embedded"`
|
||||
EmbeddedTaskID *uint `gorm:"column:embedded_task_id;comment:'若为水课嵌入,记录任务ID'" json:"embedded_task_id"`
|
||||
Week int `gorm:"column:week" json:"week"`
|
||||
DayOfWeek int `gorm:"column:day_of_week" json:"day_of_week"`
|
||||
Sections string `gorm:"type:varchar(255)" json:"sections"`
|
||||
Status string `gorm:"type:enum('normal','interrupted');default:'normal'" json:"status"`
|
||||
}
|
||||
|
||||
type UserImportCoursesRequest struct {
|
||||
Courses []UserCheckCourseRequest `json:"courses"`
|
||||
}
|
||||
|
||||
type UserCheckCourseRequest struct {
|
||||
CourseName string `json:"course_name"`
|
||||
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"`
|
||||
} `json:"arrangements"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TaskClass 用于和数据库中的 task_classes 表进行映射
|
||||
type TaskClass struct {
|
||||
//section 1
|
||||
ID int `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
@@ -24,33 +25,7 @@ type TaskClass struct {
|
||||
Items []TaskClassItem `gorm:"foreignKey:CategoryID;references:ID"` // 一对多关联:一个 TaskClass 有多个 TaskClassItem
|
||||
}
|
||||
|
||||
// TableName 设定 TaskClass 的表名为 task_classes
|
||||
func (TaskClass) TableName() string {
|
||||
return "task_classes"
|
||||
}
|
||||
|
||||
type UserAddTaskClassRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
StartDate string `json:"start_date" binding:"required"` // YYYY-MM-DD
|
||||
EndDate string `json:"end_date" binding:"required"` // YYYY-MM-DD
|
||||
Mode string `json:"mode" binding:"required,oneof=auto manual"`
|
||||
Config UserAddTaskClassConfig `json:"config" binding:"required"`
|
||||
Items []UserAddTaskClassItemRequest `json:"items" binding:"required"`
|
||||
}
|
||||
|
||||
type UserAddTaskClassConfig struct {
|
||||
TotalSlots int `json:"total_slots" binding:"required,min=1"`
|
||||
AllowFillerCourse bool `json:"allow_filler_course"`
|
||||
Strategy string `json:"strategy" binding:"required,oneof=steady rapid"`
|
||||
ExcludedSlots []int `json:"excluded_slots"`
|
||||
}
|
||||
|
||||
type UserAddTaskClassItemRequest struct {
|
||||
Order int `json:"order" binding:"required,min=1"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
EmbeddedTime *TargetTime `json:"embedded_time"` // 例: 2025-12-22 1-2节; nil 表示未安排
|
||||
}
|
||||
|
||||
// TaskClassItem 用于和数据库中的 task_items 表进行映射
|
||||
type TaskClassItem struct {
|
||||
//section 1
|
||||
ID int `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
@@ -62,12 +37,55 @@ type TaskClassItem struct {
|
||||
Status *int `gorm:"column:status;comment:1:未安排, 2:已应用"`
|
||||
}
|
||||
|
||||
// UserAddTaskClassRequest 用于处理用户添加任务类别的请求
|
||||
type UserAddTaskClassRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
StartDate string `json:"start_date" binding:"required"` // YYYY-MM-DD
|
||||
EndDate string `json:"end_date" binding:"required"` // YYYY-MM-DD
|
||||
Mode string `json:"mode" binding:"required,oneof=auto manual"`
|
||||
Config UserAddTaskClassConfig `json:"config" binding:"required"`
|
||||
Items []UserAddTaskClassItemRequest `json:"items" binding:"required"`
|
||||
}
|
||||
|
||||
// UserAddTaskClassConfig 用于处理用户添加任务类别时的配置部分
|
||||
type UserAddTaskClassConfig struct {
|
||||
TotalSlots int `json:"total_slots" binding:"required,min=1"`
|
||||
AllowFillerCourse bool `json:"allow_filler_course"`
|
||||
Strategy string `json:"strategy" binding:"required,oneof=steady rapid"`
|
||||
ExcludedSlots []int `json:"excluded_slots"`
|
||||
}
|
||||
|
||||
// UserAddTaskClassItemRequest 用于处理用户添加任务类别时的任务块部分
|
||||
type UserAddTaskClassItemRequest struct {
|
||||
Order int `json:"order" binding:"required,min=1"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
EmbeddedTime *TargetTime `json:"embedded_time"` // 例: 2025-12-22 1-2节; nil 表示未安排
|
||||
}
|
||||
|
||||
// TargetTime 表示任务块的目标时间
|
||||
type TargetTime struct {
|
||||
Date string `json:"date"` // 例: 2025-12-22
|
||||
SectionFrom int `json:"section_from"` // 起始节次
|
||||
SectionTo int `json:"section_to"` // 结束节次
|
||||
}
|
||||
|
||||
// UserGetTaskClassesResponse 用于返回用户的任务类列表,展示简要信息
|
||||
type UserGetTaskClassesResponse struct {
|
||||
TaskClasses []TaskClassSummary `json:"task_classes"`
|
||||
}
|
||||
|
||||
// TaskClassSummary 提供任务类别的简要信息
|
||||
type TaskClassSummary struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode"`
|
||||
Strategy string `json:"strategy"`
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
TotalSlots int `json:"total_slots"`
|
||||
}
|
||||
|
||||
// Value 实现 driver.Valuer 接口,负责将 TargetTime 转换为数据库存储的格式
|
||||
func (t *TargetTime) Value() (driver.Value, error) {
|
||||
if t == nil {
|
||||
return nil, nil
|
||||
@@ -77,6 +95,7 @@ func (t *TargetTime) Value() (driver.Value, error) {
|
||||
return json.Marshal(t)
|
||||
}
|
||||
|
||||
// Scan 实现 sql.Scanner 接口,负责将数据库中的值转换为 TargetTime 结构体
|
||||
func (t *TargetTime) Scan(value any) error {
|
||||
if value == nil {
|
||||
// 如果数据库是 NULL,保持指针对应的对象为零值即可
|
||||
@@ -97,25 +116,18 @@ func (t *TargetTime) Scan(value any) error {
|
||||
return json.Unmarshal(data, t)
|
||||
}
|
||||
|
||||
// TableName 指定 TaskClass 对应的数据库表名
|
||||
func (TaskClass) TableName() string {
|
||||
return "task_classes"
|
||||
}
|
||||
|
||||
// TableName 指定 TaskClassItem 对应的数据库表名
|
||||
func (TaskClassItem) TableName() string {
|
||||
return "task_items"
|
||||
}
|
||||
|
||||
// 任务块状态常量
|
||||
const (
|
||||
TaskItemStatusUnscheduled = 1
|
||||
TaskItemStatusApplied = 2
|
||||
TaskItemStatusUnscheduled = 1 // 未安排
|
||||
TaskItemStatusApplied = 2 // 已应用
|
||||
)
|
||||
|
||||
type UserGetTaskClassesResponse struct {
|
||||
TaskClasses []TaskClassSummary `json:"task_classes"`
|
||||
}
|
||||
|
||||
type TaskClassSummary struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode"`
|
||||
Strategy string `json:"strategy"`
|
||||
StartDate time.Time `json:"start_date"`
|
||||
EndDate time.Time `json:"end_date"`
|
||||
TotalSlots int `json:"total_slots"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user