> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked. # Fonrey 权限管理技术方案 **版本**: 2.1 **项目**: Fonrey 房产经纪管理系统 **技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL 16 + Redis **关联 PRD**: `PRD/权限管理/权限管理模块PRD.md` **关联数据模型**: `DATA_MODEL/DATA_MODEL_PERMISSION.md`(本方案不重复 DDL) **关联契约规范**: `TECH_STACK/API_CONTRACT.md`(全局 API 契约权威) **最后更新**: 2026-04-27 --- ## 变更历史 | 日期 | 变更人 | 变更内容 | |---|---|---| | 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) | ## 一、文档定位与边界 本文件仅定义权限模块的: 1. RBAC + 个人覆盖架构边界 2. 权限解析/合并与门禁约束 3. API 端点设计(页面 / HTMX / JSON) 4. 缓存一致性与审计策略 5. 错误码与测试映射 > 不在本文件展开权限相关表字段与索引。数据结构以 `DATA_MODEL_PERMISSION.md` 为唯一权威。 --- ## 二、范围定义(以 P0 为准) ### 2.1 P0 必须覆盖 - 角色管理(创建/编辑/停用) - 角色权限配置(BOOLEAN/SCOPE/INTEGER) - 人员绑定角色 - 人员权限覆盖(稀疏覆盖) - 权限快照解析与缓存失效 - 业务接口统一门禁能力 ### 2.2 预留(非本期强制) - 权限变更审批流 - 临时授权(时效权限) - 跨租户角色模板市场 --- ## 三、模块架构边界 ## 3.1 模块职责(`apps/permission`) - 权限定义读取与检索 - 角色权限配置与人员绑定 - 权限快照解析、缓存、失效 - 门禁 Decorator / Mixin 输出 - 权限变更审计写入 ## 3.2 核心合并规则 多角色叠加采用最宽松原则: | 值类型 | 合并规则 | |---|---| | BOOLEAN | OR(任一为 true 则 true) | | SCOPE | 取最大范围(self < group < store < area < region < company) | | INTEGER | 取最大值,`0` 表示不限制 | 解析优先级:`个人覆盖 > 角色合并 > 默认值` ## 3.3 外部依赖 | 依赖模块 | 用途 | |---|---| | `apps/org` | 员工状态、组织路径、`is_system_admin` 短路 | | `core/cache.py` | 权限快照缓存与版本控制 | | 各业务模块 | 调用门禁函数校验权限 | --- ## 四、API 设计原则 1. PermissionDef 以只读为主,角色与覆盖可编辑。 2. 权限面板按模块分片加载,避免一次渲染超大集合。 3. 任何权限写操作后必须触发缓存失效。 4. 全部变更强制写审计(前后值、操作者、来源)。 5. API 统一结构化错误码,禁止透出内部异常栈。 --- ## 五、端点清单(核心) ## 5.1 页面路由(SSR) | 路径 | 方法 | 权限 code | 说明 | |---|---|---|---| | `/permission/roles/` | GET | `system.role.manage.allow` | 角色列表页 | | `/permission/roles/{role_id}/` | GET | `system.role.manage.allow` | 角色详情与配置页 | | `/permission/staff/` | GET | `system.role.manage.allow` | 人员权限列表页 | | `/permission/staff/{staff_id}/` | GET | `system.role.manage.allow` | 人员权限详情页 | ## 5.2 HTMX 片段端点 | 路径 | 方法 | 用途 | 返回 | |---|---|---|---| | `/permission/fragments/role-table/` | POST | 角色筛选局刷 | HTML 片段 | | `/permission/fragments/role-permissions/{role_id}/` | GET | 角色权限局刷 | HTML 片段 | | `/permission/fragments/staff-permissions/{staff_id}/` | GET | 人员有效权限矩阵局刷 | HTML 片段 | ## 5.3 JSON API(P0) | 端点 | 方法 | 权限 code | 说明 | |---|---|---|---| | `/api/permission/defs/query/` | POST | `system.role.manage.allow` | 权限定义检索 | | `/api/permission/roles/` | POST | `system.role.manage.allow` | 新增角色 | | `/api/permission/roles/{id}/` | PATCH | `system.role.manage.allow` | 编辑角色 | | `/api/permission/roles/{id}/permissions/batch-upsert/` | POST | `system.role.manage.allow` | 批量保存角色权限 | | `/api/permission/staff/{staff_id}/roles/assign/` | POST | `system.role.manage.allow` | 人员绑定角色 | | `/api/permission/staff/{staff_id}/overrides/batch-upsert/` | POST | `system.role.manage.allow` | 批量保存人员覆盖 | | `/api/permission/staff/{staff_id}/effective/` | GET | `system.role.manage.allow` | 查询有效权限快照 | | `/api/permission/cache/invalidate/` | POST | `system.role.manage.allow` | 手动失效缓存 | --- ## 六、关键 API 规范(请求/响应) ## 6.1 批量保存角色权限 `POST /api/permission/roles/{id}/permissions/batch-upsert/` ```json { "items": [ {"code": "property.list.view.scope", "value": {"v": "store"}}, {"code": "property.create.allow", "value": {"v": true}}, {"code": "property.owner_phone.view.daily_limit", "value": {"v": 50}} ] } ``` ## 6.2 批量保存人员覆盖 `POST /api/permission/staff/{staff_id}/overrides/batch-upsert/` ```json { "items": [ {"code": "property.list.view.scope", "value": {"v": "self"}}, {"code": "property.owner_phone.view.daily_limit", "value": {"v": 20}} ] } ``` 规则: - 仅存储与角色合并值不同的条目 - 若覆盖值回到合并值,应删除覆盖记录 --- ## 七、HTMX 交互约定 ## 7.1 Header 约定 - 请求头:`HX-Request: true` - 成功提示:`HX-Trigger` toast(success) - 失败提示:`HX-Trigger` toast(error) ## 7.2 模板分片命名 - `templates/permission/fragments/role_table.html` - `templates/permission/fragments/role_permissions_panel.html` - `templates/permission/fragments/staff_permissions_panel.html` --- ## 八、权限与数据范围 ## 8.1 门禁函数标准 - `permission_required(code)`(BOOLEAN) - `scope_required(code, min_scope)`(SCOPE) - `limit_required(code, required_value)`(INTEGER) ## 8.2 强约束 - 禁止业务模块直接查权限表自行判定 - `is_system_admin=true` 可短路,但仍需落审计 --- ## 九、异步任务与缓存策略 ## 9.1 异步任务(可选) | 任务 | 触发时机 | 说明 | |---|---|---| | `permission_rebuild_snapshot_task` | 批量角色变更后 | 后台重建快照(大租户优化) | ## 9.2 Redis Key 规范 | Key | TTL | 说明 | |---|---|---| | `{schema}:perm:v{version}:staff:{staff_id}` | 1800s | 员工权限快照 | | `{schema}:perm:version` | 永久 | 快照版本号 | | `{schema}:perm:role:{role_id}:staff_ids` | 1800s | 角色关联员工索引 | --- ## 十、性能与可靠性约束 - 权限快照查询目标:`p95 < 30ms`(命中缓存) - 角色保存后传播目标:`< 3s`(含缓存失效) - 大范围权限调整采用版本号惰性失效,避免全量删 key 风暴 --- ## 十一、安全与合规 1. 权限台仅授权管理员访问。 2. 每次变更必须记录前后值 JSON、操作者、IP、时间。 3. 系统预置关键角色只允许停用,不允许物理删除。 4. 不允许直接向前端暴露无权访问权限项明细。 --- ## 十二、错误码建议 | code | HTTP | 场景 | |---|---|---| | `PERMISSION_ROLE_NOT_FOUND` | 404 | 角色不存在 | | `PERMISSION_DEF_NOT_FOUND` | 404 | 权限项不存在 | | `PERMISSION_VALUE_TYPE_MISMATCH` | 400 | 值类型不匹配 | | `PERMISSION_SCOPE_INVALID` | 400 | scope 非法 | | `PERMISSION_DENIED` | 403 | 无访问权限 | --- ## 十三、测试映射(P0) | 场景 | 最低覆盖 | |---|---| | 多角色合并 | BOOLEAN/SCOPE/INTEGER 合并正确 | | 个人覆盖 | 覆盖优先级正确,冗余覆盖可清理 | | 缓存一致性 | 写后失效,读取最新快照 | | 门禁三态 | 200 / 403 / 302 覆盖完整 | 测试文件:`tests/integration/permission/test_us_permission.py` --- ## 十四、落地顺序建议 1. 先实现 Resolver + Cache 能力 2. 再做角色与角色权限编辑 3. 再做人员绑定与覆盖配置 4. 最后统一替换业务模块门禁调用 --- ## 十五、文档同步规则 - 权限数据结构变更:同步 `DATA_MODEL_PERMISSION.md` - 新增权限 code:同步 `DATA_MODEL/ENUMS.md` 与权限种子 - API 变更:同步本文件与权限 PRD 验收条目