Allow __initiator__ contains matching in publish validation and the flow designer candidate list, excluding it from form useInCondition locks. Co-authored-by: Cursor <cursoragent@cursor.com>
4.8 KiB
4.8 KiB
OA 条件分支:系统变量「发起人」包含
日期: 2026-07-30
范围: web-admin 审批设计器步骤 3(流程设计)条件编辑;backend 条件匹配、发布校验、流程预览
关联: docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md(原非目标「系统变量」本期落地发起人);docs/superpowers/specs/2026-07-30-oa-person-select-design.md(contains / 人员多选 UI 可复用)
状态: 已实现
1. 背景与目标
表单驱动条件已支持勾选字段进分支;钉钉式「发起人属于某些人」仍缺失。现有规格将「系统变量(发起人等)」列为二期。本期仅落地 发起人 + 包含。
1.1 产品结论(已确认)
| 项 | 结论 |
|---|---|
| 候选出现方式 | 条件字段下拉常驻「发起人」,不依赖表单勾选 |
| 新建分支 | 不预填发起人规则(空规则,配置者自行添加) |
| 运算符 | 仅「包含」;右侧多选用户;发起人 id ∈ value 即命中(OR) |
| 与表单条件 | 同一 conditionGroup 内多条规则仍 AND |
| 实现路径 | 系统伪字段 __initiator__(方案 A) |
1.2 与表单「人员」控件区别
表单 person |
系统「发起人」 | |
|---|---|---|
| 数据 | 表单填报所选人员 | initiator_user_id |
| 入池 | 需勾选 useInCondition | 始终在候选中 |
| type | person |
initiator(仅条件候选用) |
1.3 非目标
- 不包含 / 等于 / 不等于
- 发起人部门、角色等其它系统变量
- 新建条件分支自动预填发起人规则
- 改写表单设计器或新增表单控件
2. 架构
[3 流程设计] ConditionRuleEditor
candidates = [发起人伪字段] + listConditionCandidates(schema)
rules: { field: '__initiator__', op: 'contains', value: userId[] }
↓
flow_tree_json
↓
[发布] validate:系统字段合法;value 非空 string[]
↓
[引擎 / 预览] _eval_condition_rules(..., initiator_user_id)
field==__initiator__ → initiator_user_id ∈ value
不将 __initiator__ 写入实例 form_data_json。
3. 数据契约(锁定)
const INITIATOR_CONDITION_FIELD = '__initiator__'
// 候选
{ field: '__initiator__', title: '发起人', type: 'initiator' }
// 规则
{ field: '__initiator__', op: 'contains', value: string[] } // length >= 1 at publish
命中:String(initiator_user_id).strip() ∈ Set(value)。
4. 前端
4.1 候选注入
listConditionCandidates(或 designer 组装处)前置系统候选,再拼接表单合格字段。
4.2 编辑器
opsForFieldType('initiator')→ 仅「包含」- RHS:与
person共用人员多选(fetchUserListstatus=1) - 预览:
发起人 包含 张三、李四(有 map 用姓名,否则 id) - 取消表单「参与条件」拦截:引用集合中的
__initiator__不得当作表单字段要求保留勾选
4.3 流程预览
发起页预览以当前登录用户为发起人;后端预览 API 传入该 uid 即可,前端不必另造发起人选择器。
5. 后端
5.1 引擎
_eval_condition_rules/_match_condition_group增加参数initiator_user_id: Optional[str]field == '__initiator__'且op == 'contains':用person_contains_match兼容形态,或直接
initiator_user_id in set(value)(发起人为标量 id,可构造{"id": uid, "name": ""}复用 helper)- 所有条件匹配调用点(推进、路径解析、流程预览)传入发起人 id
- 其它 field 行为不变
5.2 发布
validate_condition_rules_against_form:field == '__initiator__'不要求出现在表单 condition index- 校验:仅允许
op == 'contains';value为非空字符串 id 列表 - 非法 op / 空 value → 中文错误
6. 测试
| 场景 | 期望 |
|---|---|
| 发起人在 value 内 | 命中该组 |
| 发起人不在 value 内 | 走默认组 |
| 与表单金额 AND | 两边都真才命中 |
| 发布仅系统字段条件 | 通过 |
| 发布 value=[] | 失败 |
| 回归表单 person contains | 通过 |
7. 文件清单
前端: oa-condition-fields.ts、ConditionRuleEditor.vue、designer/index.vue(若需)、取消勾选引用逻辑处
后端: oa_engine.py、oa_publish.py、tests/test_oa_engine.py、publish 相关测试
8. 验收标准
- 条件字段下拉常驻「发起人」,无表单条件字段时仍可选
- 仅「包含」+ 多选人;新建分支不预填
- 运行与发起预览按发起人正确分流
- 发布校验与单测通过;既有表单条件无回归
9. 实现顺序
- 引擎 + 单测
- 发布校验
- 前端候选 + 编辑器
- 手测设计器与发起预览