service.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from typing import Any
  2. from app.api.v1.module_system.auth.schema import AuthSchema
  3. from app.core.exceptions import CustomException
  4. from app.core.logger import log
  5. from app.utils.snowflake import get_snowflake_id_str
  6. from .crud import DepartmentCRUD
  7. from .enums import DepartmentStatusEnum
  8. from .schema import (
  9. DepartmentCreateSchema,
  10. DepartmentOutSchema,
  11. DepartmentTreeSchema,
  12. DepartmentUpdateSchema,
  13. )
  14. class DepartmentService:
  15. """部门服务层"""
  16. @classmethod
  17. async def create_service(
  18. cls, auth: AuthSchema, data: DepartmentCreateSchema
  19. ) -> DepartmentOutSchema:
  20. """创建部门"""
  21. crud = DepartmentCRUD(auth)
  22. department_data = data.model_dump(exclude_none=True)
  23. department_data["department_id"] = get_snowflake_id_str(auth.tenant_id)
  24. department_data["status"] = DepartmentStatusEnum.DEPARTMENT_ACTIVE.value
  25. if department_data.get("parent_id"):
  26. parent = await crud.get_by_department_id(department_data["parent_id"])
  27. if not parent:
  28. raise CustomException(msg="父部门不存在")
  29. department_data["enterprise_id"] = parent.enterprise_id
  30. else:
  31. if hasattr(auth, "enterprise_id") and auth.enterprise_id:
  32. department_data["enterprise_id"] = auth.enterprise_id
  33. department = await crud.create(department_data)
  34. if not department:
  35. raise CustomException(msg="创建部门失败")
  36. return DepartmentOutSchema.model_validate(department)
  37. @classmethod
  38. async def update_service(
  39. cls, auth: AuthSchema, department_id: str, data: DepartmentUpdateSchema
  40. ) -> DepartmentOutSchema:
  41. """更新部门"""
  42. crud = DepartmentCRUD(auth)
  43. existing = await crud.get_by_department_id(department_id)
  44. if not existing:
  45. raise CustomException(msg="部门不存在")
  46. update_data = data.model_dump(exclude_none=True)
  47. if update_data.get("parent_id"):
  48. if update_data["parent_id"] == department_id:
  49. raise CustomException(msg="不能将自己设为父部门")
  50. parent = await crud.get_by_department_id(update_data["parent_id"])
  51. if not parent:
  52. raise CustomException(msg="父部门不存在")
  53. updated = await crud.update_by_department_id(department_id, update_data)
  54. if not updated:
  55. raise CustomException(msg="更新部门失败")
  56. return DepartmentOutSchema.model_validate(updated)
  57. @classmethod
  58. async def delete_service(
  59. cls, auth: AuthSchema, department_id: str
  60. ) -> None:
  61. """删除部门"""
  62. crud = DepartmentCRUD(auth)
  63. existing = await crud.get_by_department_id(department_id)
  64. if not existing:
  65. raise CustomException(msg="部门不存在")
  66. if await crud.has_children(department_id):
  67. raise CustomException(msg="请先删除子部门")
  68. await crud.delete(department_id)
  69. @classmethod
  70. async def detail_service(
  71. cls, auth: AuthSchema, department_id: str
  72. ) -> DepartmentOutSchema:
  73. """查询部门详情"""
  74. crud = DepartmentCRUD(auth)
  75. department = await crud.get_by_department_id(department_id)
  76. if not department:
  77. raise CustomException(msg="部门不存在")
  78. return DepartmentOutSchema.model_validate(department)
  79. @classmethod
  80. async def list_service(
  81. cls,
  82. auth: AuthSchema,
  83. page_no: int = 1,
  84. page_size: int = 100,
  85. search: dict | None = None,
  86. ) -> dict:
  87. """查询部门列表(平铺)"""
  88. crud = DepartmentCRUD(auth)
  89. offset = (page_no - 1) * page_size
  90. return await crud.page(
  91. offset=offset,
  92. limit=page_size,
  93. order_by=[{"sort_order": "desc"}, {"id": "desc"}],
  94. search=search or {},
  95. out_schema=DepartmentOutSchema,
  96. )
  97. @classmethod
  98. async def tree_service(cls, auth: AuthSchema) -> list[DepartmentTreeSchema]:
  99. """获取部门树形结构"""
  100. crud = DepartmentCRUD(auth)
  101. search = {}
  102. if hasattr(auth, "enterprise_id") and auth.enterprise_id:
  103. search["enterprise_id"] = auth.enterprise_id
  104. result = await crud.page(
  105. offset=0,
  106. limit=1000,
  107. order_by=[{"sort_order": "desc"}, {"id": "desc"}],
  108. search=search,
  109. out_schema=DepartmentOutSchema,
  110. )
  111. items = result.get("items", [])
  112. return cls._build_tree(items)
  113. @classmethod
  114. def _build_tree(
  115. cls, departments: list[dict[str, Any]]
  116. ) -> list[DepartmentTreeSchema]:
  117. """构建部门树形结构"""
  118. department_map = {}
  119. roots = []
  120. for dept in departments:
  121. dept_id = dept.get("department_id")
  122. if dept_id:
  123. department_map[dept_id] = DepartmentTreeSchema(
  124. department_id=dept.get("department_id"),
  125. name=dept["name"],
  126. code=dept.get("code"),
  127. parent_id=dept.get("parent_id"),
  128. leader_employee_id=dept.get("leader_employee_id"),
  129. sort_order=dept.get("sort_order", 0),
  130. status=dept["status"],
  131. children=[],
  132. )
  133. for dept in departments:
  134. dept_id = dept.get("department_id")
  135. if dept_id and dept_id in department_map:
  136. parent_id = dept.get("parent_id")
  137. if parent_id and parent_id in department_map:
  138. department_map[parent_id].children.append(
  139. department_map[dept_id]
  140. )
  141. else:
  142. roots.append(department_map[dept_id])
  143. return roots
  144. @classmethod
  145. async def children_service(
  146. cls, auth: AuthSchema, department_id: str
  147. ) -> list[DepartmentOutSchema]:
  148. """获取子部门列表"""
  149. crud = DepartmentCRUD(auth)
  150. children = await crud.get_children(department_id)
  151. return [DepartmentOutSchema.model_validate(child) for child in children]
  152. @classmethod
  153. async def move_service(
  154. cls, auth: AuthSchema, department_id: str, parent_id: str | None
  155. ) -> DepartmentOutSchema:
  156. """移动部门(变更父级)"""
  157. if parent_id == department_id:
  158. raise CustomException(msg="不能将自己设为父部门")
  159. if parent_id:
  160. crud = DepartmentCRUD(auth)
  161. parent = await crud.get_by_department_id(parent_id)
  162. if not parent:
  163. raise CustomException(msg="目标父部门不存在")
  164. descendants = await cls._get_all_descendants(department_id)
  165. if parent_id in descendants:
  166. raise CustomException(msg="不能移动到子部门下方")
  167. return await cls.update_service(
  168. auth, department_id, DepartmentUpdateSchema(parent_id=parent_id)
  169. )
  170. @classmethod
  171. async def _get_all_descendants(
  172. cls, department_id: str, crud: DepartmentCRUD | None = None
  173. ) -> set[str]:
  174. """获取部门所有后代ID"""
  175. if crud is None:
  176. from app.api.v1.module_system.auth.schema import AuthSchema
  177. from app.core.request import get_current_auth
  178. auth = await get_current_auth()
  179. crud = DepartmentCRUD(auth)
  180. descendants = set()
  181. children = await crud.get_children(department_id)
  182. for child in children:
  183. if child.department_id:
  184. descendants.add(child.department_id)
  185. child_descendants = await cls._get_all_descendants(
  186. child.department_id, crud
  187. )
  188. descendants.update(child_descendants)
  189. return descendants
  190. @classmethod
  191. async def options_service(cls, auth: AuthSchema) -> list[dict]:
  192. """获取部门选择选项"""
  193. crud = DepartmentCRUD(auth)
  194. search = {}
  195. if hasattr(auth, "enterprise_id") and auth.enterprise_id:
  196. search["enterprise_id"] = auth.enterprise_id
  197. result = await crud.page(
  198. offset=0,
  199. limit=1000,
  200. order_by=[{"sort_order": "desc"}, {"id": "desc"}],
  201. search=search,
  202. out_schema=DepartmentOutSchema,
  203. )
  204. items = result.get("items", [])
  205. return [
  206. {
  207. "value": item.department_id,
  208. "label": item.name,
  209. "disabled": False
  210. }
  211. for item in items if item.department_id
  212. ]