# Hermes、本体解析与财务报告多租户安全 概念文档

更新时间：2026-07-17

## 功能一句话

让本体解析、员工行为画像、Hermes 扫描、数字员工看板和定时财务报告从请求到存储全程绑定可信企业，并在任何租户上下文缺失或冲突时停止运行。

## 背景与问题

本体解析曾在建立 `AgentRun` 前调用模型，并从全局员工、组织、客户、供应商、项目和单据字典构造提示词；员工画像详情可按任意员工 ID 查询；Hermes 扫描、看板和财务报告存在全表读取、全局收件人以及跨企业复用存储路径的风险。内部 Orchestrator 调用还可在没有认证用户时继续运行，无法证明 `AgentRun` 的企业归属。

## 目标与非目标

### 目标

- HTTP 入口只信任 `CurrentUserContext.tenant_id`，内部任务只接受显式、已注册且启用的 `trusted_tenant_id`。
- 本体解析在任何模型调用前创建租户化 `AgentRun`，商业运行上下文和失败证据可追溯。
- 员工、组织、费用、应收、应付、画像、风险、提醒和看板查询在首个 SQL 中限定租户。
- 员工画像仅允许本人、直属领导、财务/高管或管理员读取；越权和跨租户统一返回 404。
- 财务报告按租户配置收件人、生成内容、存储文件和幂等运行账本，不使用全局邮件回退。
- 所有 Hermes 定时任务逐个枚举 `status=active` 的企业，运行记录同时在 route/ontology 保存租户快照。

### 非目标

- 不提供客户端选择或覆盖租户的兼容参数。
- 不实现跨企业合并分析、集团穿透报表或平台运营后台。
- 不在本切片中重构预算分配等仍属旧模型的全部业务域。

## 信任边界

```text
HTTP 请求 ── CurrentUserContext.tenant_id ─┐
                                            ├─→ tenant-scoped AgentRun
内部任务 ── active trusted_tenant_id ──────┘       ├─ route_json.tenant_id
                                                    └─ ontology_json.tenant_id
```

- 客户端 `context_json.tenant_id` 会被可信租户覆盖，不能参与授权。
- 同时传入登录用户和内部租户时，两者必须一致。
- 内部任务租户必须存在于租户注册表且状态为 active；缺失、停用或不存在均在创建 Run 前拒绝。
- 后台消费者从数据库 Run 恢复租户，并校验 route/ontology 两份快照一致。

## 功能设计

### 本体解析

- 接口覆盖请求中的 user id，并把认证企业传给 `SemanticOntologyService`。
- `parse()` 先建立 `running/pending_model_analysis` Run，再加载租户字典、商业运行上下文并调用模型。
- 模型、规则降级和失败路径都更新同一 Run；失败不会留下无企业、无状态的调用。
- 员工、部门、费用申请、应收、应付和项目参考目录均以租户作为第一层 SQL 条件。

### 员工画像

- 快照保存 `tenant_id`，并用 `(tenant_id, subject_id)` 复合外键绑定员工。
- Profile Service 的快照、费用单和 Agent Run 查询均先限定租户。
- 详情接口先在当前企业解析目标员工，再执行本人/直属领导/财务/高管/管理员访问矩阵。
- 附带 `claim_id` 时必须同时属于目标员工和当前企业。

### Hermes 与数字员工

- 风险扫描、画像扫描、风险线索、提醒扫描和看板均接受显式租户。
- 调度器只枚举 active 企业，并为每个企业独立创建任务与结果。
- 看板只统计 route/ontology 租户快照均匹配的 Run；缺少或冲突快照的历史记录不会被猜测归属。
- 提醒任务的费用单、员工和关联报销查询在首个 SQL 限制当前企业。

### 财务分析报告

- `TenantFinanceReportConfig` 保存企业启停状态和经校验的收件人。
- `TenantFinanceReportRun` 以 `(tenant_id, idempotency_key)` 防止同企业同周期重复发送。
- 报告上下文只读取当前企业的费用、风险、画像和 Agent Run。
- 邮件发送只使用当前企业配置；未配置、停用或无有效收件人时 fail-closed。
- 文件存储目录使用企业标识哈希，避免路径注入和跨企业文件覆盖。

## 数据与迁移

`20260717_0028_hermes_ontology_tenant_security.py` 完成：

- 员工行为画像、Hermes 任务配置/日志和风险报告新增租户列、索引、唯一约束和复合外键。
- 新增租户财务报告配置和周期运行账本。
- 旧记录回填为 `default`，再移除 server default，后续写入必须显式确定企业。
- 仅支持 PostgreSQL；upgrade/downgrade 在任何 DDL 前检查方言，其他数据库直接拒绝。
- downgrade 在存在非默认企业事实或报告账本时拒绝有损回滚。

## 降级与回滚

- 租户缺失、停用、不存在或上下文冲突：不创建 Run、不查询业务数据。
- 跨企业员工、画像、费用单：统一按不存在处理。
- 企业未配置报告收件人：保留失败/跳过证据，不回退环境变量中的全局地址。
- 历史 Run 没有双租户快照：看板不纳入企业统计。
- 生产升级前必须在数据库备份副本演练 `0027 → 0028 → 0027 → 0028`。

## 测试与验收

- 两企业本体目录、画像 IDOR、Hermes 扫描、风险线索、看板和报告内容/收件人/路径/幂等隔离。
- Orchestrator 缺租户、停用租户、不存在租户和认证/内部租户冲突均在 Run 前拒绝。
- 本体模型调用前存在 running Run，失败后仍保存同企业失败证据。
- PostgreSQL 完成 fresh `base → 0028`、`0028 → 0027 → 0028` 及旧表回填/外键验证。
- 所有后端测试和 Ruff 均在 `local-x-financial-linux` 容器内执行，单命令不超过 60 秒。

## 本轮实现记录

- 2026-07-17：完成本体解析前置 Run、可信商业上下文和租户参考目录。
- 2026-07-17：完成员工画像模型、服务和 API 的租户隔离与访问矩阵。
- 2026-07-17：完成 Hermes 扫描、提醒、看板、调度和财务报告的逐租户运行。
- 2026-07-17：完成 `20260717_0028` PostgreSQL 迁移、反向回滚和安全回归。
