# OA 审批人选:工作流角色 → 职位(硬切换) **日期:** 2026-08-04 **范围:** 去掉 `oa_role` / `oa_role_user` 与【工作流角色】模块;流程审批人/抄送改为选【职位】,运行时按 `sys_user_post` 展开成员。 **方案:** 方案 1 — `assigneeType: "posts"` + `postIds` ## 已确认决策 | 项 | 结论 | |----|------| | 历史流程 | 系统未上线,无历史 `flow_tree` → **硬切换**,不做兼容层 | | 预置 GM/CHAIRMAN | **不做**;设计器普通多选启用职位 | | 下线范围 | **彻底去掉**:菜单/路由/管理页/API/`fetchOaRole*` | | 数据表 | **一并 DROP** `oa_role_user`、`oa_role`(增量 SQL) | | 审批人数据源 | **仅** `sys_user_post`(职位成员);**禁止**用 `sys_post_role` / `sys_role` | ## 目标模型 ``` 流程节点 approverConfig assigneeType / assigneeTypes: "posts" postIds: [sys_post.id, ...] │ ▼ sys_user_post(启用职位 + 启用用户) │ ▼ 待办 / 抄送人 userId 列表 ``` 与权限链分离: ``` 职位 → sys_post_role → sys_role → 菜单/数据权限 (登录 RBAC,本需求不改) 职位 → sys_user_post → 用户 (本需求:审批人选) ``` ## Flow JSON(新约定) 审批节点示例: ```json { "assigneeType": "posts", "postIds": [""], "signMode": "or" } ``` 抄送节点:`assigneeTypes` 可含 `"posts"`(与 `users` / `dept_leader` 等组合),成员来自同一套 `postIds`(或按现抄送结构对每种类型分字段;实现时与现 `oa_roles` 写法一一对应替换)。 **删除字段/枚举:** `oa_roles`、`oaRoleIds`、`oaRoleCodes`、`oa_role_ids`、`oa_role_codes`。 ## 后端 ### 新增/改造 | 位置 | 变更 | |------|------| | `crud/user.py` 或 `crud/post.py` | `list_user_ids_bound_to_posts(db, post_ids, tenant_no)`:多职位展开用户,过滤职位 `status=1`/`del_flag=0`、用户启用 | | `oa_assignee.py` | `assigneeType == "posts"` 分支;`resolve_source: "posts"`;删除 `oa_roles` 分支 | | `oa_publish.py` | 校验 `posts` 时 `postIds` 非空;引用校验查 `SysPost`;删除 `validate_oa_roles_referenced` | | `oa_engine.py` | 展示标签改职位名称映射;删除 `OaRole` 查询 | | `oa_approval.py` API | 删除整个工作流角色路由段 | | `crud/oa_approval.py` | 删除 `PRESET_ROLES` / `ensure_presets` / 角色 CRUD / `list_oa_role_user_ids` | | `models.py` | 删除 `OaRole`、`OaRoleUser`(与 drop SQL 同步) | ### SQL 创建 `docs/sql/drop_oa_role.sql`: ```sql DROP TABLE IF EXISTS `oa_role_user`; DROP TABLE IF EXISTS `oa_role`; ``` 菜单:软删或删除「工作流角色」菜单项(`oa:roles:list`),与项目菜单维护方式一致。 ### 错误语义 - 发布:未选职位 / 职位不存在或停用 → 明确错误 - 运行:职位下无有效用户 → 与现「工作流角色成员为空」同等失败(`AssigneeResolveError`) ## 前端 | 位置 | 变更 | |------|------| | `FlowDesigner.vue` | 选项「工作流角色」→「职位」;`fetchPostList` 加载(仅 `status==='1'`);`postIds` 多选 | | `flow-tree/types.ts` | `AssigneeType` 含 `posts`;去掉 `oa_roles`;`summarizeApprover` 文案「职位」 | | `OaProgressChain.vue` | `posts` →「职位」 | | 删除 | `views/oa-approval/roles/index.vue`;路由 `oa-approval/roles`;`oaApproval.ts` 中全部 `fetchOaRole*` | | 保留 | `OaPostSelect.vue`(表单控件,与审批人选独立) | ## 文档与测试 - 更新钉钉式 OA 设计文档中「工作流角色」章节为「职位」 - 需求/OpenSpec 若提及 OA 独立角色作审批人,改为职位 - 单测:`test_oa_assignee.py` 改为 posts;去掉「不读 sys_role」之外的 oa_role mocks;`test_oa_publish.py` 同步 - `./scripts/update-graph.sh backend`(及 frontend) ## 非目标 - 职位权限(`sys_post_role`)行为不变 - 不种子 GM/CHAIRMAN 职位 - 不做旧 JSON 兼容读取 - 不改审批人其它类型:`users` / `dept_leader` / `direct_supervisor` / `initiator_select` ## 实现顺序(建议) 1. 后端:`list_user_ids_bound_to_posts` + assignee/publish/engine 改 `posts` 2. 前端:FlowDesigner + types + 进度文案 3. 删除角色模块(页/路由/API/CRUD/模型) 4. drop SQL + 菜单清理 + 文档/单测 5. 图谱更新 ## 验收 1. OA 菜单无「工作流角色」 2. 流程设计审批/抄送可选职位并保存 `posts`/`postIds` 3. 职位已关联用户时,发起后待办落到这些用户 4. 空成员职位无法正常完成解析(错误可读) 5. 代码库无 `oa_roles` / `fetchOaRole` / `OaRole` 残留(除历史设计文档归档说明外)