workflow.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. "vehicle_recharge": {
  19. "wf_name": "ETC/油卡充值审批流程",
  20. "remark": "充值申请默认审批流程",
  21. },
  22. }
  23. class WorkflowService:
  24. @staticmethod
  25. def _tenant_no(db: Session, user: BxUserContext) -> str:
  26. return get_tenant_no(db, user.to_tenant_user_dict())
  27. @staticmethod
  28. def get_by_id(
  29. db: Session,
  30. workflow_id: str,
  31. tenant_no: str,
  32. *,
  33. user: Optional[BxUserContext] = None,
  34. skip_scope: bool = False,
  35. ) -> Optional[BxWorkflowDef]:
  36. return BxCrudService.get_by_id(
  37. BxWorkflowDef, db, workflow_id, tenant_no, user=user, skip_scope=skip_scope
  38. )
  39. @staticmethod
  40. def get_paged(
  41. db: Session,
  42. tenant_no: str,
  43. page: int,
  44. page_size: int,
  45. *,
  46. user: Optional[BxUserContext] = None,
  47. ) -> Tuple[Sequence[BxWorkflowDef], int]:
  48. # 流程定义为租户配置主数据,列表不做 create_by 数据范围过滤
  49. return BxCrudService.get_paged(
  50. BxWorkflowDef, db, tenant_no, page, page_size, user=user, skip_scope=True
  51. )
  52. @staticmethod
  53. def create(
  54. db: Session,
  55. obj_in: dict,
  56. *,
  57. user: BxUserContext,
  58. ) -> BxWorkflowDef:
  59. tenant_no = WorkflowService._tenant_no(db, user)
  60. return BxCrudService.create(
  61. BxWorkflowDef,
  62. db,
  63. obj_in,
  64. tenant_no=tenant_no,
  65. user_id=user.id,
  66. dept_id=user.dept_id,
  67. )
  68. @staticmethod
  69. def update(
  70. db: Session,
  71. workflow_id: str,
  72. obj_in: dict,
  73. *,
  74. user: BxUserContext,
  75. ) -> Optional[BxWorkflowDef]:
  76. tenant_no = WorkflowService._tenant_no(db, user)
  77. return BxCrudService.update(
  78. BxWorkflowDef,
  79. db,
  80. workflow_id,
  81. tenant_no,
  82. obj_in,
  83. user_id=user.id,
  84. user=user,
  85. skip_scope=True,
  86. )
  87. @staticmethod
  88. def soft_delete(db: Session, workflow_id: str, *, user: BxUserContext) -> bool:
  89. tenant_no = WorkflowService._tenant_no(db, user)
  90. deleted = BxCrudService.soft_delete(
  91. BxWorkflowDef, db, workflow_id, tenant_no, user_id=user.id, user=user, skip_scope=True
  92. )
  93. if deleted:
  94. db.query(BxWorkflowNode).filter(
  95. BxWorkflowNode.wf_id == workflow_id,
  96. BxWorkflowNode.tenant_no == tenant_no,
  97. ).update({"del_flag": 1}, synchronize_session=False)
  98. db.flush()
  99. return deleted
  100. @staticmethod
  101. def get_by_biz_type(db: Session, biz_type: str, tenant_no: str) -> Optional[BxWorkflowDef]:
  102. return (
  103. db.query(BxWorkflowDef)
  104. .filter(
  105. BxWorkflowDef.biz_type == biz_type,
  106. BxWorkflowDef.tenant_no == tenant_no,
  107. BxWorkflowDef.del_flag == 0,
  108. )
  109. .first()
  110. )
  111. @staticmethod
  112. def ensure_by_biz_type(
  113. db: Session,
  114. biz_type: str,
  115. *,
  116. user: BxUserContext,
  117. ) -> Optional[BxWorkflowDef]:
  118. """按业务类型获取工作流;已知类型不存在时自动创建空定义(无节点)。"""
  119. tenant_no = WorkflowService._tenant_no(db, user)
  120. workflow = WorkflowService.get_by_biz_type(db, biz_type, tenant_no)
  121. if workflow:
  122. return workflow
  123. meta = _DEFAULT_WORKFLOWS.get(biz_type)
  124. if not meta:
  125. return None
  126. workflow = WorkflowService.create(
  127. db,
  128. {
  129. "biz_type": biz_type,
  130. "wf_name": meta["wf_name"],
  131. "remark": meta["remark"],
  132. "status": 1,
  133. },
  134. user=user,
  135. )
  136. db.commit()
  137. db.refresh(workflow)
  138. return workflow
  139. @staticmethod
  140. def save_nodes(
  141. db: Session,
  142. wf_id: str,
  143. nodes: List[dict],
  144. *,
  145. user: BxUserContext,
  146. ) -> List[BxWorkflowNode]:
  147. tenant_no = WorkflowService._tenant_no(db, user)
  148. workflow = WorkflowService.get_by_id(db, wf_id, tenant_no, user=user, skip_scope=True)
  149. if not workflow:
  150. raise ValueError("工作流不存在")
  151. disburse_count = 0
  152. for node_data in nodes:
  153. action = (
  154. node_data.get("action_type")
  155. or node_data.get("actionType")
  156. or "approve"
  157. )
  158. if action == "disburse":
  159. disburse_count += 1
  160. if disburse_count > 1:
  161. raise ValueError("发放登记节点至多一个")
  162. db.query(BxWorkflowNode).filter(
  163. BxWorkflowNode.wf_id == wf_id,
  164. BxWorkflowNode.tenant_no == tenant_no,
  165. ).delete(synchronize_session=False)
  166. created: List[BxWorkflowNode] = []
  167. for node_data in nodes:
  168. data = dict(node_data)
  169. data.pop("actionType", None)
  170. data["id"] = generate_uuid()
  171. data["wf_id"] = wf_id
  172. data["tenant_no"] = tenant_no
  173. data["create_by"] = user.id
  174. data["create_dept_id"] = user.dept_id or ""
  175. if data.get("approver_id") is not None:
  176. data["approver_id"] = str(data["approver_id"]).strip() or None
  177. if not data.get("action_type"):
  178. data["action_type"] = "approve"
  179. node = BxWorkflowNode(**data)
  180. db.add(node)
  181. created.append(node)
  182. db.flush()
  183. db.expire(workflow, ["nodes"])
  184. return created