# 完整示例、Python 脚本与流程模式
## 1. 完整示例 — 请假审批流程
**流程描述:** 开始 → 员工提交 → 经理审批 → 排他网关(通过/拒绝) → HR审批 → 结束
### 1.1 节点定义
```xml
```
### 1.2 连线定义
```xml
```
### 1.3 布局计算
```
节点列表(按垂直顺序):
start: type=startEvent, y=30, h=36 → bottom=66
task_apply: type=userTask, y=106, h=60 → bottom=166
task_manager: type=userTask, y=206, h=60 → bottom=266
gateway_result: type=exclusiveGateway, y=306, h=50 → bottom=356
task_hr: type=userTask, y=396, h=60 → bottom=456
end: type=endEvent, y=496, h=36 → bottom=532
```
### 1.4 nodes 参数
```
id=task_apply###nodeName=员工提交申请@@@id=task_manager###nodeName=部门经理审批@@@id=task_hr###nodeName=HR审批@@@
```
## 2. Python 调用脚本模板
```python
import urllib.request
import urllib.parse
import json
import time
def create_bpm_process(api_base, token, process_name, process_type, bpmn_xml, nodes_str, tenant_id="1"):
"""
创建 JeecgBoot BPM 流程
Args:
api_base: 后端地址,如 "https://api3.boot.jeecg.com"
token: X-Access-Token
process_name: 流程名称
process_type: 流程类型(如 "oa")
bpmn_xml: 完整 BPMN XML 字符串
nodes_str: nodes 参数字符串
tenant_id: 租户ID,默认 "1"
Returns:
dict: API 返回结果
"""
ts = str(int(time.time() * 1000))
process_key = f"process_{ts}"
# 替换 XML 中的占位符
bpmn_xml = bpmn_xml.replace("${PROCESS_KEY}", process_key)
bpmn_xml = bpmn_xml.replace("${PROCESS_NAME}", process_name)
data = {
"processDefinitionId": "0",
"processName": process_name,
"processkey": process_key,
"typeid": process_type,
"lowAppId": "",
"params": "",
"nodes": nodes_str,
"processDescriptor": bpmn_xml,
"realProcDefId": "",
"startType": "manual"
}
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(
f"{api_base}/act/designer/api/saveProcess",
data=encoded_data,
headers={
"X-Access-Token": token,
"X-Sign": "00000000000000000000000000000000",
"X-Tenant-Id": tenant_id,
"X-Timestamp": ts,
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
method="POST"
)
resp = urllib.request.urlopen(req)
result = json.loads(resp.read().decode('utf-8'))
return result, process_key
def update_bpm_process(api_base, token, process_id, process_key, process_name, process_type, bpmn_xml, nodes_str, tenant_id="1"):
"""
更新已有 JeecgBoot BPM 流程
Args:
api_base: 后端地址
token: X-Access-Token
process_id: 流程数据表ID(新建时返回的 obj)
process_key: 流程定义ID
process_name: 流程名称
process_type: 流程类型
bpmn_xml: 完整 BPMN XML 字符串
nodes_str: nodes 参数字符串
tenant_id: 租户ID,默认 "1"
Returns:
dict: API 返回结果
"""
ts = str(int(time.time() * 1000))
data = {
"processDefinitionId": process_id,
"processName": process_name,
"processkey": process_key,
"typeid": process_type,
"lowAppId": "",
"params": "",
"nodes": nodes_str,
"processDescriptor": bpmn_xml,
"startType": "manual"
}
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(
f"{api_base}/act/designer/api/saveProcess",
data=encoded_data,
headers={
"X-Access-Token": token,
"X-Sign": "00000000000000000000000000000000",
"X-Tenant-Id": tenant_id,
"X-Timestamp": ts,
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
method="POST"
)
resp = urllib.request.urlopen(req)
result = json.loads(resp.read().decode('utf-8'))
return result
```
## 3. 常见流程模式速查
### 模式A:简单审批(线性)
```
开始 → 提交 → 审批 → 结束
```
### 模式B:多级审批(线性)
```
开始 → 提交 → 经理审批 → 总监审批 → HR审批 → 结束
```
### 模式C:条件分支(排他网关)
```
开始 → 提交 → 审批 → 网关
├─ 通过 → 下一步 → 结束
└─ 拒绝 → 结束
```
### 模式D:金额条件分支
```
开始 → 提交 → 网关(金额判断)
├─ ≤1000 → 经理审批 → 结束
├─ ≤10000 → 总监审批 → 结束
└─ >10000 → CEO审批 → 结束
```
### 模式E:并行会签
```
开始 → 提交 → 并行网关(fork)
├─ 部门A审批
└─ 部门B审批
并行网关(join) → 结束
```
### 模式F:审批+驳回到发起人(草稿节点模式)
```
开始 → 草稿(自动跳过) → 审批 → 网关
├─ 通过 → 结束
└─ 驳回 → 草稿(发起人修改后重新提交)
```
**关键节点 XML:**
```xml
```
### 模式G:多级审批 + 表达式审批人 + 上一节点指派(实战验证)
```
开始 → 申请人填写(草稿) → 部门负责人审批(表达式) → 分管领导审批(表达式) → 指派确认(上一节点指派) → 结束
```
**已验证成功的完整 Python 脚本(f-string 方式构造 XML):**
```python
import urllib.request
import urllib.parse
import json
import time
API_BASE = '{后端地址}'
TOKEN = '{X-Access-Token}'
ts = str(int(time.time() * 1000))
process_key = f'process_{ts}'
process_name = '车辆出差申请流程'
# 注意:f-string 中 ${xxx} 写作 ${{xxx}},JSON 花括号也需 {{}}
bpmn_xml = f'''
'''
nodes_str = 'id=task_apply###nodeName=申请人填写@@@id=task_dept_leader###nodeName=部门负责人审批@@@id=task_leader###nodeName=分管领导审批@@@id=task_dispatch###nodeName=车辆调度确认@@@'
data = {
'processDefinitionId': '0',
'processName': process_name,
'processkey': process_key,
'typeid': 'oa',
'lowAppId': '',
'params': '',
'nodes': nodes_str,
'processDescriptor': bpmn_xml,
'realProcDefId': '',
'startType': 'manual'
}
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(
f'{API_BASE}/act/designer/api/saveProcess',
data=encoded_data,
headers={
'X-Access-Token': TOKEN,
'X-Sign': '00000000000000000000000000000000',
'X-Tenant-Id': '1',
'X-Timestamp': str(int(time.time() * 1000)),
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
method='POST'
)
resp = urllib.request.urlopen(req)
result = json.loads(resp.read().decode('utf-8'))
print(json.dumps(result, ensure_ascii=False, indent=2))
print(f'\\nProcess Key: {process_key}')
```
**实战要点:**
1. 此脚本必须先写入 `.py` 文件再执行(不能 `python3 -c` 内联,bash 会展开 `${}` 导致报错)
2. f-string 中所有 `${xxx}` 都写作 `${{xxx}}`
3. taskExtendJson 的 `value` 用单引号包裹 JSON,JSON 花括号用 `{{}}` 转义
4. 执行完毕后删除临时 `.py` 文件