Przeglądaj źródła

feat(oa): auto-pass approval when assignee is initiator

Extract approve core and hook after creating approval tasks when the advanced setting is enabled.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 miesięcy temu
rodzic
commit
8bd45561b7
1 zmienionych plików z 130 dodań i 54 usunięć
  1. 130 54
      backend/app/services/oa_engine.py

+ 130 - 54
backend/app/services/oa_engine.py

@@ -30,6 +30,9 @@ class OaEngineError(ValueError):
     """引擎业务错误"""
 
 
+_AUTO_PASS_COMMENT = "系统自动通过"
+
+
 def _loads(text: Optional[str], default: Any = None) -> Any:
     if text is None or text == "":
         return default
@@ -1501,6 +1504,13 @@ def advance_from(
                 continue
             if not tasks:
                 raise OaEngineError("审批节点未生成任务")
+            _maybe_auto_pass_initiator(
+                db,
+                inst=inst,
+                ver=ver,
+                tasks=tasks,
+                user_id=user_id,
+            )
             return
         # 未知类型跳过子节点
         cur = next_node(cur)
@@ -1773,6 +1783,116 @@ def list_tasks(
     return _enrich_list_rows(db, tenant_no=tenant_no, rows=rows, instances=insts)
 
 
+def _approve_task_core(
+    db: Session,
+    *,
+    task: OaTask,
+    inst: OaInstance,
+    ver: OaAppVersion,
+    user_id: str,
+    comment: Optional[str],
+) -> bool:
+    """将 pending 审批任务置为 approved 并按或签/会签决定是否推进。
+
+    返回 True 表示已调用 advance_from 推进;False 表示会签未齐、未推进。
+    调用方负责 commit(若需要)。
+    """
+    now = datetime.now()
+    task.comment_text = (comment or "").strip() or None
+    task.status = "approved"
+    task.update_by = user_id
+    task.update_time = now
+    db.add(task)
+    # Session 默认 autoflush=False:不先 flush 时,后续按 status=pending 的查询仍会命中本任务,
+    # 或签「取消同节点其余待办」会把本人已同意覆盖成 done。
+    db.flush()
+
+    sign_mode = (task.sign_mode or "or").strip() or "or"
+    pending_same = (
+        db.query(OaTask)
+        .filter(
+            OaTask.instance_id == inst.id,
+            OaTask.node_key == task.node_key,
+            OaTask.task_type == "approval",
+            OaTask.id != task.id,
+            OaTask.status == "pending",
+            OaTask.del_flag == 0,
+        )
+        .count()
+    )
+    if sign_mode == "and" and pending_same > 0:
+        return False
+
+    if sign_mode != "and":
+        for s in (
+            db.query(OaTask)
+            .filter(
+                OaTask.instance_id == inst.id,
+                OaTask.node_key == task.node_key,
+                OaTask.id != task.id,
+                OaTask.status == "pending",
+                OaTask.del_flag == 0,
+            )
+            .all()
+        ):
+            s.status = "done"
+            s.update_by = user_id
+            s.update_time = now
+            db.add(s)
+
+    root = _flow_root(_loads(ver.flow_tree_json, {}))
+    node = _find_node(root, task.node_key or "")
+    if not node:
+        raise OaEngineError("找不到对应流程节点")
+    advance_from(
+        db,
+        inst=inst,
+        ver=ver,
+        from_node=node,
+        user_id=user_id,
+        initiator_select_by_node=_load_initiator_select_by_node(inst),
+    )
+    return True
+
+
+def _maybe_auto_pass_initiator(
+    db: Session,
+    *,
+    inst: OaInstance,
+    ver: OaAppVersion,
+    tasks: List[OaTask],
+    user_id: Optional[str],
+) -> None:
+    _ = user_id
+    advanced = _advanced(ver)
+    if not advanced.get("autoPassWhenApproverIsInitiator"):
+        return
+    if inst.status != "running":
+        return
+    initiator = (inst.initiator_user_id or "").strip()
+    if not initiator:
+        return
+    self_task = None
+    for t in tasks:
+        if (
+            t.task_type == "approval"
+            and t.status == "pending"
+            and (t.assignee_user_id or "") == initiator
+        ):
+            self_task = t
+            break
+    if not self_task:
+        return
+    _approve_task_core(
+        db,
+        task=self_task,
+        inst=inst,
+        ver=ver,
+        user_id=initiator,
+        comment=_AUTO_PASS_COMMENT,
+    )
+
+
 def complete_task(
     db: Session,
     task_id: str,
@@ -1814,18 +1934,14 @@ def complete_task(
     if advanced.get("commentRequired") and not (comment or "").strip():
         raise OaEngineError("请填写审批意见")
 
-    now = datetime.now()
-    task.comment_text = (comment or "").strip() or None
-    task.status = "approved" if action == "approve" else "rejected"
-    task.update_by = user_id
-    task.update_time = now
-    db.add(task)
-    # Session 默认 autoflush=False:不先 flush 时,后续按 status=pending 的查询仍会命中本任务,
-    # 或签「取消同节点其余待办」会把本人已同意/已拒绝覆盖成 done,导致「已处理」列表查不到。
-    db.flush()
-
     if action == "reject":
-        # 同节点其他待办作废(排除当前任务)
+        now = datetime.now()
+        task.comment_text = (comment or "").strip() or None
+        task.status = "rejected"
+        task.update_by = user_id
+        task.update_time = now
+        db.add(task)
+        db.flush()
         siblings = (
             db.query(OaTask)
             .filter(
@@ -1849,53 +1965,13 @@ def complete_task(
         db.commit()
         return get_instance_detail(db, inst.id, tenant_no=tenant_no)
 
-    # 或签:一人通过即可;会签:全部通过
-    sign_mode = (task.sign_mode or "or").strip() or "or"
-    pending_same = (
-        db.query(OaTask)
-        .filter(
-            OaTask.instance_id == inst.id,
-            OaTask.node_key == task.node_key,
-            OaTask.task_type == "approval",
-            OaTask.id != task.id,
-            OaTask.status == "pending",
-            OaTask.del_flag == 0,
-        )
-        .count()
-    )
-    if sign_mode == "and" and pending_same > 0:
-        db.commit()
-        return get_instance_detail(db, inst.id, tenant_no=tenant_no)
-
-    # 或签:取消同节点其余待办(排除当前任务)
-    if sign_mode != "and":
-        for s in (
-            db.query(OaTask)
-            .filter(
-                OaTask.instance_id == inst.id,
-                OaTask.node_key == task.node_key,
-                OaTask.id != task.id,
-                OaTask.status == "pending",
-                OaTask.del_flag == 0,
-            )
-            .all()
-        ):
-            s.status = "done"
-            s.update_by = user_id
-            s.update_time = now
-            db.add(s)
-
-    root = _flow_root(_loads(ver.flow_tree_json, {}))
-    node = _find_node(root, task.node_key or "")
-    if not node:
-        raise OaEngineError("找不到对应流程节点")
-    advance_from(
+    _approve_task_core(
         db,
+        task=task,
         inst=inst,
         ver=ver,
-        from_node=node,
         user_id=user_id,
-        initiator_select_by_node=_load_initiator_select_by_node(inst),
+        comment=comment,
     )
     db.commit()
     return get_instance_detail(db, inst.id, tenant_no=tenant_no)