Files
zkxw/docs/superpowers/specs/2026-08-04-oa-assignee-posts-replace-roles-design.md
T
zlwuandCursor 06db3bc966 feat(permission): replace OA roles with post-bound roles and assignee posts
Unify effective roles via post-role bindings, remove dedicated OA role UI, and update OA assignee resolution plus related SQL/docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 17:41:04 +08:00

123 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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": ["<sys_post.id>"],
"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` 残留(除历史设计文档归档说明外)