camunda-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- camunda/__init__.py +10 -0
- camunda/api/__init__.py +14 -0
- camunda/api/app.py +80 -0
- camunda/api/deps.py +17 -0
- camunda/api/errors.py +84 -0
- camunda/api/pagination.py +74 -0
- camunda/api/routers/__init__.py +5 -0
- camunda/api/routers/decision.py +57 -0
- camunda/api/routers/deployment.py +139 -0
- camunda/api/routers/history.py +128 -0
- camunda/api/routers/process_definition.py +49 -0
- camunda/api/routers/process_instance.py +106 -0
- camunda/api/routers/task.py +92 -0
- camunda/api/schemas.py +200 -0
- camunda/common/__init__.py +19 -0
- camunda/common/clock.py +30 -0
- camunda/common/exceptions.py +34 -0
- camunda/common/idgen.py +23 -0
- camunda/common/timers.py +106 -0
- camunda/dmn/__init__.py +5 -0
- camunda/dmn/engine.py +219 -0
- camunda/dmn/feel.py +392 -0
- camunda/engine/__init__.py +9 -0
- camunda/engine/behavior.py +51 -0
- camunda/engine/expression.py +126 -0
- camunda/engine/process_engine.py +3237 -0
- camunda/job/__init__.py +9 -0
- camunda/job/executor.py +136 -0
- camunda/model/__init__.py +48 -0
- camunda/model/bpmn.py +326 -0
- camunda/model/dmn.py +101 -0
- camunda/model/execution.py +121 -0
- camunda/model/job.py +88 -0
- camunda/model/task.py +33 -0
- camunda/model/variable.py +35 -0
- camunda/parser/__init__.py +5 -0
- camunda/parser/bpmn_parser.py +646 -0
- camunda/parser/dmn_parser.py +225 -0
- camunda/persistence/__init__.py +21 -0
- camunda/persistence/entities.py +202 -0
- camunda/persistence/store.py +721 -0
- camunda_python-0.1.0.dist-info/METADATA +377 -0
- camunda_python-0.1.0.dist-info/RECORD +46 -0
- camunda_python-0.1.0.dist-info/WHEEL +5 -0
- camunda_python-0.1.0.dist-info/licenses/LICENSE +200 -0
- camunda_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""DMN 1.1/1.3 XML 解析器(lxml,M5-1)。
|
|
2
|
+
|
|
3
|
+
职责(对齐 Camunda dmn-model 的解析部分、与 bpmn_parser 同风格):
|
|
4
|
+
1. 解析 XML -> DmnModel(含多个 Decision)
|
|
5
|
+
2. decisionTable 结构:inputs(inputExpression 文本)/ outputs / rules
|
|
6
|
+
(inputEntry/outputEntry 的 dmn:text 原文)
|
|
7
|
+
3. 校验:hitPolicy 合法、COLLECT aggregator 合法、entry 数量与列对齐、
|
|
8
|
+
非 decisionTable 形态(literalExpression 等)明确报错
|
|
9
|
+
|
|
10
|
+
命名空间处理策略:只用 localName 分派(DMN 1.1/1.3 同构),不校验版本。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from lxml import etree
|
|
18
|
+
|
|
19
|
+
from camunda.common.exceptions import DeploymentException
|
|
20
|
+
from camunda.model.dmn import (
|
|
21
|
+
AGGREGATORS,
|
|
22
|
+
HIT_POLICIES,
|
|
23
|
+
Decision,
|
|
24
|
+
DecisionTable,
|
|
25
|
+
DmnInput,
|
|
26
|
+
DmnModel,
|
|
27
|
+
DmnOutput,
|
|
28
|
+
DmnRule,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _local(tag: str) -> str:
|
|
33
|
+
"""lxml tag 形如 {ns}localName -> localName。"""
|
|
34
|
+
return tag.rsplit("}", 1)[-1]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _children(el: etree._Element, local_name: str) -> List[etree._Element]:
|
|
38
|
+
return [c for c in el if isinstance(c.tag, str) and _local(c.tag) == local_name]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _text_of(el: Optional[etree._Element]) -> str:
|
|
42
|
+
"""取元素文本(None 安全)。"""
|
|
43
|
+
return (el.text or "") if el is not None else ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _normalize_entry(text: str) -> Optional[str]:
|
|
47
|
+
"""单元格文本归一:空 / '-' -> None(通配或空输出);其余 strip。"""
|
|
48
|
+
t = text.strip()
|
|
49
|
+
if t in ("", "-"):
|
|
50
|
+
return None
|
|
51
|
+
return t
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def parse_dmn_xml(xml_text: str, source_name: Optional[str] = None) -> DmnModel:
|
|
55
|
+
"""解析 DMN XML 文本 -> DmnModel。失败抛 DeploymentException。"""
|
|
56
|
+
try:
|
|
57
|
+
root = etree.fromstring(xml_text.encode("utf-8"))
|
|
58
|
+
except etree.XMLSyntaxError as e:
|
|
59
|
+
raise DeploymentException(f"DMN XML 语法错误: {e}") from e
|
|
60
|
+
|
|
61
|
+
if _local(root.tag) != "definitions":
|
|
62
|
+
raise DeploymentException(
|
|
63
|
+
f"DMN 根元素须为 definitions,实际 {_local(root.tag)!r}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
model = DmnModel(source_name=source_name, source_xml=xml_text)
|
|
67
|
+
for el in root:
|
|
68
|
+
if not isinstance(el.tag, str):
|
|
69
|
+
continue
|
|
70
|
+
if _local(el.tag) == "decision":
|
|
71
|
+
model.decisions.append(_parse_decision(el))
|
|
72
|
+
if not model.decisions:
|
|
73
|
+
raise DeploymentException("DMN definitions 未包含任何 decision")
|
|
74
|
+
return model
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def parse_dmn_file(path: str) -> DmnModel:
|
|
78
|
+
"""解析 .dmn 文件(demo/测试便捷入口)。"""
|
|
79
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
80
|
+
return parse_dmn_xml(f.read(), source_name=path.rsplit("/", 1)[-1])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _parse_decision(el: etree._Element) -> Decision:
|
|
84
|
+
dec_id = el.get("id")
|
|
85
|
+
if not dec_id:
|
|
86
|
+
raise DeploymentException("decision 缺少 id 属性")
|
|
87
|
+
dec = Decision(id=dec_id, name=el.get("name"))
|
|
88
|
+
|
|
89
|
+
for child in el:
|
|
90
|
+
if not isinstance(child.tag, str):
|
|
91
|
+
continue
|
|
92
|
+
ln = _local(child.tag)
|
|
93
|
+
if ln == "decisionTable":
|
|
94
|
+
dec.decision_table = _parse_decision_table(child)
|
|
95
|
+
elif ln == "variable":
|
|
96
|
+
continue # decision 输出类型声明,M5 不消费
|
|
97
|
+
else:
|
|
98
|
+
# literalExpression / relation / invocation / context -> 明确报错
|
|
99
|
+
raise DeploymentException(
|
|
100
|
+
f"decision {dec_id!r} 仅支持 decisionTable 形态,"
|
|
101
|
+
f"遇到不支持的子元素 {ln!r}(M5 文档化差异)"
|
|
102
|
+
)
|
|
103
|
+
if dec.decision_table is None:
|
|
104
|
+
raise DeploymentException(f"decision {dec_id!r} 缺少 decisionTable")
|
|
105
|
+
return dec
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _parse_decision_table(el: etree._Element) -> DecisionTable:
|
|
109
|
+
hit_policy = (el.get("hitPolicy") or "UNIQUE").strip().upper()
|
|
110
|
+
if hit_policy not in HIT_POLICIES:
|
|
111
|
+
raise DeploymentException(
|
|
112
|
+
f"未知 hitPolicy: {el.get('hitPolicy')!r}(支持 {sorted(HIT_POLICIES)})"
|
|
113
|
+
)
|
|
114
|
+
aggregator = el.get("aggregation")
|
|
115
|
+
aggregator = aggregator.strip().upper() if aggregator else None
|
|
116
|
+
if aggregator is not None and aggregator not in AGGREGATORS:
|
|
117
|
+
raise DeploymentException(
|
|
118
|
+
f"未知 aggregation: {el.get('aggregation')!r}(支持 {sorted(AGGREGATORS)})"
|
|
119
|
+
)
|
|
120
|
+
if aggregator is not None and hit_policy != "COLLECT":
|
|
121
|
+
raise DeploymentException(
|
|
122
|
+
f"aggregation 仅在 hitPolicy=COLLECT 下合法(当前 {hit_policy!r})"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
table = DecisionTable(
|
|
126
|
+
id=el.get("id"), hit_policy=hit_policy, aggregator=aggregator
|
|
127
|
+
)
|
|
128
|
+
for child in el:
|
|
129
|
+
if not isinstance(child.tag, str):
|
|
130
|
+
continue
|
|
131
|
+
ln = _local(child.tag)
|
|
132
|
+
if ln == "input":
|
|
133
|
+
table.inputs.append(_parse_input(child))
|
|
134
|
+
elif ln == "output":
|
|
135
|
+
table.outputs.append(_parse_output(child))
|
|
136
|
+
elif ln == "rule":
|
|
137
|
+
table.rules.append(_parse_rule(child))
|
|
138
|
+
# annotation / informationRequirement 等忽略
|
|
139
|
+
|
|
140
|
+
if not table.outputs:
|
|
141
|
+
raise DeploymentException(
|
|
142
|
+
f"decisionTable {table.id!r} 至少需要一个 output 列"
|
|
143
|
+
)
|
|
144
|
+
_validate_entry_alignment(table)
|
|
145
|
+
return table
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _parse_input(el: etree._Element) -> DmnInput:
|
|
149
|
+
expr_el = None
|
|
150
|
+
for c in _children(el, "inputExpression"):
|
|
151
|
+
expr_el = c
|
|
152
|
+
break
|
|
153
|
+
if expr_el is None:
|
|
154
|
+
raise DeploymentException(f"input {el.get('id')!r} 缺少 inputExpression")
|
|
155
|
+
return DmnInput(
|
|
156
|
+
id=el.get("id"),
|
|
157
|
+
name=el.get("label"),
|
|
158
|
+
expression=_text_of(expr_el).strip(),
|
|
159
|
+
type_ref=expr_el.get("typeRef"),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _parse_output(el: etree._Element) -> DmnOutput:
|
|
164
|
+
return DmnOutput(
|
|
165
|
+
id=el.get("id"),
|
|
166
|
+
name=el.get("name"),
|
|
167
|
+
label=el.get("label"),
|
|
168
|
+
type_ref=el.get("typeRef"),
|
|
169
|
+
output_values=_parse_output_values(el),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _parse_output_values(el: etree._Element) -> List[str]:
|
|
174
|
+
"""outputValues 子元素:逗号分隔的 FEEL 字面量列表(尊重引号内的逗号)。"""
|
|
175
|
+
for child in el:
|
|
176
|
+
if isinstance(child.tag, str) and _local(child.tag) == "outputValues":
|
|
177
|
+
texts = _children(child, "text")
|
|
178
|
+
raw = _text_of(texts[0]) if texts else ""
|
|
179
|
+
parts: List[str] = []
|
|
180
|
+
buf: List[str] = []
|
|
181
|
+
in_quote = False
|
|
182
|
+
for ch in raw:
|
|
183
|
+
if ch == '"':
|
|
184
|
+
in_quote = not in_quote
|
|
185
|
+
buf.append(ch)
|
|
186
|
+
elif ch == "," and not in_quote:
|
|
187
|
+
parts.append("".join(buf).strip())
|
|
188
|
+
buf = []
|
|
189
|
+
else:
|
|
190
|
+
buf.append(ch)
|
|
191
|
+
parts.append("".join(buf).strip())
|
|
192
|
+
return [p for p in parts if p]
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _parse_rule(el: etree._Element) -> DmnRule:
|
|
197
|
+
rule = DmnRule(id=el.get("id"))
|
|
198
|
+
for child in el:
|
|
199
|
+
if not isinstance(child.tag, str):
|
|
200
|
+
continue
|
|
201
|
+
ln = _local(child.tag)
|
|
202
|
+
if ln == "inputEntry":
|
|
203
|
+
rule.input_entries.append(_normalize_entry(_text_of(_children(child, "text")[0]) if _children(child, "text") else ""))
|
|
204
|
+
elif ln == "outputEntry":
|
|
205
|
+
texts = _children(child, "text")
|
|
206
|
+
rule.output_entries.append(
|
|
207
|
+
_normalize_entry(_text_of(texts[0]) if texts else "")
|
|
208
|
+
)
|
|
209
|
+
# description / annotationEntry 忽略
|
|
210
|
+
return rule
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _validate_entry_alignment(table: DecisionTable) -> None:
|
|
214
|
+
"""规则行 entry 数量与列对齐校验(DMN 规范要求严格对应)。"""
|
|
215
|
+
for rule in table.rules:
|
|
216
|
+
if len(rule.input_entries) != len(table.inputs):
|
|
217
|
+
raise DeploymentException(
|
|
218
|
+
f"rule {rule.id!r} 的 inputEntry 数 {len(rule.input_entries)}"
|
|
219
|
+
f" 与 input 列数 {len(table.inputs)} 不一致"
|
|
220
|
+
)
|
|
221
|
+
if len(rule.output_entries) != len(table.outputs):
|
|
222
|
+
raise DeploymentException(
|
|
223
|
+
f"rule {rule.id!r} 的 outputEntry 数 {len(rule.output_entries)}"
|
|
224
|
+
f" 与 output 列数 {len(table.outputs)} 不一致"
|
|
225
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""persistence 包:SQLAlchemy 2.0 持久层(M2 里程碑交付)。
|
|
2
|
+
|
|
3
|
+
- entities.py ACT_RE_*/ACT_RU_*/ACT_HI_* ORM 实体(对齐 Camunda 表契约)
|
|
4
|
+
- store.py 快照同步 + 存取门面(SQLite/PostgreSQL url 均可)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from camunda.persistence.store import (
|
|
8
|
+
ActivitySnap,
|
|
9
|
+
ExecutionSnap,
|
|
10
|
+
ProcInstSnap,
|
|
11
|
+
Store,
|
|
12
|
+
TaskSnap,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Store",
|
|
17
|
+
"ProcInstSnap",
|
|
18
|
+
"ExecutionSnap",
|
|
19
|
+
"TaskSnap",
|
|
20
|
+
"ActivitySnap",
|
|
21
|
+
]
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""SQLAlchemy 2.0 ORM 实体:对齐 Camunda ACT_* 表契约。
|
|
2
|
+
|
|
3
|
+
表契约(对齐 Camunda 7 的 MyBatis 表):
|
|
4
|
+
- ACT_RE_DEPLOYMENT / ACT_RE_PROCDEF 静态定义(M2 简化:资源 xml 直接存 prodef 行)
|
|
5
|
+
- ACT_RU_EXECUTION / ACT_RU_TASK /
|
|
6
|
+
ACT_RU_VARIABLE 运行时瞬时态(RU = running)
|
|
7
|
+
- ACT_HI_PROCINST / ACT_HI_ACTINST /
|
|
8
|
+
ACT_HI_TASKINST / ACT_HI_VARINST 历史归档
|
|
9
|
+
|
|
10
|
+
M2 差异说明(文档化):
|
|
11
|
+
- Camunda 用 bytearray 表存资源 -> M2 直接存 prodef.resource_xml 文本
|
|
12
|
+
- Camunda HI_VARINST 每次变更追加版本 -> M2 按实例快照(每实例每变量一行)
|
|
13
|
+
- Camunda 变量挂 execution -> M2 引擎变量为实例级,VARIABLE 表挂 proc_inst
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from sqlalchemy import Integer, String, Text
|
|
19
|
+
from sqlalchemy.dialects.mysql import MEDIUMTEXT
|
|
20
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
21
|
+
|
|
22
|
+
# MySQL 方言下 TEXT 上限 64KB(65535 字节),BPMN 资源 XML 与变量 JSON 可能
|
|
23
|
+
# 超限(MySQL 报 Data too long);此处统一把大文本列在 MySQL 声明为
|
|
24
|
+
# MEDIUMTEXT(16MB)。SQLite/PostgreSQL 的 Text 无长度限制,with_variant
|
|
25
|
+
# 不影响这两者。无迁移工具:MySQL 上需 DROP 重建或手动 ALTER 已有列。
|
|
26
|
+
BIG_TEXT = Text().with_variant(MEDIUMTEXT(), "mysql")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Base(DeclarativeBase):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# 静态定义(RE)
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
class DeploymentEntity(Base):
|
|
37
|
+
"""一次部署(对齐 ACT_RE_DEPLOYMENT)。"""
|
|
38
|
+
|
|
39
|
+
__tablename__ = "ACT_RE_DEPLOYMENT"
|
|
40
|
+
|
|
41
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
42
|
+
name_: Mapped[str | None] = mapped_column("NAME_", String(255), nullable=True)
|
|
43
|
+
deploy_time_: Mapped[str] = mapped_column("DEPLOY_TIME_", String(32))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ProcDefEntity(Base):
|
|
47
|
+
"""流程定义(对齐 ACT_RE_PROCDEF)。
|
|
48
|
+
|
|
49
|
+
Camunda 主键是 ID_,同 KEY_ 可多版本(VERSION_ 递增)。M2 主键 = f"{key}:{version}"。
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
__tablename__ = "ACT_RE_PROCDEF"
|
|
53
|
+
|
|
54
|
+
id_: Mapped[str] = mapped_column("ID_", String(128), primary_key=True)
|
|
55
|
+
key_: Mapped[str] = mapped_column("KEY_", String(128), index=True)
|
|
56
|
+
name_: Mapped[str | None] = mapped_column("NAME_", String(255), nullable=True)
|
|
57
|
+
version_: Mapped[int] = mapped_column("VERSION_", default=1)
|
|
58
|
+
deployment_id_: Mapped[str] = mapped_column("DEPLOYMENT_ID_", String(64))
|
|
59
|
+
resource_xml_: Mapped[str] = mapped_column("RESOURCE_XML_", BIG_TEXT)
|
|
60
|
+
is_executable_: Mapped[bool] = mapped_column("IS_EXECUTABLE_", default=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# 运行时(RU)
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
class ExecutionEntity(Base):
|
|
67
|
+
"""执行树节点(对齐 ACT_RU_EXECUTION)。
|
|
68
|
+
|
|
69
|
+
只保存 ACTIVE 的 execution(ENDED 的随历史归档,RU 表语义)。
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
__tablename__ = "ACT_RU_EXECUTION"
|
|
73
|
+
|
|
74
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
75
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
76
|
+
parent_id_: Mapped[str | None] = mapped_column("PARENT_ID_", String(64), nullable=True)
|
|
77
|
+
activity_id_: Mapped[str | None] = mapped_column("ACT_ID_", String(255), nullable=True)
|
|
78
|
+
role_: Mapped[str] = mapped_column("ROLE_", String(16), default="TOKEN")
|
|
79
|
+
# M4-2c4:多实例状态 JSON(容器 total/active/completed/next_index/... 或实例
|
|
80
|
+
# {"index": i})。Camunda 以 loopCounter 等 ACT_RU_VARIABLE + IS_SCOPE_ 关联
|
|
81
|
+
# 表达;M2 简化:实例级变量 + 此列直存容器状态(崩溃恢复必需)。
|
|
82
|
+
mi_: Mapped[str | None] = mapped_column("MI_", BIG_TEXT, nullable=True)
|
|
83
|
+
# Camunda 还有 IS_CONCURRENT_ / IS_SCOPE_ 等;role 已覆盖 M1 语义
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TaskEntity(Base):
|
|
87
|
+
"""待办任务(对齐 ACT_RU_TASK)。"""
|
|
88
|
+
|
|
89
|
+
__tablename__ = "ACT_RU_TASK"
|
|
90
|
+
|
|
91
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
92
|
+
name_: Mapped[str | None] = mapped_column("NAME_", String(255), nullable=True)
|
|
93
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
94
|
+
execution_id_: Mapped[str] = mapped_column("EXECUTION_ID_", String(64))
|
|
95
|
+
task_definition_key_: Mapped[str] = mapped_column("TASK_DEF_KEY_", String(255))
|
|
96
|
+
assignee_: Mapped[str | None] = mapped_column("ASSIGNEE_", String(255), nullable=True)
|
|
97
|
+
create_time_: Mapped[str] = mapped_column("CREATE_TIME_", String(32))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class VariableEntity(Base):
|
|
101
|
+
"""流程变量(对齐 ACT_RU_VARIABLE;M2 实例级,复合主键 proc_inst+name)。"""
|
|
102
|
+
|
|
103
|
+
__tablename__ = "ACT_RU_VARIABLE"
|
|
104
|
+
|
|
105
|
+
id_: Mapped[str] = mapped_column("ID_", String(96), primary_key=True)
|
|
106
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
107
|
+
name_: Mapped[str] = mapped_column("NAME_", String(255))
|
|
108
|
+
type_: Mapped[str] = mapped_column("TYPE_", String(32)) # Java 类型名
|
|
109
|
+
text_: Mapped[str | None] = mapped_column("TEXT_", BIG_TEXT, nullable=True) # JSON 序列化
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class JobEntity(Base):
|
|
113
|
+
"""可调度作业(对齐 ACT_RU_JOB,M3 核心子集)。
|
|
114
|
+
|
|
115
|
+
- 实例级 job(timer-catch / async-continuation):PROC_INST_ID_ 有值,
|
|
116
|
+
随实例 RU 快照全量重写
|
|
117
|
+
- 定义级 job(timer-start):PROC_INST_ID_ 为 NULL,PROC_DEF_KEY_ + ACT_ID_
|
|
118
|
+
指向流程与 startEvent;部署新版本时整组重建
|
|
119
|
+
- LOCK_OWNER_ / LOCK_EXP_TIME_ 预留多实例抢占(M3 单进程不使用)
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
__tablename__ = "ACT_RU_JOB"
|
|
123
|
+
|
|
124
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
125
|
+
job_type_: Mapped[str] = mapped_column("TYPE_", String(32))
|
|
126
|
+
process_instance_id_: Mapped[str | None] = mapped_column(
|
|
127
|
+
"PROC_INST_ID_", String(64), nullable=True, index=True
|
|
128
|
+
)
|
|
129
|
+
execution_id_: Mapped[str | None] = mapped_column(
|
|
130
|
+
"EXECUTION_ID_", String(64), nullable=True
|
|
131
|
+
)
|
|
132
|
+
process_definition_key_: Mapped[str | None] = mapped_column(
|
|
133
|
+
"PROC_DEF_KEY_", String(128), nullable=True, index=True
|
|
134
|
+
)
|
|
135
|
+
node_id_: Mapped[str | None] = mapped_column("ACT_ID_", String(255), nullable=True)
|
|
136
|
+
duedate_: Mapped[str] = mapped_column("DUEDATE_", String(32), index=True)
|
|
137
|
+
created_: Mapped[str] = mapped_column("CREATED_", String(32))
|
|
138
|
+
retries_: Mapped[int] = mapped_column("RETRIES_", Integer, default=3)
|
|
139
|
+
repeat_: Mapped[str | None] = mapped_column("REPEAT_", BIG_TEXT, nullable=True) # JSON
|
|
140
|
+
lock_owner_: Mapped[str | None] = mapped_column(
|
|
141
|
+
"LOCK_OWNER_", String(255), nullable=True
|
|
142
|
+
)
|
|
143
|
+
lock_expire_time_: Mapped[str | None] = mapped_column(
|
|
144
|
+
"LOCK_EXP_TIME_", String(32), nullable=True
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
# 历史(HI)
|
|
150
|
+
# ---------------------------------------------------------------------------
|
|
151
|
+
class HistProcInstEntity(Base):
|
|
152
|
+
"""流程实例历史(对齐 ACT_HI_PROCINST)。"""
|
|
153
|
+
|
|
154
|
+
__tablename__ = "ACT_HI_PROCINST"
|
|
155
|
+
|
|
156
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
157
|
+
process_definition_key_: Mapped[str] = mapped_column("PROC_DEF_KEY_", String(128), index=True)
|
|
158
|
+
business_key_: Mapped[str | None] = mapped_column("BUSINESS_KEY_", String(255), nullable=True)
|
|
159
|
+
state_: Mapped[str] = mapped_column("STATE_", String(16)) # ACTIVE/COMPLETED
|
|
160
|
+
start_time_: Mapped[str] = mapped_column("START_TIME_", String(32))
|
|
161
|
+
end_time_: Mapped[str | None] = mapped_column("END_TIME_", String(32), nullable=True)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class HistActInstEntity(Base):
|
|
165
|
+
"""活动实例历史(对齐 ACT_HI_ACTINST)。"""
|
|
166
|
+
|
|
167
|
+
__tablename__ = "ACT_HI_ACTINST"
|
|
168
|
+
|
|
169
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
170
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
171
|
+
activity_id_: Mapped[str] = mapped_column("ACT_ID_", String(255))
|
|
172
|
+
activity_name_: Mapped[str | None] = mapped_column("ACT_NAME_", String(255), nullable=True)
|
|
173
|
+
execution_id_: Mapped[str] = mapped_column("EXECUTION_ID_", String(64))
|
|
174
|
+
start_time_: Mapped[str] = mapped_column("START_TIME_", String(32))
|
|
175
|
+
end_time_: Mapped[str | None] = mapped_column("END_TIME_", String(32), nullable=True)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class HistTaskInstEntity(Base):
|
|
179
|
+
"""任务实例历史(对齐 ACT_HI_TASKINST;M2 在 complete 时写入快照)。"""
|
|
180
|
+
|
|
181
|
+
__tablename__ = "ACT_HI_TASKINST"
|
|
182
|
+
|
|
183
|
+
id_: Mapped[str] = mapped_column("ID_", String(64), primary_key=True)
|
|
184
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
185
|
+
task_definition_key_: Mapped[str] = mapped_column("TASK_DEF_KEY_", String(255))
|
|
186
|
+
name_: Mapped[str | None] = mapped_column("NAME_", String(255), nullable=True)
|
|
187
|
+
execution_id_: Mapped[str] = mapped_column("EXECUTION_ID_", String(64))
|
|
188
|
+
assignee_: Mapped[str | None] = mapped_column("ASSIGNEE_", String(255), nullable=True)
|
|
189
|
+
create_time_: Mapped[str] = mapped_column("CREATE_TIME_", String(32))
|
|
190
|
+
end_time_: Mapped[str | None] = mapped_column("END_TIME_", String(32), nullable=True)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class HistVarInstEntity(Base):
|
|
194
|
+
"""变量历史(对齐 ACT_HI_VARINST;M2 实例快照语义)。"""
|
|
195
|
+
|
|
196
|
+
__tablename__ = "ACT_HI_VARINST"
|
|
197
|
+
|
|
198
|
+
id_: Mapped[str] = mapped_column("ID_", String(96), primary_key=True)
|
|
199
|
+
process_instance_id_: Mapped[str] = mapped_column("PROC_INST_ID_", String(64), index=True)
|
|
200
|
+
name_: Mapped[str] = mapped_column("NAME_", String(255))
|
|
201
|
+
type_: Mapped[str] = mapped_column("TYPE_", String(32))
|
|
202
|
+
text_: Mapped[str | None] = mapped_column("TEXT_", BIG_TEXT, nullable=True)
|