package logic import ( "context" "database/sql" "errors" "fmt" "time" "godemo/category/category" "godemo/category/internal/model" "godemo/category/internal/svc" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type UpdateCategoryLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewUpdateCategoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateCategoryLogic { return &UpdateCategoryLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *UpdateCategoryLogic) UpdateCategory(in *category.UpdateCategoryRequest) (*category.CategoryInfoResponse, error) { // 1. 参数校验 if err := l.validateRequest(in); err != nil { return nil, err } // 2. 获取现有分类 existing, err := l.svcCtx.CategoryModel.FindOne(l.ctx, in.Id) if err != nil { return l.handleFindError(err, in.Id) } // 3. 检查更新后的名称/别名是否冲突 if err := l.checkUniqueness(in, existing); err != nil { return nil, err } // 4. 构建更新模型 updatedCategory := l.buildUpdatedModel(in, existing) // 5. 事务更新 if err := l.updateWithTransaction(updatedCategory); err != nil { return nil, err } // 6. 清理缓存 // 7. 返回更新后的数据 return l.buildResponse(updatedCategory), nil } // 参数校验 func (l *UpdateCategoryLogic) validateRequest(in *category.UpdateCategoryRequest) error { if in.Id == "" { return status.Error(codes.InvalidArgument, "分类ID不能为空") } if in.Name == "" && in.Alias == "" && in.ParentId == "" && in.Description == "" { return status.Error(codes.InvalidArgument, "至少需要提供一个更新字段") } return nil } // 处理查询错误 func (l *UpdateCategoryLogic) handleFindError(err error, id string) (*category.CategoryInfoResponse, error) { if errors.Is(err, model.ErrNotFound) { l.Logger.Errorf("分类不存在, ID: %s", id) return nil, status.Error(codes.NotFound, "分类不存在") } l.Logger.Errorf("查询分类失败: %v", err) return nil, status.Error(codes.Internal, "内部错误") } // 检查名称/别名唯一性 func (l *UpdateCategoryLogic) checkUniqueness(in *category.UpdateCategoryRequest, existing *model.Categories) error { // 如果名称有修改,检查新名称是否冲突 if in.Name != "" && in.Name != existing.Name { parentID := existing.ParentId if in.ParentId != "" { parentID = sql.NullString{String: in.ParentId, Valid: true} } _, err := l.svcCtx.CategoryModel.FindBySystemParentName( l.ctx, existing.SystemId, parentID.String, in.Name, ) if err == nil { return status.Error(codes.AlreadyExists, "分类名称已存在") } else if !errors.Is(err, model.ErrNotFound) { l.Logger.Errorf("名称唯一性检查失败: %v", err) return status.Error(codes.Internal, "内部错误") } } // 如果别名有修改,检查新别名是否冲突 if in.Alias != "" && in.Alias != existing.Alias.String { parentID := existing.ParentId if in.ParentId != "" { parentID = sql.NullString{String: in.ParentId, Valid: true} } _, err := l.svcCtx.CategoryModel.FindBySystemParentAlias( l.ctx, existing.SystemId, parentID.String, in.Alias, ) if err == nil { return status.Error(codes.AlreadyExists, "分类别名已存在") } else if !errors.Is(err, model.ErrNotFound) { l.Logger.Errorf("别名唯一性检查失败: %v", err) return status.Error(codes.Internal, "内部错误") } } return nil } // 构建更新后的模型 func (l *UpdateCategoryLogic) buildUpdatedModel(in *category.UpdateCategoryRequest, existing *model.Categories) *model.Categories { updated := *existing // 复制原有值 // 只更新提供的字段 if in.Name != "" { updated.Name = in.Name } if in.Alias != "" { updated.Alias = sql.NullString{String: in.Alias, Valid: true} } else if in.Alias == "" && existing.Alias.Valid { // 明确传递空字符串表示清空别名 updated.Alias = sql.NullString{Valid: false} } if in.ParentId != "" { if in.ParentId == "NULL" { // 特殊值表示设为NULL updated.ParentId = sql.NullString{Valid: false} } else { updated.ParentId = sql.NullString{String: in.ParentId, Valid: true} } } if in.Description != "" { updated.Description = sql.NullString{String: in.Description, Valid: true} } else if in.Description == "" && existing.Description.Valid { // 明确传递空字符串表示清空描述 updated.Description = sql.NullString{Valid: false} } updated.UpdatedAt = sql.NullTime{Time: time.Now(), Valid: true} return &updated } // 事务更新 func (l *UpdateCategoryLogic) updateWithTransaction(updated *model.Categories) error { return l.svcCtx.CategoryModel.Transact(l.ctx, func(ctx context.Context, session sqlx.Session) error { // 检查父分类是否存在(如果修改了父分类) if updated.ParentId.Valid && updated.ParentId.String != "" { if _, err := l.svcCtx.CategoryModel.WithSession(session).FindOne(ctx, updated.ParentId.String); err != nil { if errors.Is(err, model.ErrNotFound) { return status.Error(codes.InvalidArgument, "指定的父分类不存在") } return fmt.Errorf("查询父分类失败: %w", err) } } // 执行更新 if err := l.svcCtx.CategoryModel.WithSession(session).Update(ctx, updated); err != nil { return fmt.Errorf("更新分类失败: %w", err) } return nil }) } // 构建响应 func (l *UpdateCategoryLogic) buildResponse(c *model.Categories) *category.CategoryInfoResponse { resp := &category.CategoryInfoResponse{ Category: &category.CategoryInfo{ Id: c.Id, SystemId: c.SystemId, Name: c.Name, CreatedAt: c.CreatedAt.Unix(), }, } // 处理可能为空的字段 if c.Alias.Valid { resp.Category.Alias = c.Alias.String } if c.ParentId.Valid { resp.Category.ParentId = c.ParentId.String } if c.Description.Valid { resp.Category.Description = c.Description.String } if c.UpdatedAt.Valid { resp.Category.UpdatedAt = c.UpdatedAt.Time.Unix() } else { resp.Category.UpdatedAt = c.CreatedAt.Unix() } return resp }