initial commit
This commit is contained in:
31
category/internal/logic/batchcreatecategorieslogic.go
Normal file
31
category/internal/logic/batchcreatecategorieslogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type BatchCreateCategoriesLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewBatchCreateCategoriesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchCreateCategoriesLogic {
|
||||
return &BatchCreateCategoriesLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 批量操作
|
||||
func (l *BatchCreateCategoriesLogic) BatchCreateCategories(in *category.BatchCreateRequest) (*category.BatchCreateResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.BatchCreateResponse{}, nil
|
||||
}
|
||||
30
category/internal/logic/batchupdatecategorieslogic.go
Normal file
30
category/internal/logic/batchupdatecategorieslogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type BatchUpdateCategoriesLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewBatchUpdateCategoriesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchUpdateCategoriesLogic {
|
||||
return &BatchUpdateCategoriesLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchUpdateCategoriesLogic) BatchUpdateCategories(in *category.BatchUpdateRequest) (*category.BatchUpdateResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.BatchUpdateResponse{}, nil
|
||||
}
|
||||
30
category/internal/logic/checkaliaslogic.go
Normal file
30
category/internal/logic/checkaliaslogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CheckAliasLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCheckAliasLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckAliasLogic {
|
||||
return &CheckAliasLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CheckAliasLogic) CheckAlias(in *category.CheckAliasRequest) (*category.CheckAliasResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CheckAliasResponse{}, nil
|
||||
}
|
||||
118
category/internal/logic/createcategorylogic.go
Normal file
118
category/internal/logic/createcategorylogic.go
Normal file
@ -0,0 +1,118 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/model"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type CreateCategoryLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCreateCategoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateCategoryLogic {
|
||||
return &CreateCategoryLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateCategoryLogic) CreateCategory(in *category.CreateCategoryRequest) (*category.CategoryInfoResponse, error) {
|
||||
// 参数校验
|
||||
if in.SystemId == "" || in.Name == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "system_id 和 name 不能为空")
|
||||
}
|
||||
|
||||
// 检查唯一性
|
||||
exists, err := l.checkCategoryExists(in)
|
||||
if err != nil {
|
||||
logx.Error("唯一性检查失败: ", err)
|
||||
return nil, status.Error(codes.Internal, "内部错误")
|
||||
}
|
||||
if exists {
|
||||
return nil, status.Error(codes.AlreadyExists, "分类已存在")
|
||||
}
|
||||
|
||||
// 构建模型
|
||||
newCategory := &model.Categories{
|
||||
Id: uuid.New().String(),
|
||||
SystemId: in.SystemId,
|
||||
Name: in.Name,
|
||||
Alias: toNullString(in.Alias),
|
||||
ParentId: toNullString(in.ParentId),
|
||||
Description: toNullString(in.Description),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}
|
||||
|
||||
// 插入数据库
|
||||
if _, err := l.svcCtx.CategoryModel.Insert(l.ctx, newCategory); err != nil {
|
||||
logx.Error("插入失败: ", err)
|
||||
return nil, status.Error(codes.Internal, "创建分类失败")
|
||||
}
|
||||
|
||||
// 返回响应
|
||||
return &category.CategoryInfoResponse{
|
||||
Category: &category.CategoryInfo{
|
||||
Id: newCategory.Id,
|
||||
SystemId: newCategory.SystemId,
|
||||
Name: newCategory.Name,
|
||||
Alias: in.Alias,
|
||||
ParentId: in.ParentId,
|
||||
Description: in.Description,
|
||||
CreatedAt: newCategory.CreatedAt.Unix(),
|
||||
UpdatedAt: newCategory.UpdatedAt.Time.Unix(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 空字符串转换为 sql.NullString
|
||||
func toNullString(s string) sql.NullString {
|
||||
return sql.NullString{String: s, Valid: s != ""}
|
||||
}
|
||||
|
||||
// 唯一性检查逻辑
|
||||
func (l *CreateCategoryLogic) checkCategoryExists(in *category.CreateCategoryRequest) (bool, error) {
|
||||
// 检查名称唯一性
|
||||
_, err := l.svcCtx.CategoryModel.FindBySystemParentName(
|
||||
l.ctx,
|
||||
in.SystemId,
|
||||
in.ParentId,
|
||||
in.Name,
|
||||
)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
} else if !errors.Is(err, model.ErrNotFound) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 检查别名唯一性(如果提供了别名)
|
||||
if in.Alias != "" {
|
||||
_, err := l.svcCtx.CategoryModel.FindBySystemParentAlias(
|
||||
l.ctx,
|
||||
in.SystemId,
|
||||
in.ParentId,
|
||||
in.Alias,
|
||||
)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
} else if !errors.Is(err, model.ErrNotFound) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
91
category/internal/logic/deletecategorylogic.go
Normal file
91
category/internal/logic/deletecategorylogic.go
Normal file
@ -0,0 +1,91 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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 DeleteCategoryLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewDeleteCategoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteCategoryLogic {
|
||||
return &DeleteCategoryLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeleteCategoryLogic) DeleteCategory(in *category.DeleteCategoryRequest) (*category.DeleteResponse, error) {
|
||||
// 1. 参数校验
|
||||
if in.Id == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "分类ID不能为空")
|
||||
}
|
||||
|
||||
// 2. 检查分类是否存在
|
||||
_, err := l.svcCtx.CategoryModel.FindOne(l.ctx, in.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, status.Error(codes.NotFound, "分类不存在")
|
||||
}
|
||||
l.Logger.Errorf("查询分类失败: %v", err)
|
||||
return nil, status.Error(codes.Internal, "内部错误")
|
||||
}
|
||||
|
||||
// 3. 开启事务执行删除
|
||||
err = l.svcCtx.CategoryModel.Transact(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 3.1 处理子分类(根据外键约束自动级联删除或设为NULL)
|
||||
if err := l.handleChildren(ctx, session, in.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3.2 删除主分类
|
||||
if err := l.svcCtx.CategoryModel.WithSession(session).Delete(ctx, in.Id); err != nil {
|
||||
return fmt.Errorf("删除分类失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
l.Logger.Errorf("删除分类事务失败: %v", err)
|
||||
return nil, status.Error(codes.Internal, "删除分类失败")
|
||||
}
|
||||
|
||||
return &category.DeleteResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handleChildren 处理子分类(根据外键约束配置决定行为)
|
||||
func (l *DeleteCategoryLogic) handleChildren(ctx context.Context, session sqlx.Session, parentID string) error {
|
||||
// 如果外键是 ON DELETE SET NULL,需要显式更新子分类
|
||||
if l.needUpdateChildren() {
|
||||
query := "UPDATE categories SET parent_id = NULL WHERE parent_id = $1"
|
||||
if _, err := session.ExecCtx(ctx, query, parentID); err != nil {
|
||||
return fmt.Errorf("更新子分类失败: %w", err)
|
||||
}
|
||||
}
|
||||
// 如果是 ON DELETE CASCADE 则无需额外处理
|
||||
return nil
|
||||
}
|
||||
|
||||
// needUpdateChildren 根据配置决定是否需要更新子分类
|
||||
func (l *DeleteCategoryLogic) needUpdateChildren() bool {
|
||||
// 这里根据实际数据库外键约束配置返回
|
||||
// 可以在 config 中添加配置项控制行为
|
||||
return false // 默认假设是 ON DELETE CASCADE
|
||||
}
|
||||
30
category/internal/logic/getancestorpathlogic.go
Normal file
30
category/internal/logic/getancestorpathlogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAncestorPathLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetAncestorPathLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAncestorPathLogic {
|
||||
return &GetAncestorPathLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAncestorPathLogic) GetAncestorPath(in *category.GetAncestorPathRequest) (*category.CategoryPathResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CategoryPathResponse{}, nil
|
||||
}
|
||||
85
category/internal/logic/getcategorylogic.go
Normal file
85
category/internal/logic/getcategorylogic.go
Normal file
@ -0,0 +1,85 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/model"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type GetCategoryLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetCategoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCategoryLogic {
|
||||
return &GetCategoryLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetCategoryLogic) GetCategory(in *category.GetCategoryRequest) (*category.CategoryInfoResponse, error) {
|
||||
// 1. 参数校验
|
||||
if in.Id == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "分类ID不能为空")
|
||||
}
|
||||
|
||||
// 2. 查询数据库
|
||||
categoryData, err := l.svcCtx.CategoryModel.FindOne(l.ctx, in.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
l.Logger.Errorf("分类不存在, ID: %s", in.Id)
|
||||
return nil, status.Error(codes.NotFound, "分类不存在")
|
||||
}
|
||||
l.Logger.Errorf("数据库查询失败: %v", err)
|
||||
return nil, status.Error(codes.Internal, "内部错误")
|
||||
}
|
||||
|
||||
// 3. 转换响应格式
|
||||
return l.convertToResponse(categoryData), nil
|
||||
}
|
||||
|
||||
func (l *GetCategoryLogic) convertToResponse(c *model.Categories) *category.CategoryInfoResponse {
|
||||
// 处理可能为空的字段
|
||||
var alias, parentID, description string
|
||||
|
||||
if c.Alias.Valid {
|
||||
alias = c.Alias.String
|
||||
}
|
||||
if c.ParentId.Valid {
|
||||
parentID = c.ParentId.String
|
||||
}
|
||||
if c.Description.Valid {
|
||||
description = c.Description.String
|
||||
}
|
||||
|
||||
// 处理时间戳
|
||||
var updatedAt int64
|
||||
if c.UpdatedAt.Valid {
|
||||
updatedAt = c.UpdatedAt.Time.Unix()
|
||||
} else {
|
||||
updatedAt = c.CreatedAt.Unix() // 如果未更新过,使用创建时间
|
||||
}
|
||||
|
||||
return &category.CategoryInfoResponse{
|
||||
Category: &category.CategoryInfo{
|
||||
Id: c.Id,
|
||||
SystemId: c.SystemId,
|
||||
Name: c.Name,
|
||||
Alias: alias,
|
||||
ParentId: parentID,
|
||||
Description: description,
|
||||
CreatedAt: c.CreatedAt.Unix(),
|
||||
UpdatedAt: updatedAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
31
category/internal/logic/getchildrenlogic.go
Normal file
31
category/internal/logic/getchildrenlogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetChildrenLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetChildrenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetChildrenLogic {
|
||||
return &GetChildrenLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 层级结构操作
|
||||
func (l *GetChildrenLogic) GetChildren(in *category.GetChildrenRequest) (*category.CategoryListResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CategoryListResponse{}, nil
|
||||
}
|
||||
30
category/internal/logic/gettreelogic.go
Normal file
30
category/internal/logic/gettreelogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetTreeLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetTreeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTreeLogic {
|
||||
return &GetTreeLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetTreeLogic) GetTree(in *category.GetTreeRequest) (*category.CategoryTreeResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CategoryTreeResponse{}, nil
|
||||
}
|
||||
31
category/internal/logic/listcategorieslogic.go
Normal file
31
category/internal/logic/listcategorieslogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ListCategoriesLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewListCategoriesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListCategoriesLogic {
|
||||
return &ListCategoriesLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 查询过滤
|
||||
func (l *ListCategoriesLogic) ListCategories(in *category.ListCategoryRequest) (*category.CategoryListResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CategoryListResponse{}, nil
|
||||
}
|
||||
30
category/internal/logic/movecategorylogic.go
Normal file
30
category/internal/logic/movecategorylogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type MoveCategoryLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewMoveCategoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MoveCategoryLogic {
|
||||
return &MoveCategoryLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *MoveCategoryLogic) MoveCategory(in *category.MoveCategoryRequest) (*category.CategoryInfoResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.CategoryInfoResponse{}, nil
|
||||
}
|
||||
31
category/internal/logic/pinglogic.go
Normal file
31
category/internal/logic/pinglogic.go
Normal file
@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"godemo/category/category"
|
||||
"godemo/category/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type PingLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic {
|
||||
return &PingLogic{
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
Logger: logx.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
func (l *PingLogic) Ping(in *category.PingRequest) (*category.PingResponse, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return &category.PingResponse{}, nil
|
||||
}
|
||||
218
category/internal/logic/updatecategorylogic.go
Normal file
218
category/internal/logic/updatecategorylogic.go
Normal file
@ -0,0 +1,218 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user