# COS 源文件接入检查与改造方案

更新时间：2026-06-08 02:19 CST

## 0. 当前落地状态

已完成第一版接入并上线：

- `studio-cloud-bridge` 新增 `POST /v1/cos/put-object`，支持受控 base64 上传，返回 bucket、region、key、etag、contentType、byteSize、sha256。
- 云端 COS 前缀白名单已加入 `finance/invoices/`、`finance/contracts/`、`finance/proofs/`、`finance/sources/`。
- 财务系统新增 `finance_archive_source_file` MCP-like 工具；机器人可先把发票/合同/凭证源文件上传到 COS，再把返回的 attachment 元数据放入报销草稿。
- `attachment` 表已非破坏性增加 COS 元数据字段：`storage_provider/storage_bucket/storage_region/storage_key/content_type/file_size/sha256/etag`。
- 云端已执行迁移，回滚式数据库插入验证通过；没有创建正式报销测试单据。
- 线上 smoke 已验证：财务工具 → bridge → 腾讯 COS 上传 → 下载校验 → 删除测试对象，全链路通过。
- 已新增 `FINANCE_API_TOKEN` 鉴权：`/health` 与 `/mcp/tools` 保持公开诊断；`/mcp/call`、报销读取和写入型 API 需要 Bearer Token。无 token / 错 token 均返回 401，finance profile `.env` 已保存所需访问参数。
- 已在 finance profile 单独安装本地 skill `company-finance-cos-reimbursement`；其他 profile 暂未安装/开放。

## 1. 当前系统位置与线上状态

当前财务报销/走账系统 source of truth：

```text
本地项目：/Users/bot1/Volumes/root_for_ai/AI工作区/公司财务_业务梳理_报销走账系统重构_20260603_2006
云端入口：https://wwyl.yipeng.online/finance-reimbursement/
健康检查：https://wwyl.yipeng.online/finance-reimbursement/health
云端服务：company-finance.service
云端路径：/srv/company-finance/current
数据库：PostgreSQL / company_finance
```

本次只检查和规划，不在本文写入任何数据库密码、COS SecretId/SecretKey、API Token。

## 2. 已验证现状

- 项目索引显示旧财务平台已被本项目取代；当前系统为 `active`。
- 本地后端语法检查通过：`npm run check`。
- 云端服务 `company-finance.service`：active / enabled。
- 线上健康检查通过，数据库启用 PostgreSQL。
- 云端当前核心数据量：主体 4、项目 3、草稿 0、报销单 0、报销明细 0、发票 0、附件 0、审计日志 0。
- 当前 MCP-like 工具数量 13，包含报销草稿、校验、预览、提交、查询、COS 源文件归档。
- 当前 `studio-cloud-bridge` 已接入腾讯云 COS：bucket `studio-cloud-1257633934`，region `ap-nanjing`，真实 smoke-tests 上传/下载/签名/删除已通过。

## 3. 当前系统能做什么

已经具备报销闭环骨架：

1. `finance_get_intake_rules`：查询入库字段规则。
2. `finance_validate_intake_draft`：校验报销草稿。
3. `finance_prepare_reimbursement`：创建报销草稿。
4. `finance_preview_intake`：生成预览。
5. `finance_commit_draft`：用户确认后入库为正式报销单。
6. `finance_search_records(record_type="reimbursement")`：查询报销记录。

数据库已包含：

- `attachment`：附件引用；
- `invoice`：发票结构化信息；
- `invoice_link`：发票与报销明细关联；
- `reimbursement_order` / `reimbursement_item`：报销单与明细；
- `audit_log`：提交审计。

## 4. 主要问题 / 缺口

### 4.1 附件表只是“引用”，没有源文件持久化

当前 `attachment` 只有：

```text
file_name
file_path
file_token
attachment_type
```

它能记录“有一个文件/凭证”，但没有：

- COS bucket；
- COS object key；
- content type；
- file size；
- sha256；
- 上传状态；
- 源文件来源；
- 签名下载 URL 或下载接口。

所以现在如果用户发票文件只在 Feishu 消息/临时缓存里，系统不能保证长期可追溯。

### 4.2 报销工具还不能直接接收和上传源文件

`finance_prepare_reimbursement` 支持 `attachments[]` 和 `items[].invoice.file_attachment`，但目前只是把 `file_name/file_path/file_token` 写入数据库。

还缺一个明确工具（已在第一版实现）：

```text
finance_archive_source_file
```

用于接收发票/合同/付款凭证源文件，上传到 COS，并返回可入库的附件对象。

### 4.3 COS 目录规范未落地

需要固定对象 key 规范，避免后续发票/合同散落。

建议：

```text
finance/
  invoices/YYYY/MM/<invoice_id_or_invoice_no>/<sha8>__<safe_original_filename>
  contracts/YYYY/MM/<contract_or_project>/<sha8>__<safe_original_filename>
  proofs/YYYY/MM/<reimbursement_or_payment>/<sha8>__<safe_original_filename>
```

如果一开始没有发票号，可以先用 `draft_id` / 时间戳，commit 后再通过数据库关系追溯；不建议在 COS 里改名/移动旧对象。

### 4.4 当前桥服务只提供签名下载，不提供后端上传 API

`studio-cloud-bridge` 当前公开能力：

```text
POST /v1/cos/sign-download
```

服务内部已经有 `putText/getBuffer/deleteObject/getSignedDownloadUrl` 能力，但 API 层还没有：

```text
POST /v1/cos/put-object
POST /v1/cos/delete-object   # 可选，仅管理/测试用，不开放给普通报销流程
```

财务系统应优先复用 `studio-cloud-bridge`，避免在多个服务里重复保存 COS Secret。

### 4.5 合同源文件还没有对应业务表

当前一期更偏“报销闭环”，没有 `contract` / `commercial_order` 表。合同文件可以先作为附件保存到 COS，但要做到“合同源文件 + 合同款/应收/应付”完整闭环，建议补：

- `contract` 或 `source_document` 表；
- 合同与 project / receivable / payable 的关联表；
- 合同文件与 `attachment` 的关系。

## 5. 推荐改造方案

### 5.1 架构选择

采用“财务系统调用公共 COS 桥服务”的方式：

```text
Hermes/Finance Agent
  -> finance-reimbursement API / MCP 工具
    -> company-finance 后端
      -> studio-cloud-bridge COS API
        -> Tencent COS bucket
      -> PostgreSQL company_finance
```

好处：

- COS 永久密钥只放在 `studio-cloud-bridge`；
- 财务系统只持有 bridge 内部 token；
- 以后其他系统也能复用同一 COS 桥；
- 下载也通过签名 URL，不公开 bucket。

### 5.2 数据库最小迁移

建议先非破坏性扩展 `attachment`：

```sql
ALTER TABLE attachment
  ADD COLUMN IF NOT EXISTS storage_provider TEXT,
  ADD COLUMN IF NOT EXISTS storage_bucket TEXT,
  ADD COLUMN IF NOT EXISTS storage_region TEXT,
  ADD COLUMN IF NOT EXISTS storage_key TEXT,
  ADD COLUMN IF NOT EXISTS content_type TEXT,
  ADD COLUMN IF NOT EXISTS file_size BIGINT,
  ADD COLUMN IF NOT EXISTS sha256 TEXT,
  ADD COLUMN IF NOT EXISTS source_kind TEXT,
  ADD COLUMN IF NOT EXISTS source_ref TEXT,
  ADD COLUMN IF NOT EXISTS upload_status TEXT DEFAULT 'UPLOADED';

CREATE INDEX IF NOT EXISTS idx_attachment_storage_key ON attachment(storage_provider, storage_key);
CREATE INDEX IF NOT EXISTS idx_attachment_sha256 ON attachment(sha256);
```

此迁移不删除/覆盖旧字段，旧 `file_path/file_token` 继续保留兼容。

### 5.3 新增工具

建议新增 MCP-like 工具（第一版已实现为 `finance_archive_source_file`）：

```text
finance_archive_source_file
```

输入：

```json
{
  "file_name": "invoice.pdf",
  "content_base64": "...",
  "content_type": "application/pdf",
  "source_kind": "invoice|contract|payment_proof|other",
  "target_type": "draft|reimbursement_order|reimbursement_item|invoice|contract|null",
  "target_id": "可选",
  "invoice_no": "可选",
  "occurred_at": "可选 YYYY-MM-DD",
  "idempotency_key": "可选"
}
```

输出：

```json
{
  "attachment": {
    "id": "att_...",
    "file_name": "invoice.pdf",
    "storage_provider": "tencent_cos",
    "storage_bucket": "studio-cloud-1257633934",
    "storage_region": "ap-nanjing",
    "storage_key": "finance/invoices/2026/06/.../xxxx__invoice.pdf",
    "content_type": "application/pdf",
    "file_size": 12345,
    "sha256": "..."
  },
  "download": {
    "signed_url_available": true
  }
}
```

### 5.4 报销自动化流程

目标口令：“帮我报销” + 用户上传发票后：

1. Hermes 领取用户上传的发票源文件。
2. OCR/解析发票：发票号、日期、金额、税额、销售方、购买方、文件名、sha256。
3. 调用 `finance_upload_source_file`：把源文件存到 COS，拿到 `attachment`。
4. 调用 `finance_get_intake_rules(record_reimbursement)`。
5. 解析或追问缺失字段：报销人、收款人、项目/主体、费用说明、发票状态。
6. 调用 `finance_prepare_reimbursement`，在 `items[].invoice.file_attachment` 或 `attachments[]` 中放入 COS attachment 引用。
7. 调用 `finance_preview_intake` 给用户确认。
8. 用户确认后调用 `finance_commit_draft`，正式写入 `reimbursement_order`、`reimbursement_item`、`invoice`、`invoice_link`、`attachment`、`audit_log`。
9. 后续付款发生时，再走 `record_fund_out` + `allocate_payment` / `reimbursement_payment`。

## 6. 一期落地顺序

建议按小步可测方式做：

1. 给 `studio-cloud-bridge` 增加 `POST /v1/cos/put-object`，并把 `finance/` 加入 COS 前缀白名单。
2. 给财务系统加 `CosBridgeClient` 和 `AttachmentService`。
3. 数据库扩展 `attachment` 元字段。
4. 新增 `finance_upload_source_file` 工具和 `/api/attachments/source-files` 接口。
5. 改 `commitReimbursementDraft`，识别已上传的 attachment 引用并写入 invoice/attachment。
6. 端到端 smoke：上传一个测试 txt/pdf 到 `finance/invoices/smoke-tests/...`，创建草稿，commit，查询报销单并确认 invoice/attachment 可追溯。
7. 再接实际发票 OCR/解析工具。

## 7. 当前不建议直接做的事

- 不建议把 COS SecretId/SecretKey 复制到财务系统，除非后续确实要绕过 bridge。
- 不建议把 Feishu 临时 file_token 当长期文件存储。
- 不建议把合同源文件塞进报销单备注里，应使用附件/COS key/结构化合同表。
- 不建议在 COS 上重命名/移动对象来表达状态，状态应在 PostgreSQL 里表达，COS 只做对象存储。
- 不建议自动提交正式报销单；仍应保留预览确认或审批边界。

## 8. 结论

当前系统基础是可用的：云端服务、PostgreSQL、报销草稿、正式报销入库、发票/附件关系都已经有。但它目前缺少“源文件长期保存”这一层。COS 接入应作为附件存储层加入，而不是替代数据库。

最小可落地目标：先让机器人能把用户发来的发票/合同源文件上传到 COS，并把 COS key、sha256、文件大小、发票结构化信息与报销单一起写入 PostgreSQL。
