service.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from typing import Optional, List, Dict, Any
  2. from alipay.aop.api.domain.DepartmentInfoDTO import DepartmentInfoDTO
  3. from alipay.aop.api.domain.AlipayCommerceEcDepartmentCreateModel import AlipayCommerceEcDepartmentCreateModel
  4. from alipay.aop.api.domain.AlipayCommerceEcDepartmentDeleteModel import AlipayCommerceEcDepartmentDeleteModel
  5. from alipay.aop.api.domain.AlipayCommerceEcDepartmentInfoQueryModel import AlipayCommerceEcDepartmentInfoQueryModel
  6. from alipay.aop.api.response.AlipayCommerceEcDepartmentCreateResponse import AlipayCommerceEcDepartmentCreateResponse
  7. from alipay.aop.api.response.AlipayCommerceEcDepartmentDeleteResponse import AlipayCommerceEcDepartmentDeleteResponse
  8. from alipay.aop.api.response.AlipayCommerceEcDepartmentInfoQueryResponse import AlipayCommerceEcDepartmentInfoQueryResponse
  9. from alipay.aop.api.response.AlipayCommerceEcDepartmentSublistQueryResponse import AlipayCommerceEcDepartmentSublistQueryResponse
  10. from alipay.aop.api.request.AlipayCommerceEcDepartmentCreateRequest import AlipayCommerceEcDepartmentCreateRequest
  11. from alipay.aop.api.request.AlipayCommerceEcDepartmentDeleteRequest import AlipayCommerceEcDepartmentDeleteRequest
  12. from alipay.aop.api.request.AlipayCommerceEcDepartmentInfoModifyRequest import AlipayCommerceEcDepartmentInfoModifyRequest
  13. from alipay.aop.api.request.AlipayCommerceEcDepartmentInfoQueryRequest import AlipayCommerceEcDepartmentInfoQueryRequest
  14. from alipay.aop.api.request.AlipayCommerceEcDepartmentSublistQueryRequest import AlipayCommerceEcDepartmentSublistQueryRequest
  15. from app.api.v1.module_system.auth.schema import AuthSchema
  16. from app.core.alipay import AlipayClient
  17. from app.core.logger import log
  18. from app.core.exceptions import CustomException
  19. from .crud import DepartmentCRUD
  20. from .schema import (
  21. DepartmentCreateSchema,
  22. DepartmentUpdateSchema,
  23. DepartmentDetailOutSchema,
  24. DepartmentOperationOutSchema,
  25. DepartmentTreeOutSchema,
  26. DepartmentListOutSchema,
  27. DepartmentOutSchema
  28. )
  29. class DepartmentService:
  30. """部门服务"""
  31. @classmethod
  32. async def create_department_service(
  33. cls, auth: AuthSchema, data: DepartmentCreateSchema
  34. ) -> DepartmentOperationOutSchema:
  35. """创建部门"""
  36. # 调用支付宝接口创建部门
  37. department_create_model = AlipayCommerceEcDepartmentCreateModel()
  38. department_create_model.enterprise_id = data.enterprise_id
  39. department_create_model.department_name = data.department_name
  40. if data.department_code:
  41. department_create_model.department_code = data.department_code
  42. if data.parent_department_id:
  43. department_create_model.parent_department_id = data.parent_department_id
  44. client = AlipayClient.get_client()
  45. request = AlipayCommerceEcDepartmentCreateRequest()
  46. request.biz_model = department_create_model
  47. response = client.execute(request)
  48. if not response:
  49. raise CustomException(msg="创建部门失败: 无响应")
  50. result = AlipayCommerceEcDepartmentCreateResponse()
  51. result.parse_response_content(response)
  52. if not result.is_success():
  53. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  54. raise CustomException(msg=f"创建部门失败: {result.sub_code or result.msg or result.code}")
  55. # 保存到数据库
  56. crud = DepartmentCRUD(auth)
  57. department_data = {
  58. "department_id": result.department_id or "",
  59. "department_name": data.department_name,
  60. "department_code": data.department_code,
  61. "parent_department_id": data.parent_department_id,
  62. "enterprise_id": data.enterprise_id,
  63. "sort_order": data.sort_order,
  64. "leader_employee_id": data.leader_employee_id,
  65. "leader_employee_name": data.leader_employee_name
  66. }
  67. await crud.create(department_data)
  68. return DepartmentOperationOutSchema(
  69. department_id=result.department_id or "",
  70. department_name=data.department_name
  71. )
  72. @classmethod
  73. async def delete_department_service(
  74. cls, auth: AuthSchema, department_id: str, enterprise_id: str
  75. ) -> DepartmentOperationOutSchema:
  76. """删除部门(停用联动:从所有引用该部门的费控制度中移除)"""
  77. # 停用联动:从所有引用该部门的费控制度中移除
  78. try:
  79. from app.plugin.module_payment.expense.institution.scope_sync import remove_department_from_institution_scopes
  80. await remove_department_from_institution_scopes(
  81. auth=auth, enterprise_id=enterprise_id, department_id=department_id
  82. )
  83. except Exception as e:
  84. log.warning(f"从制度移除停用部门失败(不影响主体操作): {e}")
  85. return DepartmentOperationOutSchema(
  86. department_id=department_id,
  87. department_name=""
  88. )
  89. @classmethod
  90. async def update_department_service(
  91. cls, auth: AuthSchema, department_id: str, enterprise_id: str, data: DepartmentUpdateSchema
  92. ) -> DepartmentOperationOutSchema:
  93. """更新部门"""
  94. return DepartmentOperationOutSchema(
  95. department_id=department_id,
  96. department_name=""
  97. )
  98. @classmethod
  99. async def get_department_service(
  100. cls, auth: AuthSchema, department_id: str, enterprise_id: str
  101. ) -> DepartmentDetailOutSchema:
  102. """查询部门详情 (alipay.commerce.ec.department.info.query)"""
  103. # 调用支付宝接口查询部门详情
  104. model = AlipayCommerceEcDepartmentInfoQueryModel()
  105. model.enterprise_id = enterprise_id
  106. model.department_id = department_id
  107. request = AlipayCommerceEcDepartmentInfoQueryRequest()
  108. request.biz_model = model
  109. client = AlipayClient.get_client()
  110. response = client.execute(request)
  111. if not response:
  112. raise CustomException(msg="查询部门详情失败: 无响应")
  113. result = AlipayCommerceEcDepartmentInfoQueryResponse()
  114. result.parse_response_content(response)
  115. if not result.is_success():
  116. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  117. raise CustomException(msg=f"查询部门详情失败: {result.sub_code or result.msg or result.code}")
  118. # 解析部门信息
  119. department_info = result.department_info
  120. if not department_info:
  121. raise CustomException(msg="部门信息不存在")
  122. # 转换为响应模型
  123. department_out = DepartmentOutSchema(
  124. department_id=department_info.department_id or "",
  125. department_name=department_info.department_name or "",
  126. department_code=department_info.department_code,
  127. parent_department_id=department_info.parent_department_id,
  128. enterprise_id=enterprise_id,
  129. status="NORMAL",
  130. )
  131. return DepartmentDetailOutSchema(
  132. department=department_out,
  133. sub_departments=[]
  134. )
  135. @classmethod
  136. async def get_sub_departments_service(
  137. cls, auth: AuthSchema, parent_department_id: str, enterprise_id: str
  138. ) -> List[str]:
  139. """查询子部门列表"""
  140. return []
  141. @classmethod
  142. async def list_service(
  143. cls,
  144. auth: AuthSchema,
  145. page_no: int = 1,
  146. page_size: int = 20,
  147. search: dict | None = None,
  148. ) -> dict:
  149. """分页查询部门列表"""
  150. log.info(f"查询部门列表: {page_no}, {page_size}, {search}")
  151. crud = DepartmentCRUD(auth)
  152. offset = (page_no - 1) * page_size
  153. return await crud.page(
  154. offset=offset,
  155. limit=page_size,
  156. order_by=[{"id": "desc"}],
  157. search=search or {},
  158. out_schema=DepartmentListOutSchema,
  159. )
  160. @classmethod
  161. async def get_department_tree_service(
  162. cls, auth: AuthSchema, enterprise_id: str
  163. ) -> List[DepartmentTreeOutSchema]:
  164. """获取部门树形结构"""
  165. crud = DepartmentCRUD(auth)
  166. # 查询指定企业下的所有部门
  167. departments = await crud.list({"enterprise_id": enterprise_id})
  168. # 转换为字典,方便查找
  169. department_dict = {dept.department_id: DepartmentTreeOutSchema.model_validate(dept) for dept in departments}
  170. # 构建树形结构
  171. tree: List[DepartmentTreeOutSchema] = []
  172. for dept in department_dict.values():
  173. # 找到父部门
  174. parent_id = dept.parent_department_id
  175. if not parent_id or parent_id == "-1" or parent_id not in department_dict:
  176. tree.append(dept)
  177. else:
  178. # 添加到父部门的子节点
  179. department_dict[parent_id].children.append(dept)
  180. # 排序子节点
  181. def sort_children(n: DepartmentTreeOutSchema):
  182. if n.children:
  183. n.children.sort(key=lambda x: (x.sort_order or 0, x.department_name))
  184. for child in n.children:
  185. sort_children(child)
  186. for node in tree:
  187. sort_children(node)
  188. return tree
  189. @classmethod
  190. async def get_all_departments(
  191. cls, auth: AuthSchema, enterprise_id: str
  192. ) -> List[DepartmentOutSchema]:
  193. """获取所有部门列表(不分页)"""
  194. crud = DepartmentCRUD(auth)
  195. departments = await crud.list({"enterprise_id": enterprise_id})
  196. return [DepartmentOutSchema.model_validate(dept) for dept in departments]