|
@@ -0,0 +1,483 @@
|
|
|
|
|
+"""
|
|
|
|
|
+标签分类服务层
|
|
|
|
|
+处理标签分类的业务逻辑、树形结构管理、路径维护等
|
|
|
|
|
+"""
|
|
|
|
|
+import logging
|
|
|
|
|
+from typing import Optional, List, Dict, Tuple, Any
|
|
|
|
|
+from datetime import datetime, timezone
|
|
|
|
|
+
|
|
|
|
|
+from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
+from sqlalchemy import select, update, and_, or_
|
|
|
|
|
+from app.sample.models.tag_category import TagCategory
|
|
|
|
|
+from app.sample.schemas.tag_category import (
|
|
|
|
|
+ TagCategoryCreate, TagCategoryUpdate, TagCategoryResponse,
|
|
|
|
|
+ TagCategoryTreeNode
|
|
|
|
|
+)
|
|
|
|
|
+from app.services.user_service import UserService
|
|
|
|
|
+
|
|
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class TagCategoryService:
|
|
|
|
|
+ """标签分类服务类 - db session由路由层注入"""
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, session: AsyncSession):
|
|
|
|
|
+ """初始化服务,接收数据库session"""
|
|
|
|
|
+ self.session = session
|
|
|
|
|
+ self.user_service = UserService(session)
|
|
|
|
|
+
|
|
|
|
|
+ async def create_tag_category(
|
|
|
|
|
+ self,
|
|
|
|
|
+ tag_data: TagCategoryCreate,
|
|
|
|
|
+ created_by: Optional[str] = None
|
|
|
|
|
+ ) -> TagCategoryResponse:
|
|
|
|
|
+ """创建标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ path = "/"
|
|
|
|
|
+ level = 1
|
|
|
|
|
+
|
|
|
|
|
+ if tag_data.parent_id != 0:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.id == tag_data.parent_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ parent = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not parent:
|
|
|
|
|
+ raise ValueError(f"父级分类ID {tag_data.parent_id} 不存在")
|
|
|
|
|
+
|
|
|
|
|
+ path = f"{parent.path}{tag_data.parent_id}/"
|
|
|
|
|
+ level = parent.level + 1
|
|
|
|
|
+
|
|
|
|
|
+ tag_category = TagCategory(
|
|
|
|
|
+ parent_id=tag_data.parent_id,
|
|
|
|
|
+ name=tag_data.name,
|
|
|
|
|
+ path=path,
|
|
|
|
|
+ level=level,
|
|
|
|
|
+ sort_no=tag_data.sort_no or 0,
|
|
|
|
|
+ status=tag_data.status or 1,
|
|
|
|
|
+ created_by=created_by,
|
|
|
|
|
+ created_at=datetime.now(timezone.utc),
|
|
|
|
|
+ updated_by=created_by,
|
|
|
|
|
+ updated_at=datetime.now(timezone.utc)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ self.session.add(tag_category)
|
|
|
|
|
+ await self.session.flush()
|
|
|
|
|
+ await self.session.refresh(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f"创建标签分类成功: {tag_category.id} - {tag_category.name}")
|
|
|
|
|
+ return await self._enrich_with_user_names(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"创建标签分类失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def get_tag_category_by_id(self, category_id: int) -> Optional[TagCategoryResponse]:
|
|
|
|
|
+ """根据ID获取标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.id == category_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ tag_category = result.scalar_one_or_none()
|
|
|
|
|
+ if tag_category:
|
|
|
|
|
+ return await self._enrich_with_user_names(tag_category)
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"查询标签分类失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def list_tag_categories(
|
|
|
|
|
+ self,
|
|
|
|
|
+ parent_id: Optional[int] = None,
|
|
|
|
|
+ status: Optional[int] = None,
|
|
|
|
|
+ skip: int = 0,
|
|
|
|
|
+ limit: int = 100
|
|
|
|
|
+ ) -> List[TagCategoryResponse]:
|
|
|
|
|
+ """列表查询标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ conditions = [TagCategory.is_deleted == 0]
|
|
|
|
|
+
|
|
|
|
|
+ if parent_id is not None:
|
|
|
|
|
+ conditions.append(TagCategory.parent_id == parent_id)
|
|
|
|
|
+
|
|
|
|
|
+ if status is not None:
|
|
|
|
|
+ conditions.append(TagCategory.status == status)
|
|
|
|
|
+
|
|
|
|
|
+ query = select(TagCategory).where(and_(*conditions))
|
|
|
|
|
+ query = query.order_by(TagCategory.sort_no, TagCategory.id)
|
|
|
|
|
+ query = query.offset(skip).limit(limit)
|
|
|
|
|
+
|
|
|
|
|
+ result = await self.session.execute(query)
|
|
|
|
|
+ categories = result.scalars().all()
|
|
|
|
|
+
|
|
|
|
|
+ return await self._enrich_list_with_user_names(categories)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"查询标签分类列表失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def update_tag_category(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_id: int,
|
|
|
|
|
+ tag_data: TagCategoryUpdate,
|
|
|
|
|
+ updated_by: Optional[str] = None
|
|
|
|
|
+ ) -> TagCategoryResponse:
|
|
|
|
|
+ """更新标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.id == category_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ tag_category = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not tag_category:
|
|
|
|
|
+ raise ValueError(f"分类ID {category_id} 不存在")
|
|
|
|
|
+
|
|
|
|
|
+ path_changed = tag_data.parent_id is not None and tag_data.parent_id != tag_category.parent_id
|
|
|
|
|
+
|
|
|
|
|
+ if path_changed:
|
|
|
|
|
+ if tag_data.parent_id != 0:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.id == tag_data.parent_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ parent = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not parent:
|
|
|
|
|
+ raise ValueError(f"新的父级分类ID {tag_data.parent_id} 不存在")
|
|
|
|
|
+
|
|
|
|
|
+ if self._is_ancestor(tag_category, parent):
|
|
|
|
|
+ raise ValueError("不能将分类移动到其子级分类下")
|
|
|
|
|
+
|
|
|
|
|
+ tag_category.path = f"{parent.path}{tag_data.parent_id}/"
|
|
|
|
|
+ tag_category.level = parent.level + 1
|
|
|
|
|
+ else:
|
|
|
|
|
+ tag_category.path = "/"
|
|
|
|
|
+ tag_category.level = 1
|
|
|
|
|
+
|
|
|
|
|
+ tag_category.parent_id = tag_data.parent_id
|
|
|
|
|
+
|
|
|
|
|
+ await self._update_descendants_path(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ if tag_data.name is not None:
|
|
|
|
|
+ tag_category.name = tag_data.name
|
|
|
|
|
+ if tag_data.sort_no is not None:
|
|
|
|
|
+ tag_category.sort_no = tag_data.sort_no
|
|
|
|
|
+ if tag_data.status is not None:
|
|
|
|
|
+ tag_category.status = tag_data.status
|
|
|
|
|
+
|
|
|
|
|
+ tag_category.updated_by = updated_by
|
|
|
|
|
+ tag_category.updated_at = datetime.now(timezone.utc)
|
|
|
|
|
+
|
|
|
|
|
+ await self.session.flush()
|
|
|
|
|
+ await self.session.refresh(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f"更新标签分类成功: {tag_category.id}")
|
|
|
|
|
+ return await self._enrich_with_user_names(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"更新标签分类失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def delete_tag_category(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_id: int,
|
|
|
|
|
+ soft_delete: bool = True
|
|
|
|
|
+ ) -> bool:
|
|
|
|
|
+ """删除标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(TagCategory.id == category_id)
|
|
|
|
|
+ )
|
|
|
|
|
+ tag_category = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not tag_category:
|
|
|
|
|
+ raise ValueError(f"分类ID {category_id} 不存在")
|
|
|
|
|
+
|
|
|
|
|
+ if soft_delete:
|
|
|
|
|
+ await self._soft_delete_with_children(category_id)
|
|
|
|
|
+ else:
|
|
|
|
|
+ await self.session.delete(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f"删除标签分类成功: {category_id}")
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"删除标签分类失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def get_category_tree(
|
|
|
|
|
+ self,
|
|
|
|
|
+ root_id: int = 0,
|
|
|
|
|
+ include_disabled: bool = False
|
|
|
|
|
+ ) -> List[TagCategoryTreeNode]:
|
|
|
|
|
+ """获取标签分类树"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ conditions = [TagCategory.is_deleted == 0]
|
|
|
|
|
+ if not include_disabled:
|
|
|
|
|
+ conditions.append(TagCategory.status == 1)
|
|
|
|
|
+
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(and_(*conditions))
|
|
|
|
|
+ .order_by(TagCategory.sort_no, TagCategory.id)
|
|
|
|
|
+ )
|
|
|
|
|
+ all_categories = result.scalars().all()
|
|
|
|
|
+
|
|
|
|
|
+ return self._build_tree(all_categories, root_id)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"获取分类树失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ @staticmethod
|
|
|
|
|
+ def _build_tree(categories: List[TagCategory], parent_id: int = 0) -> List[TagCategoryTreeNode]:
|
|
|
|
|
+ """递归构建树形结构"""
|
|
|
|
|
+ tree = []
|
|
|
|
|
+ for cat in categories:
|
|
|
|
|
+ if cat.parent_id == parent_id:
|
|
|
|
|
+ children = TagCategoryService._build_tree(categories, cat.id)
|
|
|
|
|
+ node = TagCategoryTreeNode(
|
|
|
|
|
+ id=cat.id,
|
|
|
|
|
+ parent_id=cat.parent_id,
|
|
|
|
|
+ name=cat.name,
|
|
|
|
|
+ path=cat.path,
|
|
|
|
|
+ level=cat.level,
|
|
|
|
|
+ sort_no=cat.sort_no,
|
|
|
|
|
+ status=cat.status,
|
|
|
|
|
+ children=children if children else None
|
|
|
|
|
+ )
|
|
|
|
|
+ tree.append(node)
|
|
|
|
|
+ return tree
|
|
|
|
|
+
|
|
|
|
|
+ async def get_breadcrumb(self, category_id: int) -> List[Dict[str, Any]]:
|
|
|
|
|
+ """获取分类的面包屑路径"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.id == category_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ category = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not category:
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ path_ids = [int(x) for x in category.path.strip('/').split('/') if x]
|
|
|
|
|
+ path_ids.append(category.id)
|
|
|
|
|
+
|
|
|
|
|
+ if not path_ids:
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(TagCategory.id.in_(path_ids))
|
|
|
|
|
+ .order_by(TagCategory.level)
|
|
|
|
|
+ )
|
|
|
|
|
+ ancestors = result.scalars().all()
|
|
|
|
|
+
|
|
|
|
|
+ return [{"id": cat.id, "name": cat.name} for cat in ancestors]
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"获取面包屑路径失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ @staticmethod
|
|
|
|
|
+ def _is_ancestor(ancestor: TagCategory, descendant: TagCategory) -> bool:
|
|
|
|
|
+ """检查ancestor是否是descendant的祖先"""
|
|
|
|
|
+ path_ids = [int(x) for x in descendant.path.strip('/').split('/') if x]
|
|
|
|
|
+ return ancestor.id in path_ids
|
|
|
|
|
+
|
|
|
|
|
+ async def _update_descendants_path(self, parent: TagCategory) -> None:
|
|
|
|
|
+ """递归更新所有子分类的path和level"""
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(TagCategory.parent_id == parent.id)
|
|
|
|
|
+ )
|
|
|
|
|
+ children = result.scalars().all()
|
|
|
|
|
+
|
|
|
|
|
+ for child in children:
|
|
|
|
|
+ child.path = f"{parent.path}{parent.id}/"
|
|
|
|
|
+ child.level = parent.level + 1
|
|
|
|
|
+ child.updated_at = datetime.now(timezone.utc)
|
|
|
|
|
+
|
|
|
|
|
+ await self._update_descendants_path(child)
|
|
|
|
|
+
|
|
|
|
|
+ async def _soft_delete_with_children(self, category_id: int) -> None:
|
|
|
|
|
+ """软删除分类及其所有子分类"""
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(TagCategory.id == category_id)
|
|
|
|
|
+ )
|
|
|
|
|
+ category = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not category:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ path_prefix = f"{category.path}{category_id}/"
|
|
|
|
|
+
|
|
|
|
|
+ query = update(TagCategory).where(
|
|
|
|
|
+ or_(
|
|
|
|
|
+ TagCategory.id == category_id,
|
|
|
|
|
+ TagCategory.path.like(f"{path_prefix}%")
|
|
|
|
|
+ )
|
|
|
|
|
+ ).values(
|
|
|
|
|
+ is_deleted=1,
|
|
|
|
|
+ updated_at=datetime.now(timezone.utc)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ await self.session.execute(query)
|
|
|
|
|
+
|
|
|
|
|
+ async def batch_delete_tag_categories(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_ids: List[int],
|
|
|
|
|
+ soft_delete: bool = True
|
|
|
|
|
+ ) -> Tuple[int, str]:
|
|
|
|
|
+ """批量删除标签分类"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ if not category_ids:
|
|
|
|
|
+ return 0, "分类ID列表为空"
|
|
|
|
|
+
|
|
|
|
|
+ success_count = 0
|
|
|
|
|
+
|
|
|
|
|
+ for cat_id in category_ids:
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(TagCategory.id == cat_id)
|
|
|
|
|
+ )
|
|
|
|
|
+ tag_category = result.scalar_one_or_none()
|
|
|
|
|
+
|
|
|
|
|
+ if not tag_category:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ if soft_delete:
|
|
|
|
|
+ await self._soft_delete_with_children(cat_id)
|
|
|
|
|
+ else:
|
|
|
|
|
+ await self.session.delete(tag_category)
|
|
|
|
|
+
|
|
|
|
|
+ success_count += 1
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.warning(f"删除分类 {cat_id} 失败: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ message = f"成功删除 {success_count}/{len(category_ids)} 条分类"
|
|
|
|
|
+ return success_count, message
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"批量删除分类失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def move_tag_category(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_id: int,
|
|
|
|
|
+ target_parent_id: int,
|
|
|
|
|
+ updated_by: Optional[str] = None
|
|
|
|
|
+ ) -> TagCategoryResponse:
|
|
|
|
|
+ """移动分类到新的父分类"""
|
|
|
|
|
+ update_data = TagCategoryUpdate(parent_id=target_parent_id)
|
|
|
|
|
+ return await self.update_tag_category(category_id, update_data, updated_by)
|
|
|
|
|
+
|
|
|
|
|
+ async def reorder_siblings(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_id: int,
|
|
|
|
|
+ new_sort_no: int,
|
|
|
|
|
+ updated_by: Optional[str] = None
|
|
|
|
|
+ ) -> TagCategoryResponse:
|
|
|
|
|
+ """重新排序分类"""
|
|
|
|
|
+ update_data = TagCategoryUpdate(sort_no=new_sort_no)
|
|
|
|
|
+ return await self.update_tag_category(category_id, update_data, updated_by)
|
|
|
|
|
+
|
|
|
|
|
+ async def get_children_count(
|
|
|
|
|
+ self,
|
|
|
|
|
+ category_id: int,
|
|
|
|
|
+ recursive: bool = False
|
|
|
|
|
+ ) -> int:
|
|
|
|
|
+ """获取分类的子分类数量"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ if recursive:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.path.like(f"/%{category_id}/%"),
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ result = await self.session.execute(
|
|
|
|
|
+ select(TagCategory).where(
|
|
|
|
|
+ and_(
|
|
|
|
|
+ TagCategory.parent_id == category_id,
|
|
|
|
|
+ TagCategory.is_deleted == 0
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ categories = result.scalars().all()
|
|
|
|
|
+ return len(categories)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"获取子分类数量失败: {str(e)}")
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ async def _enrich_with_user_names(self, category: TagCategory) -> TagCategoryResponse:
|
|
|
|
|
+ """为单个分类对象填充用户名"""
|
|
|
|
|
+ response = TagCategoryResponse.model_validate(category)
|
|
|
|
|
+
|
|
|
|
|
+ if category.created_by:
|
|
|
|
|
+ user = await self.user_service.get_user_by_id(category.created_by)
|
|
|
|
|
+ response.created_by_name = user.username if user else None
|
|
|
|
|
+
|
|
|
|
|
+ if category.updated_by:
|
|
|
|
|
+ user = await self.user_service.get_user_by_id(category.updated_by)
|
|
|
|
|
+ response.updated_by_name = user.username if user else None
|
|
|
|
|
+
|
|
|
|
|
+ return response
|
|
|
|
|
+
|
|
|
|
|
+ async def _enrich_list_with_user_names(self, categories: List[TagCategory]) -> List[TagCategoryResponse]:
|
|
|
|
|
+ """为分类列表批量填充用户名"""
|
|
|
|
|
+ if not categories:
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ # 收集所有需要查询的用户ID
|
|
|
|
|
+ user_ids = set()
|
|
|
|
|
+ for cat in categories:
|
|
|
|
|
+ if cat.created_by:
|
|
|
|
|
+ user_ids.add(cat.created_by)
|
|
|
|
|
+ if cat.updated_by:
|
|
|
|
|
+ user_ids.add(cat.updated_by)
|
|
|
|
|
+
|
|
|
|
|
+ # 批量查询用户名
|
|
|
|
|
+ user_map = {}
|
|
|
|
|
+ if user_ids:
|
|
|
|
|
+ for user_id in user_ids:
|
|
|
|
|
+ user = await self.user_service.get_user_by_id(user_id)
|
|
|
|
|
+ if user:
|
|
|
|
|
+ user_map[user_id] = user.username
|
|
|
|
|
+
|
|
|
|
|
+ # 构建响应列表
|
|
|
|
|
+ result = []
|
|
|
|
|
+ for cat in categories:
|
|
|
|
|
+ response = TagCategoryResponse.model_validate(cat)
|
|
|
|
|
+ response.created_by_name = user_map.get(cat.created_by) if cat.created_by else None
|
|
|
|
|
+ response.updated_by_name = user_map.get(cat.updated_by) if cat.updated_by else None
|
|
|
|
|
+ result.append(response)
|
|
|
|
|
+
|
|
|
|
|
+ return result
|