2026-08-06-oa-dept-condition-design.md 7.9 KB

OA 条件分支:部门 / 归属部门「包含」

日期: 2026-08-06
范围: web-admin 审批设计器步骤 2(表单设计)勾选 + 步骤 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 / 人员多选)
  • docs/superpowers/specs/2026-07-30-oa-owner-fields-design.md(曾约定 ownerDept 暂不进条件,本期废止该限制)
    状态: 已实现

1. 背景与目标

表单「人员 / 归属人」已可勾选参与条件分支(contains + id 交集)。「部门 / 归属部门」值结构同为 {id, name}(可单可多),但前后端白名单均未纳入,条件编辑器也无部门值选择器。

1.1 产品结论(已确认)

项 结论
控件 dept、ownerDept 均可勾选「参与流程条件分支」
匹配语义 精确 id;表单填下级部门 ≠ 条件选中上级(不含树包含)
运算符 仅「包含」contains
条件值 string[] 部门 id;多选;与表单部门 id 有交集即命中(OR)
条件值 UI 树形多选(与表单部门控件同源数据 fetchDeptTree)
必填联动 勾选 ⇒ 强制必填并锁定,与现网一致
实现路径 镜像人员路径(方案 1)

1.2 与人员条件对照

person / ownerPerson dept / ownerDept
入池 useInCondition + 白名单 同左(本期补白名单)
op contains contains
value 用户 id[] 部门 id[]
命中 extract_*_ids(form) ∩ value ≠ ∅ 同左
RHS UI 用户扁平多选 部门树多选

1.3 非目标

  • 含下级 / 祖先匹配、部门路径字符串比较
  • 系统变量「发起人部门」
  • == / != / 「不包含」
  • 改写公式引擎核心
  • 改部门表单控件本身的选人/选部门交互

2. 架构

[2 表单设计]                         [3 流程设计]
  dept / ownerDept                    ConditionRuleEditor
  · useInCondition 开关可见              · 字段候选含勾选后的部门控件
  · 勾选 ⇒ required 锁定               · op 仅「包含」
         ↓                              · RHS:el-tree-select 多选部门 id
           form_schema_json              rules: { field, op:'contains', value: string[] }
                        ↘              ↙
                     发布 / 运行引擎
               · CONDITION_ELIGIBLE 含 dept、ownerDept
               · dept_contains_match(form_val, value)

职责边界

  • 表单: 谁可进条件池(白名单 + 勾选)。
  • 流程编辑器: 树多选写出 value: string[]。
  • 引擎: contains 时对部门值走 dept_contains_match(与 person/post 并列)。

3. 数据契约(锁定)

// 表单填报值(已有)
// 单选:{ id: string, name: string }
// 多选:Array<{ id: string, name: string }>
// ownerDept:始终单选对象

// 条件规则(与人员同形)
{ field: string, op: 'contains', value: string[] } // length >= 1 在发布/完善规则时由现有校验约束

命中:

extract_dept_ids(form_data[field]) ∩ Set(value) ≠ ∅

extract_dept_ids:与 extract_person_ids 同构——支持单对象或对象数组,只取非空 id。

预览文案: 格式化字段名 包含 部门A、部门B(有树 map 用名称,否则 id)。


4. 前端

4.1 白名单与运算符

web-admin/src/components/oa-approval/oa-condition-fields.ts:

  • CONDITION_ELIGIBLE_TYPES 增加 'dept'、'ownerDept'
  • opsForFieldType:dept / ownerDept 与 person 一样仅返回 contains
  • formatConditionPreview:沿用现有 op 文案「包含」(无需新 op)

oa-fc-host/App.vue 已用 CONDITION_ELIGIBLE_TYPES 控制开关显隐,加类型即可,不必改宿主逻辑。

4.2 条件编辑器

ConditionRuleEditor.vue:

  • isDeptLikeField:type === 'dept' || type === 'ownerDept'
  • RHS:el-tree-select(multiple、filterable、check-strictly、collapse-tags),数据来自 fetchDeptTree,节点展示本级 deptName(与表单部门控件一致;不必在条件下拉里拼全路径)
  • defaultValueForType:部门类返回 []
  • 挂载时若候选含部门类字段则加载部门树(可与用户/职位加载并列)

不复用完整 OaDeptSelect 组件(表单控件含窄屏抽屉、emit 对象值);条件侧只存 id[],用轻量 tree-select 即可。

4.3 取消勾选拦截

沿用现有「流程仍引用该 field → 禁止取消 useInCondition」。部门字段无特殊伪字段,无需改引用集合逻辑。


5. 后端

5.1 oa_dept_select.py

新增:

  • extract_dept_ids(raw) -> Set[str]
  • dept_contains_match(form_value, cond_ids) -> bool
    (实现与 person_contains_match / post_contains_match 对齐)

5.2 引擎

oa_engine._eval_condition_rules 中 op == "contains" 且非 __initiator__ 时:

if not (
    person_contains_match(form_val, val)
    or post_contains_match(form_val, val)
    or dept_contains_match(form_val, val)
):
    return False

保持与现网「多 helper OR」一致:错误形态抽取为空集,不会误命中。

5.3 发布

oa_publish.CONDITION_ELIGIBLE_TYPES 增加 "dept"、"ownerDept"。

index_condition_fields / validate_condition_rules_against_form 自动吃到新类型;不要求为部门单独加「发起人式」校验块(与人员表单字段同级:依赖流程树「规则须完善」+ 字段在 index 内)。

可选增强(非必须):对 dept/ownerDept/person/ownerPerson/post 统一校验 op == contains 且 value 为非空 string 列表——若本期做,须覆盖人员回归;默认可不做,避免扩大范围。


6. 测试

场景 期望
表单多选部门与 value 有交集 dept_contains_match True
仅下级 id、条件为上级 id False(精确匹配)
单对象 ownerDept 命中 True
引擎条件组含 dept contains 正确分流 / 走默认
发布:勾选 dept 字段可被 rules 引用 通过
回归 person / post / initiator contains 通过

文件:backend/tests/test_oa_dept_select.py(补 extract/contains)、backend/tests/test_oa_engine.py(补一条 dept 条件)。


7. 文件清单

前端:
oa-condition-fields.ts、ConditionRuleEditor.vue(必要时样式微调)

后端:
oa_dept_select.py、oa_engine.py、oa_publish.py、
tests/test_oa_dept_select.py、tests/test_oa_engine.py

文档(旁注,非阻塞):
2026-07-30-oa-owner-fields-design.md §4.2 一句改为「ownerDept 与 dept 同属部门类(contains)」——可选 follow-up,避免历史规格误导。


8. 验收标准

  • 表单设计选中部门/归属部门时,出现「参与流程条件分支」且勾选后必填锁定
  • 流程条件可选该字段;仅「包含」;RHS 为部门树多选
  • 运行时按部门 id 精确交集分流;下级不命中上级条件
  • 发布与单测通过;人员/职位/发起人条件无回归

9. 实现顺序

  1. extract_dept_ids + dept_contains_match + 单测
  2. 引擎 OR 接入 + 引擎单测
  3. 发布白名单
  4. 前端白名单 + ConditionRuleEditor 树多选
  5. 设计器手测(勾选 → 配条件 → 预览/发起)

10. Spec 自检

  • 无 TBD / 占位未决
  • 与人员路径一致,无互相矛盾的 op/value 约定
  • 范围单一:仅部门进条件;不含发起人部门、不含树包含
  • 「精确匹配」与「树形多选 UI」语义已区分:UI 树仅便于挑选,匹配仍比 id