Files
cma-management/docs/account-mode-dev-spec.md
T

97 lines
4.4 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.
# CMA 账套模式开发规范(2026-08-11 定稿)
> 依据:研学Bot《cma-account-mode-research-20260811.md》+ 全栈Bot落地改造。
> 适用范围:cma-management 仓库全部后端/前端开发。
---
## 1. 铁律:新表必须带 entity_id
**任何新增业务数据表(租户表)必须包含 `entity_id` 列**,用于账套隔离。
```python
class 新模型(Base):
__tablename__ = "xxx"
id = Column(Integer, primary_key=True, index=True)
entity_id = Column(Integer, ForeignKey("entities.id"), nullable=False, index=True, comment="企业ID(账套)")
# ... 业务字段
```
### 1.1 全局表 vs 租户表归类
| 类型 | 说明 | 示例 | entity_id |
|------|------|------|:---:|
| **全局表** | 系统级、跨账套共享 | users / entities / user_entities / role_permissions / system_configs / notification_channels / kpi_templates / okr_templates / bi_report_templates | ❌ 不带 |
| **租户表** | 业务数据、按账套隔离 | kpi_definitions / kpi_values / alerts / data_source_config / objectives / action_plans / org_nodes / budget_plans / cost_* / bsc_layer_config / kpi_hierarchy / cash_* / mpm_results / bi_reports / kpi_causality / report_history | ✅ **必须带** |
**判断口诀**:数据属于"某个企业/账套" → 租户表带 entity_id;数据属于"系统/平台" → 全局表。
### 1.2 新表检查清单
- [ ] 建表 SQL / ORM 模型包含 `entity_id``nullable=False` + index
- [ ] 查询接口默认按 entity_id 过滤(通过 `Depends(get_entity_id)` 或显式参数)
- [ ] 写入接口强制使用当前 token 绑定的 entity_id(禁止客户端传入)
- [ ] 存量表缺 entity_id 的,补列并回填默认值后设为 NOT NULL
---
## 2. 账套解析规则(后端)
```text
get_entity_id 解析链(backend/app/deps.py):
1. 有 Bearer token(登录用户)→ 强制使用 token 绑定的 entity_id(忽略 query/header/body,防越权)
2. 无 tokenBot服务通道白名单)→ query → header → body,校验 entity 状态 = active
3. 兜底默认 1(酣客)
```
### 2.1 API 开发约定
- **业务 API**:一律使用 `entity_id: int = Depends(get_entity_id)` 获取当前账套,禁止自己解析 query 参数
- **禁止**在业务 API 里让客户端传 `entity_id` 来指定账套(token 绑定才是唯一来源)
- 需要"某账套数据"的 Bot/服务通道:无 token 时通过 query/header 传 entity_id,后端校验 `Entity.status == "active"`
### 2.2 用户授权
- 用户可访问哪些账套 = `user_entities` 表(user_id ↔ entity_id 多对多)
- 登录/切换时必须校验 `user_has_entity(db, user_id, entity_id)`,未授权返回 **403**
- 新用户注册默认授权 entity_id=1(酣客)
---
## 3. 账套切换(前端)
- 切换走 `POST /api/cma/auth/switch-entity` → 后端校验授权 → **重新签发 token**(绑定新 entity)→ 整页刷新
- **禁止**直接改 `localStorage['cma_entity_id']` 切换(token 未变会导致数据错乱)
- 拦截器**不自动附加** `X-Entity-Id` / `entity_id`(账套由 token 绑定,删除自动附加逻辑)
- 登录页公司下拉数据源:`GET /api/cma/auth/login-entities?username=xxx`(按授权过滤)
---
## 4. 安全红线
1. 任何请求携带的 `entity_id` 与 token 绑定不一致时,**以 token 为准**(忽略 query/header
2. 未授权账套访问返回 403(登录、切换、业务 API 三层都校验)
3. Redis token 存储结构 = `JSON {user_id, entity_id}`;旧 int 格式 token 一律视为无效(强制重新登录)
4. Bot 通道(X-BOT-KEY)不走用户 token,靠 query/header 指定账套时仍须校验 entity active
---
## 5. 验证要点(每次改动后)
```bash
# 1. 隔离验证:登录账套A → 全系统只显示A的数据
curl -s -X POST http://127.0.0.1:8010/api/cma/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123","entity_id":2}'
# → token;用该token访问 /api/cma/kpis 只返回博海数据
# 2. 越权验证:未授权entity请求返回403
curl -s -X POST http://127.0.0.1:8010/api/cma/auth/switch-entity \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"entity_id":999}' # → 403
# 3. Bot通道:无token带query entity_id 仍可用
curl -s -H "X-BOT-KEY: cma-bot-finance-2026" \
"http://127.0.0.1:8010/api/cma/bot/overview" # → 200
```