workflow.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. from __future__ import annotations
  2. from typing import List, Optional, Sequence, Tuple
  3. from sqlalchemy.orm import Session
  4. from app.core.bx_auth import BxUserContext
  5. from app.core.tenant_context import get_tenant_no
  6. from app.core.utils import generate_uuid
  7. from app.models.bx_models import BxWorkflowDef, BxWorkflowNode
  8. from app.services.bx.base import BxCrudService
  9. _DEFAULT_WORKFLOWS = {
  10. "advance": {
  11. "wf_name": "备用金审批流程",
  12. "remark": "备用金申请默认审批流程",
  13. },
  14. "reimburse": {
  15. "wf_name": "报销单审批流程",
  16. "remark": "报销申请默认审批流程",
  17. },
  18. }
  19. class WorkflowService:
  20. @staticmethod
  21. def _tenant_no(db: Session, user: BxUserContext) -> str:
  22. return get_tenant_no(db, user.to_tenant_user_dict())
  23. @staticmethod
  24. def get_by_id(
  25. db: Session,
  26. workflow_id: str,
  27. tenant_no: str,
  28. *,
  29. user: Optional[BxUserContext] = None,
  30. skip_scope: bool = False,
  31. ) -> Optional[BxWorkflowDef]:
  32. return BxCrudService.get_by_id(
  33. BxWorkflowDef, db, workflow_id, tenant_no, user=user, skip_scope=skip_scope
  34. )
  35. @staticmethod
  36. def get_paged(
  37. db: Session,
  38. tenant_no: str,
  39. page: int,
  40. page_size: int,
  41. *,
  42. user: Optional[BxUserContext] = None,
  43. ) -> Tuple[Sequence[BxWorkflowDef], int]:
  44. # 流程定义为租户配置主数据,列表不做 create_by 数据范围过滤
  45. return BxCrudService.get_paged(
  46. BxWorkflowDef, db, tenant_no, page, page_size, user=user, skip_scope=True
  47. )
  48. @staticmethod
  49. def create(
  50. db: Session,
  51. obj_in: dict,
  52. *,
  53. user: BxUserContext,
  54. ) -> BxWorkflowDef:
  55. tenant_no = WorkflowService._tenant_no(db, user)
  56. return BxCrudService.create(
  57. BxWorkflowDef,
  58. db,
  59. obj_in,
  60. tenant_no=tenant_no,
  61. user_id=user.id,
  62. dept_id=user.dept_id,
  63. )
  64. @staticmethod
  65. def update(
  66. db: Session,
  67. workflow_id: str,
  68. obj_in: dict,
  69. *,
  70. user: BxUserContext,
  71. ) -> Optional[BxWorkflowDef]:
  72. tenant_no = WorkflowService._tenant_no(db, user)
  73. return BxCrudService.update(
  74. BxWorkflowDef,
  75. db,
  76. workflow_id,
  77. tenant_no,
  78. obj_in,
  79. user_id=user.id,
  80. user=user,
  81. skip_scope=True,
  82. )
  83. @staticmethod
  84. def soft_delete(db: Session, workflow_id: str, *, user: BxUserContext) -> bool:
  85. tenant_no = WorkflowService._tenant_no(db, user)
  86. deleted = BxCrudService.soft_delete(
  87. BxWorkflowDef, db, workflow_id, tenant_no, user_id=user.id, user=user, skip_scope=True
  88. )
  89. if deleted:
  90. db.query(BxWorkflowNode).filter(
  91. BxWorkflowNode.wf_id == workflow_id,
  92. BxWorkflowNode.tenant_no == tenant_no,
  93. ).update({"del_flag": 1}, synchronize_session=False)
  94. db.flush()
  95. return deleted
  96. @staticmethod
  97. def get_by_biz_type(db: Session, biz_type: str, tenant_no: str) -> Optional[BxWorkflowDef]:
  98. return (
  99. db.query(BxWorkflowDef)
  100. .filter(
  101. BxWorkflowDef.biz_type == biz_type,
  102. BxWorkflowDef.tenant_no == tenant_no,
  103. BxWorkflowDef.del_flag == 0,
  104. )
  105. .first()
  106. )
  107. @staticmethod
  108. def ensure_by_biz_type(
  109. db: Session,
  110. biz_type: str,
  111. *,
  112. user: BxUserContext,
  113. ) -> Optional[BxWorkflowDef]:
  114. """按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。"""
  115. tenant_no = WorkflowService._tenant_no(db, user)
  116. workflow = WorkflowService.get_by_biz_type(db, biz_type, tenant_no)
  117. if workflow:
  118. return workflow
  119. meta = _DEFAULT_WORKFLOWS.get(biz_type)
  120. if not meta:
  121. return None
  122. workflow = WorkflowService.create(
  123. db,
  124. {
  125. "biz_type": biz_type,
  126. "wf_name": meta["wf_name"],
  127. "remark": meta["remark"],
  128. "status": 1,
  129. },
  130. user=user,
  131. )
  132. db.commit()
  133. db.refresh(workflow)
  134. return workflow
  135. @staticmethod
  136. def save_nodes(
  137. db: Session,
  138. wf_id: str,
  139. nodes: List[dict],
  140. *,
  141. user: BxUserContext,
  142. ) -> List[BxWorkflowNode]:
  143. tenant_no = WorkflowService._tenant_no(db, user)
  144. workflow = WorkflowService.get_by_id(db, wf_id, tenant_no, user=user, skip_scope=True)
  145. if not workflow:
  146. raise ValueError("工作流不存在")
  147. db.query(BxWorkflowNode).filter(
  148. BxWorkflowNode.wf_id == wf_id,
  149. BxWorkflowNode.tenant_no == tenant_no,
  150. ).delete(synchronize_session=False)
  151. created: List[BxWorkflowNode] = []
  152. for node_data in nodes:
  153. data = dict(node_data)
  154. data["id"] = generate_uuid()
  155. data["wf_id"] = wf_id
  156. data["tenant_no"] = tenant_no
  157. data["create_by"] = user.id
  158. data["create_dept_id"] = user.dept_id or ""
  159. if data.get("approver_id") is not None:
  160. data["approver_id"] = str(data["approver_id"]).strip() or None
  161. node = BxWorkflowNode(**data)
  162. db.add(node)
  163. created.append(node)
  164. db.flush()
  165. db.expire(workflow, ["nodes"])
  166. return created