From 4e9736a3b44eda0a58ba261b103d87dc2dd1d9df Mon Sep 17 00:00:00 2001 From: mayiming <1627832236@qq.com> Date: Fri, 31 Oct 2025 15:01:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=A7=86=E9=A2=91=E6=A1=88?= =?UTF-8?q?=E4=BE=8B=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logic/video_case/createvideocaselogic.go | 50 ++++++++++++++- .../logic/video_case/deletevideocaselogic.go | 27 +++++++- .../logic/video_case/getvideocaselogic.go | 50 ++++++++++++++- .../logic/video_case/listvideocaselogic.go | 64 ++++++++++++++++++- .../logic/video_case/updatevideocaselogic.go | 60 ++++++++++++++++- .../internal/model/videocasemodel_gen.go | 62 +++++++++++++++++- 6 files changed, 301 insertions(+), 12 deletions(-) diff --git a/server/internal/video_case/internal/logic/video_case/createvideocaselogic.go b/server/internal/video_case/internal/logic/video_case/createvideocaselogic.go index 11a6660e..a536e2ae 100644 --- a/server/internal/video_case/internal/logic/video_case/createvideocaselogic.go +++ b/server/internal/video_case/internal/logic/video_case/createvideocaselogic.go @@ -5,6 +5,8 @@ package video_case import ( "context" + "database/sql" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/video_case/internal/model" @@ -30,7 +32,51 @@ func NewCreateVideoCaseLogic(ctx context.Context, cfg *config.Config, model mode } func (l *CreateVideoCaseLogic) CreateVideoCase(req *types.CreateVideoCaseReq) (resp *types.BaseResp, err error) { - // todo: add your logic here and delete this line + // 1. 二次参数校验(补充validate标签之外的必填项检查) + if req.Title == "" { + return &types.BaseResp{Code: 400, Msg: "视频案例标题为必填项"}, nil + } + if req.VideoUrl == "" { + return &types.BaseResp{Code: 400, Msg: "视频播放地址为必填项"}, nil + } + if req.DesignerNames == "" { + return &types.BaseResp{Code: 400, Msg: "设计人员名单为必填项"}, nil + } + if req.TutorNames == "" { + return &types.BaseResp{Code: 400, Msg: "指导老师名单为必填项"}, nil + } - return + // 2. 映射请求参数到model结构体(设置软删除默认值) + data := &model.VideoCase{ + Title: req.Title, + Intro: StringToNullString(req.Intro), // 允许为空,使用默认空字符串 + VideoUrl: req.VideoUrl, + DesignerNames: req.DesignerNames, + TutorNames: req.TutorNames, + Sort: int64(req.Sort), // 默认为0,无需额外处理 + IsDelete: 0, // 软删除默认值:未删除(0) + // create_time和update_time由数据库自动维护,无需手动赋值 + } + + // 3. 调用model插入数据 + _, err = l.model.Insert(l.ctx, data) + if err != nil { + // 4. 数据库错误处理:返回具体错误信息 + return &types.BaseResp{ + Code: 500, + Msg: fmt.Sprintf("创建视频案例失败:%s", err.Error()), + }, err + } + + // 5. 成功响应 + return &types.BaseResp{ + Code: 0, + Msg: "视频案例创建成功", + }, nil +} +func StringToNullString(s string) sql.NullString { + if s != "" { + return sql.NullString{String: s, Valid: true} + } + return sql.NullString{Valid: false} } diff --git a/server/internal/video_case/internal/logic/video_case/deletevideocaselogic.go b/server/internal/video_case/internal/logic/video_case/deletevideocaselogic.go index fa062338..08110303 100644 --- a/server/internal/video_case/internal/logic/video_case/deletevideocaselogic.go +++ b/server/internal/video_case/internal/logic/video_case/deletevideocaselogic.go @@ -5,6 +5,7 @@ package video_case import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/video_case/internal/model" @@ -30,7 +31,29 @@ func NewDeleteVideoCaseLogic(ctx context.Context, cfg *config.Config, model mode } func (l *DeleteVideoCaseLogic) DeleteVideoCase(req *types.DeleteVideoCaseReq) (resp *types.BaseResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验:确保ID合法 + if req.Id <= 0 { + return &types.BaseResp{Code: 400, Msg: "视频案例ID不合法"}, nil + } + id := int64(req.Id) // 转换为int64(与Model层参数类型一致) - return + // 2. 检查视频案例是否存在且未被删除 + _, err = l.model.FindOne(l.ctx, id) + if err != nil { + // 若案例不存在或已删除(FindOne已过滤is_delete=1的记录) + if err == model.ErrNotFound { + return &types.BaseResp{Code: 404, Msg: "视频案例不存在或已删除"}, nil + } + // 其他查询错误 + return &types.BaseResp{Code: 500, Msg: fmt.Sprintf("查询视频案例失败:%s", err.Error())}, err + } + + // 3. 执行软删除:更新is_delete为1 + err = l.model.Delete(l.ctx, id) + if err != nil { + return &types.BaseResp{Code: 500, Msg: fmt.Sprintf("删除视频案例失败:%s", err.Error())}, err + } + + // 4. 成功响应 + return &types.BaseResp{Code: 0, Msg: "视频案例删除成功"}, nil } diff --git a/server/internal/video_case/internal/logic/video_case/getvideocaselogic.go b/server/internal/video_case/internal/logic/video_case/getvideocaselogic.go index f5c93cab..6980d69d 100644 --- a/server/internal/video_case/internal/logic/video_case/getvideocaselogic.go +++ b/server/internal/video_case/internal/logic/video_case/getvideocaselogic.go @@ -5,6 +5,8 @@ package video_case import ( "context" + "errors" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/video_case/internal/model" @@ -30,7 +32,51 @@ func NewGetVideoCaseLogic(ctx context.Context, cfg *config.Config, model model.V } func (l *GetVideoCaseLogic) GetVideoCase(req *types.GetVideoCaseReq) (resp *types.VideoCaseResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验:确保ID合法 + if req.Id <= 0 { + return nil, errors.New("视频案例ID不合法") + } + id := int64(req.Id) // 转换为int64(与Model层参数类型一致) - return + // 2. 调用Model查询(过滤已删除数据,需先修改Model的FindOne方法) + modelCase, err := l.model.FindOne(l.ctx, id) + if err != nil { + // 区分"未找到"和"查询异常" + if err == model.ErrNotFound { + return nil, errors.New("视频案例不存在或已删除") + } + return nil, fmt.Errorf("查询视频案例失败:%w", err) + } + + // 3. 处理特殊类型转换 + // 3.1 Intro字段:sql.NullString → string(Valid为true时取值,否则为空) + intro := "" + if modelCase.Intro.Valid { + intro = modelCase.Intro.String + } + + // 3.2 时间字段:time.Time → string(统一格式) + createTime := "" + if !modelCase.CreateTime.IsZero() { + createTime = modelCase.CreateTime.Format("2006-01-02 15:04:05") + } + updateTime := "" + if !modelCase.UpdateTime.IsZero() { + updateTime = modelCase.UpdateTime.Format("2006-01-02 15:04:05") + } + + // 4. 映射Model数据到响应结构体 + resp = &types.VideoCaseResp{ + Id: int(modelCase.Id), + Title: modelCase.Title, + Intro: intro, // 转换后的简介 + VideoUrl: modelCase.VideoUrl, + DesignerNames: modelCase.DesignerNames, + TutorNames: modelCase.TutorNames, + Sort: int(modelCase.Sort), + CreateTime: createTime, // 转换后的创建时间 + UpdateTime: updateTime, // 转换后的更新时间 + } + + return resp, nil } diff --git a/server/internal/video_case/internal/logic/video_case/listvideocaselogic.go b/server/internal/video_case/internal/logic/video_case/listvideocaselogic.go index a9edb3a8..35dc45d5 100644 --- a/server/internal/video_case/internal/logic/video_case/listvideocaselogic.go +++ b/server/internal/video_case/internal/logic/video_case/listvideocaselogic.go @@ -5,6 +5,7 @@ package video_case import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/video_case/internal/model" @@ -30,7 +31,66 @@ func NewListVideoCaseLogic(ctx context.Context, cfg *config.Config, model model. } func (l *ListVideoCaseLogic) ListVideoCase(req *types.ListVideoCaseReq) (resp *types.ListVideoCaseResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验与默认值处理 + if req.Page <= 0 { + req.Page = 1 // 默认第一页 + } + if req.Size <= 0 || req.Size > 100 { + req.Size = 10 // 限制每页最大100条,默认10条 + } - return + // 2. 分页计算:offset = (页码-1) * 每页条数 + offset := (req.Page - 1) * req.Size + + // 3. 调用Model层查询:先查总条数,再查分页数据 + total, err := l.model.Count(l.ctx, req.Keyword) + if err != nil { + return nil, fmt.Errorf("查询视频案例总条数失败:%w", err) + } + + list, err := l.model.ListByPage(l.ctx, req.Keyword, req.Sort, offset, req.Size) + if err != nil { + return nil, fmt.Errorf("查询视频案例列表失败:%w", err) + } + + // 4. 数据映射:Model → 响应结构体(处理特殊类型转换) + respList := make([]types.VideoCaseResp, 0, len(list)) + for _, item := range list { + // 4.1 处理Intro字段(sql.NullString → string) + intro := "" + if item.Intro.Valid { + intro = item.Intro.String + } + + // 4.2 处理时间字段(time.Time → string,统一格式) + createTime := "" + if !item.CreateTime.IsZero() { + createTime = item.CreateTime.Format("2006-01-02 15:04:05") + } + updateTime := "" + if !item.UpdateTime.IsZero() { + updateTime = item.UpdateTime.Format("2006-01-02 15:04:05") + } + + // 4.3 组装单条响应数据 + respList = append(respList, types.VideoCaseResp{ + Id: int(item.Id), + Title: item.Title, + Intro: intro, + VideoUrl: item.VideoUrl, + DesignerNames: item.DesignerNames, + TutorNames: item.TutorNames, + Sort: int(item.Sort), + CreateTime: createTime, + UpdateTime: updateTime, + }) + } + + // 5. 组装最终响应 + resp = &types.ListVideoCaseResp{ + Total: total, + List: respList, + } + + return resp, nil } diff --git a/server/internal/video_case/internal/logic/video_case/updatevideocaselogic.go b/server/internal/video_case/internal/logic/video_case/updatevideocaselogic.go index 17cbcb44..e97b5716 100644 --- a/server/internal/video_case/internal/logic/video_case/updatevideocaselogic.go +++ b/server/internal/video_case/internal/logic/video_case/updatevideocaselogic.go @@ -5,6 +5,7 @@ package video_case import ( "context" + "fmt" "github.com/JACKYMYPERSON/hldrCenter/config" "github.com/JACKYMYPERSON/hldrCenter/internal/video_case/internal/model" @@ -30,7 +31,62 @@ func NewUpdateVideoCaseLogic(ctx context.Context, cfg *config.Config, model mode } func (l *UpdateVideoCaseLogic) UpdateVideoCase(req *types.UpdateVideoCaseReq) (resp *types.BaseResp, err error) { - // todo: add your logic here and delete this line + // 1. 参数校验:ID为必填且合法 + if req.Id <= 0 { + return &types.BaseResp{Code: 400, Msg: "视频案例ID不合法"}, nil + } + id := int64(req.Id) - return + // 2. 校验案例是否存在且未被删除 + existCase, err := l.model.FindOne(l.ctx, id) + if err != nil { + if err == model.ErrNotFound { + return &types.BaseResp{Code: 404, Msg: "视频案例不存在或已删除"}, nil + } + return &types.BaseResp{Code: 500, Msg: fmt.Sprintf("查询视频案例失败:%s", err.Error())}, err + } + + // 3. 部分字段更新:只更新请求中提供的非空/有效值(保留原有数据) + updateData := &model.VideoCase{ + Id: existCase.Id, // 必须保留ID(更新条件) + IsDelete: existCase.IsDelete, // 锁定软删除状态(不允许修改) + Title: existCase.Title, + Intro: existCase.Intro, // 初始化为原有值(sql.NullString类型) + VideoUrl: existCase.VideoUrl, + DesignerNames: existCase.DesignerNames, + TutorNames: existCase.TutorNames, + Sort: existCase.Sort, + } + + // 覆盖请求中传递的非空字段(注意类型转换) + if req.Title != "" { + updateData.Title = req.Title + } + if req.VideoUrl != "" { + updateData.VideoUrl = req.VideoUrl + } + if req.DesignerNames != "" { + updateData.DesignerNames = req.DesignerNames + } + if req.TutorNames != "" { + updateData.TutorNames = req.TutorNames + } + // 处理Intro字段(string → sql.NullString) + if req.Intro != "" || (req.Intro == "" && existCase.Intro.Valid) { + // 若请求传空字符串,或原有值非空,均需更新(覆盖为NULL或新值) + updateData.Intro = StringToNullString(req.Intro) + } + // 处理Sort字段(0是合法值,允许主动设置) + if req.Sort != 0 { + updateData.Sort = int64(req.Sort) + } + + // 4. 调用Model层执行更新 + err = l.model.Update(l.ctx, updateData) + if err != nil { + return &types.BaseResp{Code: 500, Msg: fmt.Sprintf("更新视频案例失败:%s", err.Error())}, err + } + + // 5. 成功响应 + return &types.BaseResp{Code: 0, Msg: "视频案例更新成功"}, nil } diff --git a/server/internal/video_case/internal/model/videocasemodel_gen.go b/server/internal/video_case/internal/model/videocasemodel_gen.go index 1260572a..8dc49127 100644 --- a/server/internal/video_case/internal/model/videocasemodel_gen.go +++ b/server/internal/video_case/internal/model/videocasemodel_gen.go @@ -29,6 +29,8 @@ type ( FindOne(ctx context.Context, id int64) (*VideoCase, error) Update(ctx context.Context, data *VideoCase) error Delete(ctx context.Context, id int64) error + Count(ctx context.Context, keyword string) (int64, error) + ListByPage(ctx context.Context, keyword string, sort int, offset, size int) ([]*VideoCase, error) } defaultVideoCaseModel struct { @@ -58,13 +60,14 @@ func newVideoCaseModel(conn sqlx.SqlConn) *defaultVideoCaseModel { } func (m *defaultVideoCaseModel) Delete(ctx context.Context, id int64) error { - query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + query := fmt.Sprintf("update %s set `is_delete` = 1, `update_time` = CURRENT_TIMESTAMP where `id` = ? and `is_delete` = 0", m.table) _, err := m.conn.ExecCtx(ctx, query, id) return err } func (m *defaultVideoCaseModel) FindOne(ctx context.Context, id int64) (*VideoCase, error) { - query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", videoCaseRows, m.table) + // 关键:仅查询未删除的案例(is_delete=0) + query := fmt.Sprintf("select %s from %s where `id` = ? and `is_delete` = 0 limit 1", videoCaseRows, m.table) var resp VideoCase err := m.conn.QueryRowCtx(ctx, &resp, query, id) switch err { @@ -89,6 +92,61 @@ func (m *defaultVideoCaseModel) Update(ctx context.Context, data *VideoCase) err return err } +func (m *defaultVideoCaseModel) Count(ctx context.Context, keyword string) (int64, error) { + query := fmt.Sprintf("select count(id) from %s where is_delete = 0", m.table) + args := make([]interface{}, 0) + + // 关键词搜索:模糊匹配标题、设计人员、指导老师 + if keyword != "" { + keyword = strings.TrimSpace(keyword) + query += " and (title like ? or designer_names like ? or tutor_names like ?)" + likeVal := "%" + keyword + "%" + args = append(args, likeVal, likeVal, likeVal) // 三个字段共用一个模糊值 + } + + var total int64 + // sqlx.QueryRowCtx自动扫描结果到total(无需额外Scan) + err := m.conn.QueryRowCtx(ctx, &total, query, args...) + if err != nil { + return 0, err + } + return total, nil +} + +// ListByPage 分页查询未删除的视频案例列表(支持关键词、排序) +func (m *defaultVideoCaseModel) ListByPage(ctx context.Context, keyword string, sort int, offset, size int) ([]*VideoCase, error) { + query := fmt.Sprintf("select %s from %s where is_delete = 0", videoCaseRows, m.table) + args := make([]interface{}, 0) + + // 关键词搜索条件(与Count方法一致) + if keyword != "" { + keyword = strings.TrimSpace(keyword) + query += " and (title like ? or designer_names like ? or tutor_names like ?)" + likeVal := "%" + keyword + "%" + args = append(args, likeVal, likeVal, likeVal) + } + + // 排序逻辑:优先按sort升序(数字越小越靠前),否则按创建时间降序 + query += " order by " + if sort != 0 { + query += "sort asc, create_time desc" + } else { + query += "create_time desc" + } + + // 分页条件 + query += " limit ?, ?" + args = append(args, offset, size) + + // 执行查询 + var list []*VideoCase + err := m.conn.QueryRowsCtx(ctx, &list, query, args...) + if err != nil { + return nil, err + } + return list, nil +} + func (m *defaultVideoCaseModel) tableName() string { return m.table }