package service import ( dao "assets/dao/asset" dto "assets/model/dto/asset" entity "assets/model/entity/asset" "context" "errors" "gitea.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.Scan(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 }