diff --git a/server/internal/course_activity/internal/logic/course_activity/createcourseactivitylogic.go b/server/internal/course_activity/internal/logic/course_activity/createcourseactivitylogic.go index a1c95b13..4b5300cd 100644 --- a/server/internal/course_activity/internal/logic/course_activity/createcourseactivitylogic.go +++ b/server/internal/course_activity/internal/logic/course_activity/createcourseactivitylogic.go @@ -5,6 +5,9 @@ package course_activity import ( "context" + "database/sql" + "fmt" + "time" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/model" @@ -30,7 +33,95 @@ func NewCreateCourseActivityLogic(ctx context.Context, cfg *config.Config, model } func (l *CreateCourseActivityLogic) CreateCourseActivity(req *types.CreateCourseActivityReq) (resp *types.CreateCourseActivityResp, err error) { - // todo: add your logic here and delete this line + // 1. 基础参数校验 + if req.CourseId <= 0 { + return nil, fmt.Errorf("参数错误:课程ID必须为正整数") + } + if len(req.Title) == 0 || len(req.Title) > 255 { + return nil, fmt.Errorf("参数错误:活动标题不能为空且长度不超过255字符") + } - return + // 2. 可选参数校验与默认值设置 + // 活动类型:未传递则默认1(作业),传递则校验1-4范围 + activityType := 1 + if req.ActivityType != 0 { + if req.ActivityType < 1 || req.ActivityType > 4 { + return nil, fmt.Errorf("参数错误:活动类型必须是1-4(1-作业/2-考试/3-讨论/4-直播)") + } + activityType = req.ActivityType + } + + // 排序:未传递则默认0 + sort := 0 + if req.Sort >= 0 { + sort = req.Sort + } + + // 3. 时间参数解析与适配 sql.NullTime(核心修改) + timeLayout := "2006-01-02 15:04:05" + var startTime, endTime sql.NullTime // 改为 sql.NullTime 类型 + + // 解析开始时间:传递则解析并标记 Valid=true,未传递则 Valid=false(数据库存NULL) + if req.StartTime != "" { + parsedTime, err := time.Parse(timeLayout, req.StartTime) + if err != nil { + return nil, fmt.Errorf("参数错误:开始时间格式不正确,需符合yyyy-MM-dd HH:mm:ss") + } + startTime = sql.NullTime{Time: parsedTime, Valid: true} + } + + // 解析结束时间:逻辑同上 + if req.EndTime != "" { + parsedTime, err := time.Parse(timeLayout, req.EndTime) + if err != nil { + return nil, fmt.Errorf("参数错误:结束时间格式不正确,需符合yyyy-MM-dd HH:mm:ss") + } + endTime = sql.NullTime{Time: parsedTime, Valid: true} + } + + // 校验:若同时传递开始和结束时间,结束时间需晚于开始时间 + if startTime.Valid && endTime.Valid && endTime.Time.Before(startTime.Time) { + return nil, fmt.Errorf("参数错误:结束时间不能早于开始时间") + } + + // 4. 转换请求参数为Model层结构体(适配 sql.NullTime 和 int64) + activityModel := &model.CourseActivity{ + CourseId: int64(req.CourseId), // int→int64(适配数据库) + Title: req.Title, + ActivityType: int64(activityType), // int→int64 + Content: stringToNullString(req.Content), + StartTime: startTime, // 直接赋值 sql.NullTime + EndTime: endTime, // 直接赋值 sql.NullTime + Sort: int64(sort), // int→int64 + } + + // 5. 调用Model层插入数据 + result, err := l.model.Insert(l.ctx, activityModel) + if err != nil { + return nil, fmt.Errorf("创建课程活动失败:%w", err) + } + + // 6. 获取新增活动的ID + newId, err := result.LastInsertId() + if err != nil { + return nil, fmt.Errorf("获取新增活动ID失败:%w", err) + } + + // 7. 构造响应 + resp = &types.CreateCourseActivityResp{ + Id: int(newId), // int64→int(适配响应) + Message: "课程活动创建成功", + } + + return resp, nil +} + +func stringToNullString(s string) sql.NullString { + if s == "" { + return sql.NullString{Valid: false} + } + return sql.NullString{ + String: s, + Valid: true, + } } diff --git a/server/internal/course_activity/internal/logic/course_activity/deletecourseactivitylogic.go b/server/internal/course_activity/internal/logic/course_activity/deletecourseactivitylogic.go index 4c3e522e..89566d60 100644 --- a/server/internal/course_activity/internal/logic/course_activity/deletecourseactivitylogic.go +++ b/server/internal/course_activity/internal/logic/course_activity/deletecourseactivitylogic.go @@ -30,7 +30,18 @@ func NewDeleteCourseActivityLogic(ctx context.Context, cfg *config.Config, model } func (l *DeleteCourseActivityLogic) DeleteCourseActivity(req *types.DeleteCourseActivityReq) (resp *types.DeleteCourseActivityResp, err error) { - // todo: add your logic here and delete this line + // 将请求中的int类型ID转换为数据层需要的int64类型 + activityID := int64(req.Id) - return + // 调用数据层的删除方法 + err = l.model.Delete(l.ctx, activityID) + if err != nil { + // 若删除过程出现错误,返回错误信息 + return nil, err + } + + // 删除成功,返回操作结果 + return &types.DeleteCourseActivityResp{ + Message: "课程活动已成功删除", + }, nil } diff --git a/server/internal/course_activity/internal/logic/course_activity/getcourseactivitylogic.go b/server/internal/course_activity/internal/logic/course_activity/getcourseactivitylogic.go index 838e2892..bf30edeb 100644 --- a/server/internal/course_activity/internal/logic/course_activity/getcourseactivitylogic.go +++ b/server/internal/course_activity/internal/logic/course_activity/getcourseactivitylogic.go @@ -9,6 +9,7 @@ import ( "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/model" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/types" + "github.com/JACKYMYPERSON/hldrCenter/util" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,7 +31,28 @@ func NewGetCourseActivityLogic(ctx context.Context, cfg *config.Config, model mo } func (l *GetCourseActivityLogic) GetCourseActivity(req *types.GetCourseActivityReq) (resp *types.GetCourseActivityResp, err error) { - // todo: add your logic here and delete this line + // 将请求中的int类型ID转换为数据层需要的int64类型 + activityID := int64(req.Id) - return + // 调用数据层方法查询活动详情 + activity, err := l.model.FindOne(l.ctx, activityID) + if err != nil { + // 若查询出错(如未找到或数据库错误),返回错误 + return nil, err + } + + // 将数据层模型转换为响应结构体 + resp = &types.GetCourseActivityResp{ + Id: int(activity.Id), // 转换int64为int + CourseId: int(activity.CourseId), // 转换关联课程ID类型 + Title: activity.Title, // 活动标题 + ActivityType: int(activity.ActivityType), // 转换活动类型 + Content: util.NullStringToString(activity.Content), // 活动详情 + // 假设数据层返回的是time.Time类型,这里格式化为字符串(根据实际格式调整) + StartTime: util.NullTimeToString(activity.StartTime), + EndTime: util.NullTimeToString(activity.EndTime), + Sort: int(activity.Sort), // 转换排序字段类型 + } + + return resp, nil } diff --git a/server/internal/course_activity/internal/logic/course_activity/listcourseactivitylogic.go b/server/internal/course_activity/internal/logic/course_activity/listcourseactivitylogic.go index 578147eb..5f63a92b 100644 --- a/server/internal/course_activity/internal/logic/course_activity/listcourseactivitylogic.go +++ b/server/internal/course_activity/internal/logic/course_activity/listcourseactivitylogic.go @@ -5,10 +5,12 @@ package course_activity import ( "context" + "strings" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/model" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/types" + "github.com/JACKYMYPERSON/hldrCenter/util" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,7 +32,67 @@ func NewListCourseActivityLogic(ctx context.Context, cfg *config.Config, model m } func (l *ListCourseActivityLogic) ListCourseActivity(req *types.ListCourseActivityReq) (resp *types.ListCourseActivityResp, err error) { - // todo: add your logic here and delete this line + // 1. 处理分页参数:计算偏移量(页码从1开始) + // 2. 构建查询条件(根据可选参数拼接WHERE子句) + var whereConditions []string + var queryArgs []interface{} - return + // 按课程ID筛选(若传入有效CourseId) + if req.CourseId > 0 { + whereConditions = append(whereConditions, "course_id = ?") + queryArgs = append(queryArgs, req.CourseId) + } + + // 按活动类型筛选(若传入有效ActivityType,1-4) + if req.ActivityType >= 1 && req.ActivityType <= 4 { + whereConditions = append(whereConditions, "activity_type = ?") + queryArgs = append(queryArgs, req.ActivityType) + } + + // 拼接WHERE子句(若有条件则添加where,多个条件用and连接) + whereClause := "" + if len(whereConditions) > 0 { + whereClause = "WHERE " + strings.Join(whereConditions, " AND ") + } + + // 3. 查询符合条件的总条数(用于分页计算) + total, err := l.model.Count(l.ctx, whereClause, queryArgs...) + if err != nil { + return nil, err // 传递数据库查询错误 + } + + // 4. 查询当前页的活动列表数据 + activities, err := l.model.FindAll( + l.ctx, + whereClause, + queryArgs..., // 这里是最后一个参数,符合语法 + ) + if err != nil { + return nil, err + } + + // 5. 转换数据层模型为响应列表(复用单个活动的转换逻辑) + list := make([]types.GetCourseActivityResp, 0, len(activities)) + for _, activity := range activities { + list = append(list, types.GetCourseActivityResp{ + Id: util.Int64ToInt(activity.Id), + CourseId: util.Int64ToInt(activity.CourseId), + Title: activity.Title, + ActivityType: util.Int64ToInt(activity.ActivityType), + Content: util.NullStringToString(activity.Content), + StartTime: util.NullTimeToString(activity.StartTime), + EndTime: util.NullTimeToString(activity.EndTime), + Sort: util.Int64ToInt(activity.Sort), + }) + } + + // 6. 构建并返回响应 + resp = &types.ListCourseActivityResp{ + Total: total, // 总条数 + List: list, // 转换后的活动列表 + Page: req.Page, // 当前页码(透传请求参数) + PageSize: req.PageSize, // 每页条数(透传请求参数) + } + + return resp, nil } diff --git a/server/internal/course_activity/internal/logic/course_activity/updatecourseactivitylogic.go b/server/internal/course_activity/internal/logic/course_activity/updatecourseactivitylogic.go index 15894188..a7008a67 100644 --- a/server/internal/course_activity/internal/logic/course_activity/updatecourseactivitylogic.go +++ b/server/internal/course_activity/internal/logic/course_activity/updatecourseactivitylogic.go @@ -5,6 +5,10 @@ package course_activity import ( "context" + "database/sql" + "errors" + "fmt" + "time" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/internal/model" @@ -30,7 +34,83 @@ func NewUpdateCourseActivityLogic(ctx context.Context, cfg *config.Config, model } func (l *UpdateCourseActivityLogic) UpdateCourseActivity(req *types.UpdateCourseActivityReq) (resp *types.UpdateCourseActivityResp, err error) { - // todo: add your logic here and delete this line + // 1. 转换活动ID为数据层所需的int64类型 + activityID := int64(req.Id) - return + // 2. 查询原始活动信息(确保活动存在,用于保留未更新字段的原始值) + original, err := l.model.FindOne(l.ctx, activityID) + if err != nil { + if errors.Is(err, errors.New("resource not found")) { + return nil, fmt.Errorf("活动不存在(ID: %d)", req.Id) + } + return nil, fmt.Errorf("查询活动信息失败: %v", err) + } + + // 3. 处理开始时间(字符串转sql.NullTime) + var startTime sql.NullTime + if req.StartTime != "" { + // 解析请求中的时间字符串 + parsedTime, err := time.Parse("2006-01-02 15:04:05", req.StartTime) + if err != nil { + return nil, fmt.Errorf("开始时间格式错误(需符合2006-01-02 15:04:05): %v", err) + } + startTime = sql.NullTime{Time: parsedTime, Valid: true} // 标记为有效时间 + } else { + startTime = original.StartTime // 未提供则沿用原始值 + } + + // 4. 处理结束时间(逻辑同上) + var endTime sql.NullTime + if req.EndTime != "" { + parsedTime, err := time.Parse("2006-01-02 15:04:05", req.EndTime) + if err != nil { + return nil, fmt.Errorf("结束时间格式错误(需符合2006-01-02 15:04:05): %v", err) + } + endTime = sql.NullTime{Time: parsedTime, Valid: true} + } else { + endTime = original.EndTime // 未提供则沿用原始值 + } + + // 5. 构建更新数据(基于原始数据,覆盖请求中提供的字段) + updateData := &model.CourseActivity{ + Id: activityID, // 主键ID(必须保留,用于WHERE条件) + CourseId: original.CourseId, // 默认沿用原始课程ID + Title: original.Title, // 默认沿用原始标题 + ActivityType: original.ActivityType, // 默认沿用原始活动类型 + Content: original.Content, // 默认沿用原始详情 + StartTime: startTime, // 处理后的开始时间(新值或原始值) + EndTime: endTime, // 处理后的结束时间(新值或原始值) + Sort: original.Sort, // 默认沿用原始排序 + } + + // 6. 覆盖需要更新的字段(仅处理请求中提供的有效值) + if req.CourseId != 0 { + // 课程ID不为0时更新(int转int64) + updateData.CourseId = int64(req.CourseId) + } + if req.Title != "" { + // 标题非空时更新(直接赋值string) + updateData.Title = req.Title + } + if req.ActivityType >= 1 && req.ActivityType <= 4 { + // 活动类型在有效范围(1-4)时更新(int转int64) + updateData.ActivityType = int64(req.ActivityType) + } + // 详情字段无论是否为空,均按请求值更新(转为sql.NullString) + updateData.Content = sql.NullString{String: req.Content, Valid: true} + if req.Sort >= 0 { + // 排序值非负时更新(int转int64) + updateData.Sort = int64(req.Sort) + } + + // 7. 调用数据层执行更新 + err = l.model.Update(l.ctx, updateData) + if err != nil { + return nil, fmt.Errorf("更新活动失败: %v", err) + } + + // 8. 返回成功响应 + return &types.UpdateCourseActivityResp{ + Message: fmt.Sprintf("活动(ID: %d)更新成功", req.Id), + }, nil } diff --git a/server/internal/course_activity/internal/model/courseactivitymodel_gen.go b/server/internal/course_activity/internal/model/courseactivitymodel_gen.go index 72faf33e..1803397f 100644 --- a/server/internal/course_activity/internal/model/courseactivitymodel_gen.go +++ b/server/internal/course_activity/internal/model/courseactivitymodel_gen.go @@ -28,6 +28,12 @@ type ( FindOne(ctx context.Context, id int64) (*CourseActivity, error) Update(ctx context.Context, data *CourseActivity) error Delete(ctx context.Context, id int64) error + Count(ctx context.Context, whereClause string, args ...interface{}) (int, error) + FindAll( + ctx context.Context, + whereClause string, + args ...interface{}, // 可变参数作为最后一个参数,接收所有参数(筛选+分页) + ) ([]*CourseActivity, error) } defaultCourseActivityModel struct { @@ -86,6 +92,36 @@ func (m *defaultCourseActivityModel) Update(ctx context.Context, data *CourseAct return err } +// 数据层CourseActivityModel的Count方法:统计符合条件的总条数 +func (m *defaultCourseActivityModel) Count(ctx context.Context, whereClause string, args ...interface{}) (int, error) { + query := fmt.Sprintf("SELECT COUNT(*) FROM %s %s", m.table, whereClause) + var total int + err := m.conn.QueryRowCtx(ctx, &total, query, args...) + return total, err +} + +// 数据层CourseActivityModel的FindAll方法:查询符合条件的列表数据 +func (m *defaultCourseActivityModel) FindAll( + ctx context.Context, + whereClause string, + args ...interface{}, // 可变参数作为最后一个参数,接收所有参数(筛选+分页) +) ([]*CourseActivity, error) { + // SQL中添加分页:LIMIT ? OFFSET ?(注意参数顺序与queryArgs一致) + query := fmt.Sprintf( + "SELECT %s FROM %s %s ORDER BY sort DESC LIMIT ? OFFSET ?", + courseActivityRows, // 预定义的查询字段 + m.table, + whereClause, + ) + + // 执行查询(args包含所有参数,...展开后是最后一个实参,符合语法) + var activities []*CourseActivity + err := m.conn.QueryRowsCtx(ctx, &activities, query, args...) + if err != nil { + return nil, err + } + return activities, nil +} func (m *defaultCourseActivityModel) tableName() string { return m.table } diff --git a/server/internal/course_resource/internal/logic/course_resource/createcourseresourcelogic.go b/server/internal/course_resource/internal/logic/course_resource/createcourseresourcelogic.go index 01ec4bf1..43de6a94 100644 --- a/server/internal/course_resource/internal/logic/course_resource/createcourseresourcelogic.go +++ b/server/internal/course_resource/internal/logic/course_resource/createcourseresourcelogic.go @@ -5,6 +5,7 @@ package course_resource import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/model" @@ -30,7 +31,54 @@ func NewCreateCourseResourceLogic(ctx context.Context, cfg *config.Config, model } func (l *CreateCourseResourceLogic) CreateCourseResource(req *types.CreateCourseResourceReq) (resp *types.CreateCourseResourceResp, err error) { - // todo: add your logic here and delete this line + // 1. 校验必填参数 + if req.CourseId <= 0 { + return nil, fmt.Errorf("参数错误:课程ID必须为正整数") + } + if len(req.Title) == 0 || len(req.Title) > 255 { + return nil, fmt.Errorf("参数错误:资源标题不能为空且长度不超过255字符") + } + if len(req.ResourceUrl) == 0 || len(req.ResourceUrl) > 512 { + return nil, fmt.Errorf("参数错误:资源URL不能为空且长度不超过512字符") + } - return + // 2. 处理可选参数默认值 + size := int64(0) + if req.Size >= 0 { + size = int64(req.Size) // 适配int→int64(数据库字段常规类型) + } + + sort := int64(0) + if req.Sort >= 0 { + sort = int64(req.Sort) // 适配int→int64 + } + + // 3. 转换请求参数为Model层结构体 + resourceModel := &model.CourseResource{ + CourseId: int64(req.CourseId), // 课程ID:int→int64 + Title: req.Title, + ResourceUrl: req.ResourceUrl, + Size: size, // 资源大小(默认0) + Sort: sort, // 排序(默认0) + } + + // 4. 调用Model层执行插入 + result, err := l.model.Insert(l.ctx, resourceModel) + if err != nil { + return nil, fmt.Errorf("创建课程资源失败:%w", err) + } + + // 5. 获取新增资源的主键ID + newId, err := result.LastInsertId() + if err != nil { + return nil, fmt.Errorf("获取新增资源ID失败:%w", err) + } + + // 6. 构造响应结果 + resp = &types.CreateCourseResourceResp{ + Id: int(newId), // int64→int(适配响应结构体) + Message: "课程资源创建成功", + } + + return resp, nil } diff --git a/server/internal/course_resource/internal/logic/course_resource/deletecourseresourcelogic.go b/server/internal/course_resource/internal/logic/course_resource/deletecourseresourcelogic.go index a4c9f942..97f64fd0 100644 --- a/server/internal/course_resource/internal/logic/course_resource/deletecourseresourcelogic.go +++ b/server/internal/course_resource/internal/logic/course_resource/deletecourseresourcelogic.go @@ -5,10 +5,12 @@ package course_resource import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/model" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/types" + "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,7 +32,32 @@ func NewDeleteCourseResourceLogic(ctx context.Context, cfg *config.Config, model } func (l *DeleteCourseResourceLogic) DeleteCourseResource(req *types.DeleteCourseResourceReq) (resp *types.DeleteCourseResourceResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验:确保资源ID为正整数 + if req.Id <= 0 { + return nil, fmt.Errorf("参数错误:资源ID必须为正整数") + } + resourceId := int64(req.Id) // 转换为int64(适配Model层字段类型) - return + // 2. 检查资源是否存在(避免删除不存在的记录) + _, err = l.model.FindOne(l.ctx, resourceId) + if err != nil { + if err == model.ErrNotFound || err == sqlx.ErrNotFound { + return nil, fmt.Errorf("删除失败:该课程资源不存在或已被删除") + } + // 其他查询错误(如数据库连接异常) + return nil, fmt.Errorf("查询资源信息失败:%w", err) + } + + // 3. 调用Model层执行删除操作 + err = l.model.Delete(l.ctx, resourceId) + if err != nil { + return nil, fmt.Errorf("删除课程资源失败:%w", err) + } + + // 4. 构造成功响应 + resp = &types.DeleteCourseResourceResp{ + Message: "课程资源删除成功", + } + + return resp, nil } diff --git a/server/internal/course_resource/internal/logic/course_resource/getcourseresourcelogic.go b/server/internal/course_resource/internal/logic/course_resource/getcourseresourcelogic.go index 095b50bf..3bc0304e 100644 --- a/server/internal/course_resource/internal/logic/course_resource/getcourseresourcelogic.go +++ b/server/internal/course_resource/internal/logic/course_resource/getcourseresourcelogic.go @@ -5,10 +5,12 @@ package course_resource import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/model" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/types" + "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,7 +32,31 @@ func NewGetCourseResourceLogic(ctx context.Context, cfg *config.Config, model mo } func (l *GetCourseResourceLogic) GetCourseResource(req *types.GetCourseResourceReq) (resp *types.GetCourseResourceResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验:确保资源ID为正整数 + if req.Id <= 0 { + return nil, fmt.Errorf("参数错误:资源ID必须为正整数") + } + resourceId := int64(req.Id) // 转换为int64(适配Model层字段类型) - return + // 2. 调用Model层查询资源详情 + resourceModel, err := l.model.FindOne(l.ctx, resourceId) + if err != nil { + if err == model.ErrNotFound || err == sqlx.ErrNotFound { + return nil, fmt.Errorf("查询失败:该课程资源不存在或已被删除") + } + // 其他查询错误(如数据库连接异常) + return nil, fmt.Errorf("查询课程资源失败:%w", err) + } + + // 3. 转换Model数据为响应结构体(处理类型适配) + resp = &types.GetCourseResourceResp{ + Id: int(resourceModel.Id), // Model层Id为int64,转换为int + CourseId: int(resourceModel.CourseId), // 关联课程ID:int64→int + Title: resourceModel.Title, // 标题直接复用 + ResourceUrl: resourceModel.ResourceUrl, // 资源URL直接复用 + Size: int(resourceModel.Size), // 资源大小:int64→int + Sort: int(resourceModel.Sort), // 排序:int64→int + } + + return resp, nil } diff --git a/server/internal/course_resource/internal/logic/course_resource/listcourseresourcelogic.go b/server/internal/course_resource/internal/logic/course_resource/listcourseresourcelogic.go index 36316977..dc45167e 100644 --- a/server/internal/course_resource/internal/logic/course_resource/listcourseresourcelogic.go +++ b/server/internal/course_resource/internal/logic/course_resource/listcourseresourcelogic.go @@ -5,6 +5,7 @@ package course_resource import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/model" @@ -30,7 +31,54 @@ func NewListCourseResourceLogic(ctx context.Context, cfg *config.Config, model m } func (l *ListCourseResourceLogic) ListCourseResource(req *types.ListCourseResourceReq) (resp *types.ListCourseResourceResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验(补充validate标签外的业务校验) + if req.Page < 1 { + return nil, fmt.Errorf("参数错误:页码必须≥1") + } + if req.PageSize < 1 || req.PageSize > 100 { + return nil, fmt.Errorf("参数错误:每页条数必须在1-100之间") + } - return + // 2. 计算分页参数(offset = (page-1)*pageSize) + offset := (req.Page - 1) * req.PageSize + + // 3. 调用Model层查询总条数(按CourseId筛选,CourseId=0时查全部) + total, err := l.model.CountByCourseId(l.ctx, int64(req.CourseId)) + if err != nil { + return nil, fmt.Errorf("查询资源总条数失败:%w", err) + } + + // 4. 调用Model层查询当前页数据 + resources, err := l.model.FindByCourseIdWithPage( + l.ctx, + int64(req.CourseId), // 课程ID(0表示查全部) + req.PageSize, // 每页条数 + offset, // 偏移量 + ) + if err != nil { + return nil, fmt.Errorf("查询资源列表失败:%w", err) + } + + // 5. 转换Model数据为响应列表(适配GetCourseResourceResp) + var list []types.GetCourseResourceResp + for _, res := range resources { + list = append(list, types.GetCourseResourceResp{ + Id: int(res.Id), // int64→int + CourseId: int(res.CourseId), // int64→int + Title: res.Title, + ResourceUrl: res.ResourceUrl, + Size: int(res.Size), // int64→int + Sort: int(res.Sort), // int64→int + }) + } + + // 6. 构造响应 + resp = &types.ListCourseResourceResp{ + Total: int(total), // 总条数(int64→int) + List: list, // 资源列表 + Page: req.Page, // 当前页码 + PageSize: req.PageSize, // 每页条数 + } + + return resp, nil } diff --git a/server/internal/course_resource/internal/logic/course_resource/updatecourseresourcelogic.go b/server/internal/course_resource/internal/logic/course_resource/updatecourseresourcelogic.go index b29a204e..02e45a03 100644 --- a/server/internal/course_resource/internal/logic/course_resource/updatecourseresourcelogic.go +++ b/server/internal/course_resource/internal/logic/course_resource/updatecourseresourcelogic.go @@ -5,6 +5,8 @@ package course_resource import ( "context" + "errors" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/model" @@ -30,7 +32,53 @@ func NewUpdateCourseResourceLogic(ctx context.Context, cfg *config.Config, model } func (l *UpdateCourseResourceLogic) UpdateCourseResource(req *types.UpdateCourseResourceReq) (resp *types.UpdateCourseResourceResp, err error) { - // todo: add your logic here and delete this line + // 1. 转换资源ID为数据层所需的int64类型 + resourceID := int64(req.Id) - return + // 2. 查询原始资源信息(确保资源存在,保留未更新字段原始值) + original, err := l.model.FindOne(l.ctx, resourceID) + if err != nil { + if errors.Is(err, errors.New("resource not found")) { + return nil, fmt.Errorf("课程资源不存在(ID: %d)", req.Id) + } + return nil, fmt.Errorf("查询课程资源失败: %v", err) + } + + // 3. 构建更新数据(基于原始数据,覆盖请求中有效字段) + updateData := &model.CourseResource{ + Id: resourceID, // 主键ID(WHERE条件,必须保留) + CourseId: original.CourseId, // 默认沿用原始课程ID + Title: original.Title, // 默认沿用原始标题 + ResourceUrl: original.ResourceUrl, // 默认沿用原始资源链接 + Size: original.Size, // 默认沿用原始资源大小 + Sort: original.Sort, // 默认沿用原始排序 + } + + // 4. 覆盖需要更新的字段(仅处理请求中提供的有效值) + if req.CourseId != 0 { + updateData.CourseId = int64(req.CourseId) // int转int64 + } + if req.Title != "" { + updateData.Title = req.Title // 字符串直接赋值 + } + if req.ResourceUrl != "" { + updateData.ResourceUrl = req.ResourceUrl // 资源链接直接赋值 + } + if req.Size >= 0 { + updateData.Size = int64(req.Size) // int转int64(资源大小非负) + } + if req.Sort >= 0 { + updateData.Sort = int64(req.Sort) // int转int64(排序非负) + } + + // 5. 调用数据层执行更新 + err = l.model.Update(l.ctx, updateData) + if err != nil { + return nil, fmt.Errorf("更新课程资源失败: %v", err) + } + + // 6. 返回成功响应 + return &types.UpdateCourseResourceResp{ + Message: fmt.Sprintf("课程资源(ID: %d)更新成功", req.Id), + }, nil } diff --git a/server/internal/course_resource/internal/model/courseresourcemodel_gen.go b/server/internal/course_resource/internal/model/courseresourcemodel_gen.go index d94900c3..964c9cf0 100644 --- a/server/internal/course_resource/internal/model/courseresourcemodel_gen.go +++ b/server/internal/course_resource/internal/model/courseresourcemodel_gen.go @@ -28,6 +28,12 @@ type ( FindOne(ctx context.Context, id int64) (*CourseResource, error) Update(ctx context.Context, data *CourseResource) error Delete(ctx context.Context, id int64) error + CountByCourseId(ctx context.Context, courseId int64) (int64, error) + FindByCourseIdWithPage( + ctx context.Context, + courseId int64, + pageSize, offset int, + ) ([]*CourseResource, error) } defaultCourseResourceModel struct { @@ -84,6 +90,52 @@ func (m *defaultCourseResourceModel) Update(ctx context.Context, data *CourseRes return err } +func (m *defaultCourseResourceModel) CountByCourseId(ctx context.Context, courseId int64) (int64, error) { + var count int64 + // 构造查询条件:courseId=0时查全部,否则按course_id筛选 + if courseId == 0 { + query := fmt.Sprintf("select count(*) from %s", m.table) + err := m.conn.QueryRowCtx(ctx, &count, query) + return count, err + } else { + query := fmt.Sprintf("select count(*) from %s where `course_id` = ?", m.table) + err := m.conn.QueryRowCtx(ctx, &count, query, courseId) + return count, err + } +} + +// FindByCourseIdWithPage 实现按课程ID分页查询 +func (m *defaultCourseResourceModel) FindByCourseIdWithPage( + ctx context.Context, + courseId int64, + pageSize, offset int, +) ([]*CourseResource, error) { + var resources []*CourseResource + // 构造查询SQL:支持筛选+排序+分页(按sort升序,确保资源有序) + baseQuery := fmt.Sprintf("select %s from %s", courseResourceRows, m.table) + sortQuery := " order by `sort` asc limit ?, ?" // 按sort升序,限制条数和偏移量 + + var query string + var args []interface{} + + if courseId == 0 { + // 查全部资源:无where条件 + query = baseQuery + sortQuery + args = []interface{}{offset, pageSize} + } else { + // 按课程ID筛选 + query = baseQuery + " where `course_id` = ?" + sortQuery + args = []interface{}{courseId, offset, pageSize} + } + + // 执行查询 + err := m.conn.QueryRowsCtx(ctx, &resources, query, args...) + if err != nil { + return nil, err + } + return resources, nil +} + func (m *defaultCourseResourceModel) tableName() string { return m.table } diff --git a/server/internal/course_teacher/internal/logic/course_teacher/createcourseteacherlogic.go b/server/internal/course_teacher/internal/logic/course_teacher/createcourseteacherlogic.go index 1d101a4c..ecb103a0 100644 --- a/server/internal/course_teacher/internal/logic/course_teacher/createcourseteacherlogic.go +++ b/server/internal/course_teacher/internal/logic/course_teacher/createcourseteacherlogic.go @@ -5,10 +5,12 @@ package course_teacher import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/internal/model" "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/internal/types" + "github.com/JACKYMYPERSON/hldrCenter/util" "github.com/zeromicro/go-zero/core/logx" ) @@ -30,7 +32,59 @@ func NewCreateCourseTeacherLogic(ctx context.Context, cfg *config.Config, model } func (l *CreateCourseTeacherLogic) CreateCourseTeacher(req *types.CreateCourseTeacherReq) (resp *types.CreateCourseTeacherResp, err error) { - // todo: add your logic here and delete this line + // 1. 校验必填参数 + if req.CourseId <= 0 { + return nil, fmt.Errorf("参数错误:课程ID必须为正整数") + } + if req.TeacherId <= 0 { + return nil, fmt.Errorf("参数错误:教师ID必须为正整数") + } + if len(req.Name) == 0 || len(req.Name) > 100 { + return nil, fmt.Errorf("参数错误:教师姓名不能为空且长度不超过100字符") + } - return + // 2. 校验可选参数 + if len(req.Title) > 200 { + return nil, fmt.Errorf("参数错误:教师头衔长度不能超过200字符") + } + if len(req.Avatar) > 512 { + return nil, fmt.Errorf("参数错误:头像URL长度不能超过512字符") + } + + // 3. 处理可选参数默认值(Sort默认0) + sort := int64(0) + if req.Sort >= 0 { + sort = int64(req.Sort) // 适配int→int64(Model层字段类型) + } + + // 4. 转换请求参数为Model层结构体 + courseTeacher := &model.CourseTeacher{ + CourseId: int64(req.CourseId), // 课程ID:int→int64 + TeacherId: int64(req.TeacherId), // 教师ID:int→int64 + Name: req.Name, + Title: req.Title, + Avatar: req.Avatar, + Intro: util.StringToNullString(req.Intro), + Sort: sort, // 排序:int→int64 + } + + // 5. 调用Model层执行插入 + result, err := l.model.Insert(l.ctx, courseTeacher) + if err != nil { + return nil, fmt.Errorf("创建课程-教师关联失败:%w", err) + } + + // 6. 获取新增关联记录的ID + newId, err := result.LastInsertId() + if err != nil { + return nil, fmt.Errorf("获取新增关联ID失败:%w", err) + } + + // 7. 构造响应 + resp = &types.CreateCourseTeacherResp{ + Id: int(newId), // int64→int(适配响应结构体) + Message: "课程-教师关联创建成功", + } + + return resp, nil } diff --git a/server/internal/course_teacher/internal/logic/course_teacher/deletecourseteacherlogic.go b/server/internal/course_teacher/internal/logic/course_teacher/deletecourseteacherlogic.go index 940e3501..8ff4dfe1 100644 --- a/server/internal/course_teacher/internal/logic/course_teacher/deletecourseteacherlogic.go +++ b/server/internal/course_teacher/internal/logic/course_teacher/deletecourseteacherlogic.go @@ -5,6 +5,7 @@ package course_teacher import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/internal/model" @@ -30,7 +31,18 @@ func NewDeleteCourseTeacherLogic(ctx context.Context, cfg *config.Config, model } func (l *DeleteCourseTeacherLogic) DeleteCourseTeacher(req *types.DeleteCourseTeacherReq) (resp *types.DeleteCourseTeacherResp, err error) { - // todo: add your logic here and delete this line + // 1. 将请求中的int类型ID转换为数据层所需的int64类型 + teacherID := int64(req.Id) - return + // 2. 调用数据层的删除方法执行删除操作 + err = l.model.Delete(l.ctx, teacherID) + if err != nil { + // 若删除过程中出现数据库错误(如连接异常等),返回错误信息 + return nil, fmt.Errorf("删除教学团队关联失败: %v", err) + } + + // 3. 删除成功,返回操作结果 + return &types.DeleteCourseTeacherResp{ + Message: fmt.Sprintf("教学团队关联(ID: %d)已成功删除", req.Id), + }, nil } diff --git a/server/internal/course_teacher/internal/logic/course_teacher/getcourseteacherlogic.go b/server/internal/course_teacher/internal/logic/course_teacher/getcourseteacherlogic.go index f6d05ec4..7833423b 100644 --- a/server/internal/course_teacher/internal/logic/course_teacher/getcourseteacherlogic.go +++ b/server/internal/course_teacher/internal/logic/course_teacher/getcourseteacherlogic.go @@ -5,6 +5,9 @@ package course_teacher import ( "context" + "database/sql" + "errors" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/internal/model" @@ -30,7 +33,38 @@ func NewGetCourseTeacherLogic(ctx context.Context, cfg *config.Config, model mod } func (l *GetCourseTeacherLogic) GetCourseTeacher(req *types.GetCourseTeacherReq) (resp *types.GetCourseTeacherResp, err error) { - // todo: add your logic here and delete this line + // 1. 转换请求ID类型(int -> int64,适配数据层方法) + teacherID := int64(req.Id) - return + // 2. 调用数据层查询单个关联信息 + courseTeacher, err := l.model.FindOne(l.ctx, teacherID) + if err != nil { + // 处理资源不存在的错误 + if errors.Is(err, errors.New("resource not found")) { + return nil, fmt.Errorf("教学团队关联不存在(ID: %d)", req.Id) + } + // 处理其他数据库错误 + return nil, fmt.Errorf("查询教学团队关联失败: %v", err) + } + + // 3. 转换数据层模型到响应结构体(处理类型转换) + resp = &types.GetCourseTeacherResp{ + Id: int(courseTeacher.Id), // int64 -> int + CourseId: int(courseTeacher.CourseId), // int64 -> int + TeacherId: int(courseTeacher.TeacherId), // int64 -> int + Name: courseTeacher.Name, // string直接赋值 + Title: courseTeacher.Title, // string直接赋值 + Avatar: courseTeacher.Avatar, // string直接赋值 + Intro: NullStringToString(courseTeacher.Intro), // string直接赋值 + Sort: int(courseTeacher.Sort), // int64 -> int + } + + return resp, nil +} + +func NullStringToString(ns sql.NullString) string { + if ns.Valid { + return ns.String + } + return "" } diff --git a/server/internal/course_teacher/internal/logic/course_teacher/listcourseteacherlogic.go b/server/internal/course_teacher/internal/logic/course_teacher/listcourseteacherlogic.go index 21fe19c9..e13032d2 100644 --- a/server/internal/course_teacher/internal/logic/course_teacher/listcourseteacherlogic.go +++ b/server/internal/course_teacher/internal/logic/course_teacher/listcourseteacherlogic.go @@ -5,6 +5,8 @@ package course_teacher import ( "context" + "database/sql" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/internal/model" @@ -30,7 +32,73 @@ func NewListCourseTeacherLogic(ctx context.Context, cfg *config.Config, model mo } func (l *ListCourseTeacherLogic) ListCourseTeacher(req *types.ListCourseTeacherReq) (resp *types.ListCourseTeacherResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验(补充validate标签外的业务校验) + if req.Page < 1 { + return nil, fmt.Errorf("参数错误:页码必须≥1") + } + if req.PageSize < 1 || req.PageSize > 100 { + return nil, fmt.Errorf("参数错误:每页条数必须在1-100之间") + } - return + // 2. 转换筛选参数为int64(适配Model层字段类型) + courseId := int64(req.CourseId) + teacherId := int64(req.TeacherId) + + // 3. 计算分页参数(offset = (page-1)*pageSize) + offset := (req.Page - 1) * req.PageSize + + // 4. 调用Model层查询总条数(按筛选条件) + total, err := l.model.CountByConditions(l.ctx, courseId, teacherId) + if err != nil { + return nil, fmt.Errorf("查询教师总条数失败:%w", err) + } + + // 5. 调用Model层查询当前页数据(按筛选条件+分页+排序) + teachers, err := l.model.FindByConditionsWithPage( + l.ctx, + courseId, // 课程ID筛选(0表示不筛选) + teacherId, // 教师ID筛选(0表示不筛选) + req.PageSize, // 每页条数 + offset, // 偏移量 + ) + if err != nil { + return nil, fmt.Errorf("查询教师列表失败:%w", err) + } + + // 6. 转换Model数据为响应列表(处理sql.NullString和int64→int) + var list []types.GetCourseTeacherResp + for _, t := range teachers { + list = append(list, types.GetCourseTeacherResp{ + Id: int(t.Id), // int64→int + CourseId: int(t.CourseId), // int64→int + TeacherId: int(t.TeacherId), // int64→int + Name: t.Name, + Title: t.Title, // sql.NullString→string + Avatar: t.Avatar, // sql.NullString→string + Intro: NullStringToString(t.Intro), // sql.NullString→string + Sort: int(t.Sort), // int64→int + }) + } + + // 7. 构造响应 + resp = &types.ListCourseTeacherResp{ + Total: int(total), // 总条数(int64→int) + List: list, + Page: req.Page, + PageSize: req.PageSize, + } + + return resp, nil +} + +func StringToNullString(s string) sql.NullString { + if s != "" { + return sql.NullString{ + String: s, + Valid: true, + } + } + return sql.NullString{ + Valid: false, // 空字符串时标记为无效,数据库存 NULL + } } diff --git a/server/internal/course_teacher/internal/model/courseteachermodel_gen.go b/server/internal/course_teacher/internal/model/courseteachermodel_gen.go index 1b1aaf56..09c31671 100644 --- a/server/internal/course_teacher/internal/model/courseteachermodel_gen.go +++ b/server/internal/course_teacher/internal/model/courseteachermodel_gen.go @@ -28,6 +28,12 @@ type ( FindOne(ctx context.Context, id int64) (*CourseTeacher, error) Update(ctx context.Context, data *CourseTeacher) error Delete(ctx context.Context, id int64) error + CountByConditions(ctx context.Context, courseId, teacherId int64) (int64, error) + FindByConditionsWithPage( + ctx context.Context, + courseId, teacherId int64, + pageSize, offset int, + ) ([]*CourseTeacher, error) } defaultCourseTeacherModel struct { @@ -86,6 +92,74 @@ func (m *defaultCourseTeacherModel) Update(ctx context.Context, data *CourseTeac return err } +func (m *defaultCourseTeacherModel) CountByConditions(ctx context.Context, courseId, teacherId int64) (int64, error) { + var count int64 + baseQuery := fmt.Sprintf("select count(*) from %s", m.table) + + // 构造筛选条件(支持单条件/多条件/无筛选) + var whereClauses []string + var args []interface{} + + if courseId > 0 { + whereClauses = append(whereClauses, "`course_id` = ?") + args = append(args, courseId) + } + if teacherId > 0 { + whereClauses = append(whereClauses, "`teacher_id` = ?") + args = append(args, teacherId) + } + + // 拼接完整查询语句 + if len(whereClauses) > 0 { + baseQuery += " where " + strings.Join(whereClauses, " and ") + } + + // 执行查询 + err := m.conn.QueryRowCtx(ctx, &count, baseQuery, args...) + return count, err +} + +// FindByConditionsWithPage 实现按条件分页查询 +func (m *defaultCourseTeacherModel) FindByConditionsWithPage( + ctx context.Context, + courseId, teacherId int64, + pageSize, offset int, +) ([]*CourseTeacher, error) { + var teachers []*CourseTeacher + baseQuery := fmt.Sprintf("select %s from %s", courseTeacherRows, m.table) + + // 构造筛选条件(同CountByConditions) + var whereClauses []string + var args []interface{} + + if courseId > 0 { + whereClauses = append(whereClauses, "`course_id` = ?") + args = append(args, courseId) + } + if teacherId > 0 { + whereClauses = append(whereClauses, "`teacher_id` = ?") + args = append(args, teacherId) + } + + // 拼接分页和排序(按sort升序,确保展示顺序一致) + pageQuery := " order by `sort` asc limit ?, ?" + args = append(args, offset, pageSize) // 补充分页参数 + + // 拼接完整查询语句 + if len(whereClauses) > 0 { + baseQuery += " where " + strings.Join(whereClauses, " and ") + pageQuery + } else { + baseQuery += pageQuery + } + + // 执行查询 + err := m.conn.QueryRowsCtx(ctx, &teachers, baseQuery, args...) + if err != nil { + return nil, err + } + return teachers, nil +} + func (m *defaultCourseTeacherModel) tableName() string { return m.table } diff --git a/server/router/router.go b/server/router/router.go index 705e3f44..7eaf8944 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -8,6 +8,8 @@ import ( "github.com/JACKYMYPERSON/hldrCenter/internal/course_activity/handler/course_activity" "github.com/JACKYMYPERSON/hldrCenter/internal/course_content/handler/course_content" "github.com/JACKYMYPERSON/hldrCenter/internal/course_file/handler/course_file" + "github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/handler/course_resource" + "github.com/JACKYMYPERSON/hldrCenter/internal/course_teacher/handler/course_teacher" "github.com/JACKYMYPERSON/hldrCenter/internal/devproject/handler/devproject" "github.com/JACKYMYPERSON/hldrCenter/internal/file/handler/fileupload" "github.com/JACKYMYPERSON/hldrCenter/internal/meeting/handler/meeting" @@ -263,6 +265,51 @@ func SetupRouter(cfg *config.Config) *gin.Engine { // 删除课程活动(DELETE /api/course-activity/:id) courseActivities.DELETE("/:id", gin.WrapH(course_activity.DeleteCourseActivityHandler(cfg))) } + courseResources := api.Group("/course-resource") + { + // 1. 创建课程资源(POST /api/course-resource) + // 对应 service 中的 CreateCourseResourceHandler,接收 CreateCourseResourceReq + courseResources.POST("", gin.WrapH(course_resource.CreateCourseResourceHandler(cfg))) + + // 2. 查询单个课程资源(GET /api/course-resource/:id) + // 对应 service 中的 GetCourseResourceHandler,从路径参数获取 id(GetCourseResourceReq 需包含 id 字段) + courseResources.GET("/:id", gin.WrapH(course_resource.GetCourseResourceHandler(cfg))) + + // 3. 课程资源列表查询(POST /api/course-resource/list) + // 对应 service 中的 ListCourseResourceHandler,接收 ListCourseResourceReq(筛选+分页参数) + courseResources.POST("/list", gin.WrapH(course_resource.ListCourseResourceHandler(cfg))) + + // 4. 更新课程资源(PUT /api/course-resource) + // 对应 service 中的 UpdateCourseResourceHandler,接收 UpdateCourseResourceReq(含 id 和更新字段) + courseResources.PUT("", gin.WrapH(course_resource.UpdateCourseResourceHandler(cfg))) + + // 5. 删除课程资源(DELETE /api/course-resource/:id) + // 对应 service 中的 DeleteCourseResourceHandler,从路径参数获取 id(DeleteCourseResourceReq 需包含 id 字段) + courseResources.DELETE("/:id", gin.WrapH(course_resource.DeleteCourseResourceHandler(cfg))) + } + + courseTeachers := api.Group("/course-teacher") + { + // 1. 创建教学团队关联(POST /api/course-teacher) + // 对应 CreateCourseTeacherHandler,接收 CreateCourseTeacherReq 请求体 + courseTeachers.POST("", gin.WrapH(course_teacher.CreateCourseTeacherHandler(cfg))) + + // 2. 查询单个教学团队关联(GET /api/course-teacher/:id) + // 对应 GetCourseTeacherHandler,从路径参数获取 id(需确保 GetCourseTeacherReq 含 id 字段) + courseTeachers.GET("/:id", gin.WrapH(course_teacher.GetCourseTeacherHandler(cfg))) + + // 3. 教学团队关联列表查询(POST /api/course-teacher/list) + // 对应 ListCourseTeacherHandler,接收 ListCourseTeacherReq(筛选+分页参数) + courseTeachers.POST("/list", gin.WrapH(course_teacher.ListCourseTeacherHandler(cfg))) + + // 4. 更新教学团队关联(PUT /api/course-teacher) + // 对应 UpdateCourseTeacherHandler,接收 UpdateCourseTeacherReq(含 id 和更新字段) + courseTeachers.PUT("", gin.WrapH(course_teacher.UpdateCourseTeacherHandler(cfg))) + + // 5. 删除教学团队关联(DELETE /api/course-teacher/:id) + // 对应 DeleteCourseTeacherHandler,从路径参数获取 id(需确保 DeleteCourseTeacherReq 含 id 字段) + courseTeachers.DELETE("/:id", gin.WrapH(course_teacher.DeleteCourseTeacherHandler(cfg))) + } } diff --git a/server/util/error/error.go b/server/util/error/error.go new file mode 100644 index 00000000..d7be2348 --- /dev/null +++ b/server/util/error/error.go @@ -0,0 +1,5 @@ +package error + +import "errors" + +var ErrNotFoundit = errors.New("resource not found") diff --git a/server/util/utiltrans.go b/server/util/utiltrans.go new file mode 100644 index 00000000..44ae4730 --- /dev/null +++ b/server/util/utiltrans.go @@ -0,0 +1,45 @@ +package util + +import "database/sql" + +func Int64ToInt(ni int64) int { + return int(ni) // 直接强转,无需处理空值(基础类型非空) +} + +// NullStringToString 转换 sql.NullString 到 string +// 若字段无效(Valid=false),返回空字符串 +func NullStringToString(ns sql.NullString) string { + if ns.Valid { + return ns.String + } + return "" +} + +func StringToNullString(s string) sql.NullString { + if s != "" { + return sql.NullString{String: s, Valid: true} + } + return sql.NullString{Valid: false} +} + +// NullInt64ToInt 转换 sql.NullInt64 到 int +// 若字段无效(Valid=false),返回0(可根据业务调整默认值) +func NullInt64ToInt(ni sql.NullInt64) int { + if ni.Valid { + return int(ni.Int64) + } + return 0 +} + +// NullTimeToString 转换 sql.NullTime 到格式化字符串 +// 若字段无效,返回空字符串;格式默认为 "2006-01-02 15:04:05" +func NullTimeToString(nt sql.NullTime, format ...string) string { + if !nt.Valid { + return "" + } + f := "2006-01-02 15:04:05" + if len(format) > 0 { + f = format[0] + } + return nt.Time.Format(f) +}