20 KiB
OA 审批(钉钉交互)Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 在 web-admin + Python 后端交付独立「OA审批」模块:四步配置(基础/表单/流程/高级)、工作流角色、审批中心办理;引擎按钉钉式流程树运行。
Architecture: 前端用 FormCreate(Element Plus)做表单设计器、自研竖向流程树(参考 AntFlow 交互)+ Fresh Mint;后端新建 oa_* 表与 oa_approval 引擎,实例锁定已发布版本;审批人不读 sys_role,用 oa_role / dept_leader / direct_supervisor 解析。旧 X6/wf_* 菜单废弃。
Tech Stack: FastAPI, SQLAlchemy, Pydantic v2, Vue 3, Element Plus, FormCreate (@form-create/designer@^3 + @form-create/element-ui@^3), TypeScript, MinIO 附件上传, pytest
Spec: docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md
V1 审批人选人(已定,不含连续多级主管/表单联系人):
users | oa_roles(含预置 GM/CHAIRMAN)| dept_leader | direct_supervisor(无主管默认回退部门负责人)| initiator_select;节点级或签/会签。
表单底座锁定: FormCreate(Element Plus 版)。不在实现中途再切 VForm3,除非 spike 证明不可用(见 Task 0)。
分阶段交付(建议按序;每阶段可独立验收)
| Phase | 产出 | 验收焦点 |
|---|---|---|
| A | 库表、组织字段、工作流角色、审批人解析器 | 解析单元测试绿 |
| B | 应用/分组/版本 API、发布校验 | 可用 API 创建草稿并发布最小流程 |
| C | 运行时:发起/审批/抄送/条件/公式/联动/关联/附件校验 | pytest + 手工 Postman |
| D | 前端:菜单、角色、应用列表、四步向导壳 | 可点通四步保存 |
| E | 表单设计器 + 流程树 + 高级设置 UI | 对齐钉钉交互 + Mint |
| F | 审批中心全页 + 旧模块下线 + 菜单 SQL | E2E 请假样例 |
File Structure
Backend(新建)
backend/app/api/oa_approval.py— 路由聚合backend/app/schemas/oa_approval.py— Pydanticbackend/app/crud/oa_approval.py— CRUDbackend/app/services/oa_assignee.py— 审批人解析backend/app/services/oa_formula.py— 公式/联动backend/app/services/oa_engine.py— 发起与推进backend/app/services/oa_publish.py— 发布校验backend/tests/test_oa_assignee.pybackend/tests/test_oa_formula.pybackend/tests/test_oa_engine.pydocs/sql/patch_2026-07-14_oa_approval.sql— 增量 DDL + 菜单种子说明- Modify:
backend/app/models/models.py—SysUser.supervisor_user_id、SysDept.leader_user_id、全部Oa*模型 - Modify:
backend/app/api/__init__.py、backend/app/main.py— 注册路由 - Modify: 用户/部门相关 schema·CRUD·前端表单(主管、负责人 ID)
Frontend(新建)
web-admin/src/api/oaApproval.tsweb-admin/src/types/oa-approval.d.ts(或写入api.d.ts命名空间)web-admin/src/views/oa-approval/apps/index.vue— 应用列表web-admin/src/views/oa-approval/apps/designer/index.vue— 四步壳web-admin/src/views/oa-approval/apps/designer/BasicSettings.vueweb-admin/src/views/oa-approval/apps/designer/FormDesigner.vueweb-admin/src/views/oa-approval/apps/designer/FlowDesigner.vueweb-admin/src/views/oa-approval/apps/designer/AdvancedSettings.vueweb-admin/src/views/oa-approval/roles/index.vue— 工作流角色web-admin/src/views/oa-approval/start/index.vue— 发起入口web-admin/src/views/oa-approval/tasks/pending.vue|done.vue|mine.vue|cc.vueweb-admin/src/views/oa-approval/instances/detail.vueweb-admin/src/components/oa-approval/OaAttachmentField.vue— 附件卡片(对齐协议附件)web-admin/src/components/oa-approval/flow-tree/*— 竖向树节点组件- Modify:
web-admin/src/router/modules/sqxp.ts(或独立oa-approval.ts并挂载) - Modify:
web-admin/src/App.vue—panelTitleBarTextRules如需 - Modify: 用户管理、部门管理页 — 主管 /
leader_user_id - Modify: 隐藏旧
system/workflow/*菜单(SQL 或前端 filter)
依赖
web-admin:@form-create/designer@3.5.0、@form-create/element-ui@3.3.1(已安装;Vue3 + Element Plus)
Phase A — 数据与解析
Task 0: FormCreate spike(半天内)
Files:
-
Modify:
web-admin/package.json(确认可安装后再锁版本) -
Step 1: 在
web-admin安装 FormCreate Vue3 + Element Plus 设计器依赖并跑通最小 demo 页(临时路由即可)。
cd web-admin
pnpm add @form-create/element-plus @form-create/designer
# 若包名因官方版本差异调整,以能渲染 designer 为准,并把最终包名/版本写回本计划「依赖」节
pnpm dev
- Step 2: 确认:拖拽单行文本、必填、导出 JSON schema;主题可用 CSS 变量靠拢 Mint(主色替换即可)。
- Step 3: 若不可用,才评估 VForm3;默认不得切换。记录锁定版本到本计划顶部「Tech Stack」。
- Step 4: Commit
git add web-admin/package.json web-admin/pnpm-lock.yaml
git commit -m "$(cat <<'EOF'
chore(web-admin): add FormCreate for OA approval form designer
EOF
)"
Task 1: 组织字段补齐(主管 / 部门负责人用户 ID)
Files:
-
Create:
docs/sql/patch_2026-07-14_oa_approval.sql(本 Task 先写 ALTER 段) -
Modify:
backend/app/models/models.py—SysUser、SysDept -
Modify: 用户/部门的 schema、CRUD、list/detail API、前端表单项
-
Step 1: 在补丁 SQL 增加:
-- 直属主管(发起人的主管)
ALTER TABLE sys_user
ADD COLUMN supervisor_user_id VARCHAR(64) NULL COMMENT '直属主管用户ID' AFTER dept_id;
-- 部门负责人用户ID(发起人部门负责人);保留原 leader 姓名展示可用同步回填
ALTER TABLE sys_dept
ADD COLUMN leader_user_id VARCHAR(64) NULL COMMENT '部门负责人用户ID' AFTER status;
- Step 2: 模型字段与前端:用户编辑增加「直属主管」用户选择器;部门编辑负责人写入
leader_user_id,并同步leader显示名为所选用户昵称(兼容旧列)。 - Step 3: 手工:配张三主管=钱七、部门 A 负责人=赵六,接口返回新字段。
- Step 4: Commit
git commit -m "$(cat <<'EOF'
feat: add supervisor_user_id and dept leader_user_id for OA approval
EOF
)"
Task 2: oa_* 表 DDL + SQLAlchemy 模型
Files:
-
Modify:
docs/sql/patch_2026-07-14_oa_approval.sql -
Modify:
backend/app/models/models.py -
Step 1: 追加表(字段顺序:id → 业务 → 公共字段),至少包括:
oa_app_group, oa_app, oa_app_version,
oa_role, oa_role_user,
oa_instance, oa_task, oa_relation
关键列约定:
oa_role.role_code VARCHAR(64) -- 预置 GM / CHAIRMAN
oa_role.role_kind CHAR(16) -- custom | preset
oa_app_version.status -- draft | published
oa_app_version.form_schema_json / flow_tree_json / advanced_settings_json TEXT/LONGTEXT
oa_instance.initiator_user_id, initiator_dept_id, form_data_json, status
oa_task.assignee_user_id, node_key, task_type(approval|cc), status, comment_text, resolve_source(nullable) -- 如 supervisor_fallback_dept_leader
- Step 2: 在
models.py增加对应Oa*类(命名与表名一致,风格对齐现有Wf*/Sys*)。 - Step 3: 本地执行补丁 SQL,确认表创建成功。
- Step 4: Commit
Task 3: 工作流角色 CRUD + 预置种子
Files:
-
Create:
backend/app/schemas/oa_approval.py(角色段) -
Create:
backend/app/crud/oa_approval.py(角色函数) -
Create:
backend/app/api/oa_approval.py -
Modify:
backend/app/api/__init__.py、backend/app/main.py -
Step 1: API:
GET /api/admin/oa-approval/roles
POST /api/admin/oa-approval/roles
PUT /api/admin/oa-approval/roles/{id}
DELETE /api/admin/oa-approval/roles/{id} # preset → 403
PUT /api/admin/oa-approval/roles/{id}/users body: { userIds: string[] }
POST /api/admin/oa-approval/roles/ensure-presets # 幂等写入 GM/CHAIRMAN
- Step 2:
ensure_presets(tenant_no):若不存在则插入role_kind=preset、role_code in (GM, CHAIRMAN)。 - Step 3: 租户首次访问角色列表时调用 ensure。
- Step 4: 手工验证:删 GM 返回错误;可改成员。
- Step 5: Commit
Task 4: 审批人解析器 + 单元测试
Files:
-
Create:
backend/app/services/oa_assignee.py -
Create:
backend/tests/test_oa_assignee.py -
Step 1: 写失败测试(无实现时)
# backend/tests/test_oa_assignee.py
def test_dept_leader_resolves_by_initiator_dept(db_session, tenant_no):
# 准备 dept A leader=U1, user initiator dept=A
from app.services.oa_assignee import resolve_assignees
cfg = {"assigneeType": "dept_leader"}
users, meta = resolve_assignees(db_session, tenant_no, cfg, initiator_user_id="zhang", initiator_dept_id="deptA", advanced={"supervisorFallbackToDeptLeader": True}, initiator_select_user_ids=None)
assert users == ["leaderA"]
def test_direct_supervisor_fallback_to_dept_leader(db_session, tenant_no):
cfg = {"assigneeType": "direct_supervisor"}
users, meta = resolve_assignees(..., initiator_user_id="no_supervisor", initiator_dept_id="deptA", advanced={"supervisorFallbackToDeptLeader": True}, ...)
assert users == ["leaderA"]
assert meta.get("resolve_source") == "supervisor_fallback_dept_leader"
def test_oa_role_gm(db_session, tenant_no):
cfg = {"assigneeType": "oa_roles", "oaRoleIds": ["<gm_role_id>"]}
users, meta = resolve_assignees(...)
assert "gm_user" in users
def test_never_reads_sys_role(db_session, tenant_no, monkeypatch):
# 仅绑定 sys_role 的用户不应因 assigneeType=oa_roles 被选中
...
- Step 2: 运行
pytest backend/tests/test_oa_assignee.py -v→ 预期 FAIL(import/函数不存在)。 - Step 3: 实现
resolve_assignees(...)返回(list[str] user_ids, dict meta),覆盖五种assigneeType;空结果抛业务错误。 - Step 4: pytest 全绿。
- Step 5: Commit
git commit -m "$(cat <<'EOF'
feat(oa-approval): add assignee resolver with dept leader and supervisor fallback
EOF
)"
Phase B — 应用定义与发布
Task 5: 分组,应用 / 草稿版本 API
Files:
-
Modify:
backend/app/schemas/oa_approval.py、crud/oa_approval.py、api/oa_approval.py -
Step 1: 实现:
CRUD /api/admin/oa-approval/groups
CRUD /api/admin/oa-approval/apps
GET/PUT /api/admin/oa-approval/apps/{id}/draft
# draft 体:basic + formSchemaJson + flowTreeJson + advancedSettingsJson
# 无草稿则从最新 published 克隆或新建 version_no=1 status=draft
- Step 2:
advancedSettingsJson默认:
{
"supervisorFallbackToDeptLeader": true,
"allowWithdrawRunning": true,
"approverDedup": false,
"commentRequired": false,
"allowAddSign": true
}
- Step 3: 字段一致性:
_row_to_dict为可读真源;Create/Update 与之对齐。 - Step 4: Commit
Task 6: 发布校验
Files:
-
Create:
backend/app/services/oa_publish.py -
Modify: API
POST /apps/{id}/publish -
Step 1: 校验规则(失败返回中文错误列表):
- 表单至少 1 个可填字段
- 流程树从发起人可达结束
- 条件分支完整
- 每个审批节点
assigneeType合法且引用非空(dept_leader/direct_supervisor/initiator_select除外)
-
Step 2: 通过后:
status=published,is_latest_published=1,旧 published 取消 latest。 -
Step 3: 单测或手工:缺审批人策略时拒绝发布。
-
Step 4: Commit
Phase C — 运行时引擎
Task 7: 公式与数据联动
Files:
-
Create:
backend/app/services/oa_formula.py -
Create:
backend/tests/test_oa_formula.py -
Step 1: 支持白名单 AST:字段引用、
+ - * /、IF、SUM(明细后续可扩);禁止eval任意代码。 -
Step 2:
apply_formulas_and_linkages(schema, form_data) -> form_data;联动按字段props.linkage规则回填。 -
Step 3: 测试:合法公式;非法表达式抛错。
-
Step 4: Commit
Task 8: 引擎发起 / 推进 / 审批动作
Files:
- Create:
backend/app/services/oa_engine.py - Create:
backend/tests/test_oa_engine.py - Modify:
api/oa_approval.py
API:
GET /api/admin/oa-approval/templates
POST /api/admin/oa-approval/instances
GET /api/admin/oa-approval/instances/{id}
GET /api/admin/oa-approval/tasks/pending|done|mine|cc
POST /api/admin/oa-approval/tasks/{id}/approve
POST /api/admin/oa-approval/tasks/{id}/reject
POST /api/admin/oa-approval/tasks/{id}/add-sign
POST /api/admin/oa-approval/instances/{id}/withdraw
- Step 1:
start_instance:鉴权发起范围 → 锁定 latest published → 校验附件 URL/必填 → 公式 → 写 instance/relations → 沿flow_tree_json推进建 task。 - Step 2:
complete_task:权限校验 → 或签/会签 → 条件分支 → 抄送 → 结束状态。 - Step 3: 附件:
form_data中 attachment 字段为逗号分隔 URL;必填与数量在服务端再验。 - Step 4: 关联审批:写
oa_relation;详情返回可跳转列表。 - Step 5: pytest:直线审批;
dept_leader分部门;主管回退;GM 预置角色。 - Step 6: Commit
Phase D — 前端壳与配置台
Task 9: 路由、菜单、API 模块、工作流角色页
Files:
-
Create:
web-admin/src/api/oaApproval.ts -
Create:
web-admin/src/views/oa-approval/roles/index.vue(对照system/user列表模式) -
Create:
web-admin/src/views/oa-approval/apps/index.vue(对照system/unit) -
Modify: router;
docs/sql/patch_...sql追加sys_menuOA 审批节点 -
Hide: 旧 workflow 菜单
-
Step 1: 菜单:OA审批 → 审批管理 / 工作流角色 / 发起审批 / 待处理 / 已处理 / 我发起的 / 抄送我的。
-
Step 2: 角色页:列表、新增自定义、成员绑定、预置不可删。
-
Step 3: 应用列表:新增 → 进设计器;发布状态 Tag。
-
Step 4: UI:P24 或扩展模式码写进 PR 说明;按钮带图标;状态用
el-tag。 -
Step 5: Commit
Task 10: 四步设计器壳 + 基础设置 + 高级设置
Files:
-
Create:
designer/index.vue、BasicSettings.vue、AdvancedSettings.vue -
Step 1: 顶栏步骤:基础设置 | 表单设计 | 流程设计 | 高级设置;Mint 高亮;预览/发布按钮。
-
Step 2: 基础设置字段对齐钉钉截图(图标、名称 50、分组、说明 100、谁可发起、表单管理员)。
-
Step 3: 高级设置:撤销/去重/意见/加签 + 无直属主管回退部门负责人开关(默认开)。
-
Step 4: PUT draft 自动/手动保存。
-
Step 5: Commit
Phase E — 设计器
Task 11: 表单设计器(FormCreate)
Files:
-
Create:
FormDesigner.vue、OaAttachmentField.vue -
Step 1: 嵌入 FormCreate designer;导出 JSON 存
formSchemaJson。 -
Step 2: V1 控件以 Spec §4.5 为准:分栏、单行/多行/数字/单选/多选/日期/日期区间/说明/电话、图片/金额/附件/部门/省市区/计算公式/关联审批单;不做 AI/手写签名/身份证/联系人/外部联系人/行业通讯录部门;二期不做明细表格/地点/评分/级联。属性含数据联动配置入口。
-
Step 3: 附件组件:即时
fetchUploadAttachment;卡片布局对齐ProtocolFormDialog;值=URL 逗号串。 -
Step 4: 落地 Spec §4.6 兼容契约:共享控件字典(稳定
type)+form_data值类型表(前后端镜像);PC/手机 375 预览;分栏窄屏强制单列。 -
Step 5: 发布校验拒绝未知
type;单测覆盖值类型序列化(多选数组、日期区间对象、附件逗号 URL 等)。 -
Step 6: 自检:无 File/DOM/组件实例进 Schema;部门/省市区/附件语义可映射移动端选择器与上传。
-
Step 7: Commit
Task 12: 竖向流程树设计器
Files:
-
Create:
FlowDesigner.vue、components/oa-approval/flow-tree/* -
Step 1: 数据结构(存
flowTreeJson):
type FlowNode = {
id: string
type: 'start' | 'approval' | 'cc' | 'condition' | 'end'
name: string
approverConfig?: {
assigneeType: 'users' | 'oa_roles' | 'dept_leader' | 'direct_supervisor' | 'initiator_select'
userIds?: string[]
oaRoleIds?: string[]
signMode?: 'or' | 'and' // 或签 | 会签
}
conditionGroups?: { name: string; rules: unknown[]; child: FlowNode }[]
child?: FlowNode | null
}
- Step 2: UI:发起人 →「+」→ 审批/抄送/条件 → 结束;节点抽屉配置五种选人 + 或签会签。
- Step 3: 工作流角色下拉:预置置顶(总经理/董事长)+ 自定义。
- Step 4: 不使用 AntV X6。
- Step 5: Commit
Phase F — 办理中心与收尾
Task 13: 发起 / 待办 / 已办 / 我发起 / 抄送 / 详情
Files:
-
Create:
start/index.vue、tasks/*.vue、instances/detail.vue -
Step 1: 发起:分组模板宫格 → 动态渲染 form schema → 附件上传 → 自选人 → 提交。
-
Step 2: 列表页:标准 list 骨架;状态
el-tag。 -
Step 3: 详情:表单只读、附件预览下载、进度时间轴、回退来源文案、关联审批跳转、同意/拒绝/加签/撤销。
-
Step 4: 对照 unit/order 模板页与 Fresh Mint 规范。
-
Step 5: Commit
Task 14: 用户/部门前端联调、旧工作流下线、图谱与基线
Files:
-
Modify: 用户、部门页
-
Modify: 菜单 SQL / 路由隐藏
system/workflow -
Run:
./scripts/update-graph.sh all(有新建代码时) -
Optional: 重导
docs/sql/zkxw.sql或保留 patch 为权威增量 -
Step 1: 用户可选直属主管;部门负责人写
leader_user_id。 -
Step 2: 旧工作流菜单隐藏或提示「请使用 OA审批」。
-
Step 3:
python scripts/check_field_consistency.py(若脚本覆盖新实体)。 -
Step 4:
./scripts/update-graph.sh backend && ./scripts/update-graph.sh frontend -
Step 5: E2E 手工清单(对应 Spec §10):
- 发布含条件+抄送的请假应用
- A/B 部门
dept_leader - 无主管回退部门负责人
- GM/董事长预置角色
- 附件上传与详情下载
- 公式/联动不被前端篡改
- 关联审批跳转
-
Step 6: Commit
Spec 覆盖自检
| Spec 要点 | Task |
|---|---|
| 四步配置 + Mint | 10–12 |
| FormCreate 表单 + 附件 MinIO | 0, 11 |
| Web/移动兼容契约(§4.6:同 Schema/form_data/校验/控件字典) | 6, 11, 8 |
| 竖向流程树(非 X6) | 12 |
| oa_role + GM/CHAIRMAN | 3, 12 |
| dept_leader / direct_supervisor + 默认回退 | 1, 4, 8, 10 |
| 公式/联动/关联审批 | 7, 8, 11, 13 |
| 审批中心六页能力 | 13 |
| 版本锁定 / 发布校验 | 5, 6, 8 |
| 不对接业务工单 / 不做 AI | 范围外,无 Task |
| 旧 wf 下线 | 14 |
| 租户隔离 | 全部 API CRUD |
未纳入 V1(明确): 连续多级主管、表单内联系人、SQL 任意审批人、打印模板、自动化任务。
风险备忘
leader历史字符串与leader_user_id双写:部门保存时同步。- FormCreate 样式侵入:用业务包裹 class
oa-fc-mint覆盖主色。 - 流程树条件 UI 复杂度:先支持简单规则列表(字段/op/value),与引擎 JSON 一致即可。