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

4.4 KiB
Raw Permalink Blame History

CMA 账套模式开发规范(2026-08-11 定稿)

依据:研学Bot《cma-account-mode-research-20260811.md》+ 全栈Bot落地改造。 适用范围:cma-management 仓库全部后端/前端开发。


1. 铁律:新表必须带 entity_id

任何新增业务数据表(租户表)必须包含 entity_id,用于账套隔离。

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_idnullable=False + index
  • 查询接口默认按 entity_id 过滤(通过 Depends(get_entity_id) 或显式参数)
  • 写入接口强制使用当前 token 绑定的 entity_id(禁止客户端传入)
  • 存量表缺 entity_id 的,补列并回填默认值后设为 NOT NULL

2. 账套解析规则(后端)

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. 验证要点(每次改动后)

# 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