219 lines
6.1 KiB
Go
219 lines
6.1 KiB
Go
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
|
|
}
|