Files
assets/service/asset/category_service.go
2026-06-10 15:40:17 +08:00

266 lines
7.0 KiB
Go

package service
import (
dao "assets/dao/asset"
dto "assets/model/dto/asset"
entity "assets/model/entity/asset"
"context"
"errors"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/frame/g"
)
const (
// DefaultPathSeparator 路径分隔符
DefaultPathSeparator = "/"
)
// CategoryService 分类服务
type CategoryService struct{}
var Category = new(CategoryService)
// Create 创建分类
func (s *CategoryService) Create(ctx context.Context, req *dto.CreateCategoryReq) (res *dto.CreateCategoryRes, err error) {
// 构建分类路径和层级
parent, err := dao.Category.GetOne(ctx, &dto.GetCategoryReq{Id: req.ParentId})
if err != nil {
return nil, err
}
if g.IsEmpty(parent) {
return nil, errors.New("父分类不存在")
}
req.Path = parent.Path + DefaultPathSeparator + gconv.String(req.ParentId)
req.Level = parent.Level + 1
err = gfdb.DB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
var id int64
req.IsLeafNode = gconv.PtrBool(true)
if id, err = dao.Category.Insert(ctx, req); err != nil {
return err
}
// 更新父节点的 IsLeafNode 为 false
if err = s.updateLeafNode(ctx, req.ParentId, false); err != nil {
return err
}
res = &dto.CreateCategoryRes{
Id: id,
}
return nil
})
return
}
// updateLeafNode 更新分类的 IsLeafNode 字段
func (s *CategoryService) updateLeafNode(ctx context.Context, categoryId int64, isLeaf bool) (err error) {
_, err = dao.Category.Update(ctx, &dto.UpdateCategoryReq{
Id: categoryId,
IsLeafNode: &isLeaf,
})
return
}
// GetOne 获取单个分类
func (s *CategoryService) GetOne(ctx context.Context, req *dto.GetCategoryReq) (*dto.GetCategoryRes, error) {
one, err := dao.Category.GetOne(ctx, req)
if err != nil {
return nil, err
}
res := new(dto.GetCategoryRes)
if err = gconv.Struct(one, &res); err != nil {
panic(err)
}
return res, err
}
// List 获取分类列表
func (s *CategoryService) List(ctx context.Context, req *dto.ListCategoryReq) (*dto.ListCategoryRes, error) {
list, total, err := dao.Category.List(ctx, req)
if err != nil {
return nil, err
}
res := &dto.ListCategoryRes{Total: total}
err = gconv.Struct(list, &res.List)
if err != nil {
return nil, err
}
return res, nil
}
// GetTree 获取分类树
func (s *CategoryService) GetTree(ctx context.Context, req *dto.GetCategoryTreeReq) (*dto.GetCategoryTreeRes, error) {
list, _, err := dao.Category.List(ctx, &dto.ListCategoryReq{
Keyword: req.Name,
})
if err != nil {
return nil, err
}
tree, err := s.buildTree(ctx, list, 0)
if err != nil {
return nil, err
}
return &dto.GetCategoryTreeRes{Tree: tree}, nil
}
// buildTree 构建树形结构
func (s *CategoryService) buildTree(ctx context.Context, categories []entity.Category, parentId int64) ([]*dto.CategoryTreeNode, error) {
tree := make([]*dto.CategoryTreeNode, 0)
for _, cat := range categories {
if !s.isChildOf(cat.ParentId, parentId) {
continue
}
node, err := s.convertToTreeNode(ctx, &cat, categories)
if err != nil {
return nil, err
}
tree = append(tree, node)
}
return tree, nil
}
// isChildOf 判断分类是否为指定父节点的子节点
func (s *CategoryService) isChildOf(childParentId, parentId int64) bool {
if g.IsEmpty(childParentId) && g.IsEmpty(parentId) {
return true
}
if g.IsEmpty(childParentId) || g.IsEmpty(parentId) {
return false
}
return childParentId == parentId
}
// convertToTreeNode 将分类实体转换为树节点
func (s *CategoryService) convertToTreeNode(ctx context.Context, category *entity.Category, allCategories []entity.Category) (*dto.CategoryTreeNode, error) {
res := new(dto.CategoryTreeNode)
if err := gconv.Struct(&category, &res); err != nil {
return res, err
}
children, err := s.buildTree(ctx, allCategories, category.Id)
if err != nil {
return res, err
}
res.Children = children
res.IsLeafNode = len(children) == 0
return res, nil
}
// hasChildren 检查分类是否有子分类
func (s *CategoryService) hasChildren(ctx context.Context, parentId int64) (bool, error) {
count, err := dao.Category.Count(ctx, &dto.ListCategoryReq{
ParentId: parentId,
})
return count > 0, err
}
// updateLeafNodeIfNoChildren 如果父分类没有子分类了,更新为叶子节点
func (s *CategoryService) updateLeafNodeIfNoChildren(ctx context.Context, parentId int64) error {
hasChildren, err := s.hasChildren(ctx, parentId)
if err != nil {
return err
}
return s.updateLeafNode(ctx, parentId, !hasChildren)
}
// UpdateStatus 更新分类状态
func (s *CategoryService) UpdateStatus(ctx context.Context, req *dto.UpdateCategoryStatusReq) (err error) {
var updateReq *dto.UpdateCategoryReq
if err = gconv.Struct(req, &updateReq); err != nil {
return
}
_, err = dao.Category.Update(ctx, updateReq)
return
}
// Update 更新分类
func (s *CategoryService) Update(ctx context.Context, req *dto.UpdateCategoryReq) error {
oldCategory, err := dao.Category.GetOne(ctx, &dto.GetCategoryReq{Id: req.Id})
if err != nil {
return err
}
err = gfdb.DB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
if rows, err := dao.Category.Update(ctx, req); err != nil && rows > 0 {
return err
}
// 如果修改了父分类,需要更新相关节点的 IsLeafNode
if s.parentIdChanged(req.ParentId, oldCategory.ParentId) {
if !g.IsEmpty(req.ParentId) {
if err = s.updateLeafNode(ctx, req.ParentId, false); err != nil {
return err
}
}
if !g.IsEmpty(oldCategory.ParentId) {
if err = s.updateLeafNodeIfNoChildren(ctx, oldCategory.ParentId); err != nil {
return err
}
}
}
return nil
})
return err
}
// parentIdChanged 判断父分类是否发生变化
func (s *CategoryService) parentIdChanged(newParentId, oldParentId int64) bool {
if g.IsEmpty(newParentId) && g.IsEmpty(oldParentId) {
return false
}
if g.IsEmpty(newParentId) || g.IsEmpty(oldParentId) {
return true
}
return newParentId != oldParentId
}
// Delete 删除分类
func (s *CategoryService) Delete(ctx context.Context, req *dto.DeleteCategoryReq) error {
// 检查是否有子分类
hasChildren, err := s.hasChildren(ctx, req.Id)
if err != nil {
return err
}
if hasChildren {
return errors.New("该分类下有子分类,无法删除")
}
// 检查是否有资产
if count, err := dao.Asset.Count(ctx, &dto.ListAssetReq{
CategoryId: req.Id,
}); err != nil {
return err
} else if count > 0 {
return errors.New("该分类下有资产信息,无法删除")
}
// 获取分类信息用于后续操作
category, err := dao.Category.GetOne(ctx, &dto.GetCategoryReq{Id: req.Id})
if err != nil {
return err
}
err = gfdb.DB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 删除分类
if rows, err := dao.Category.Delete(ctx, req); err != nil && rows > 0 {
return err
}
// 如果删除的是叶子节点,更新父节点为叶子节点
if !g.IsEmpty(category.ParentId) {
return s.updateLeafNodeIfNoChildren(ctx, category.ParentId)
}
return nil
})
return nil
}