# API / MCP｜机器人优先的后端接口设计草案

更新时间：2026-06-03 21:11:35 CST  
适用阶段：一期系统设计草案  
核心口径：前端弱化，后端接口和 MCP 工具面向财务机器人使用。

---

## 0. 用户强调后的设计修正

本系统不应按“很多人工录入页面”的传统后台设计。

一期系统的主要入口应该是：

```text
内部成员
  ↓ 对话
财务机器人 / Hermes / Feishu Bot
  ↓ 调用 MCP tools
财务系统后端 API
  ↓ 写入草稿 / 等待审批 / 审批后入库
PostgreSQL 事实库
```

前端只保留必要的管理、核对、查看和异常处理页面，例如：

1. 主体维护；
2. 项目维护；
3. 资金流水核对；
4. 待确认/异常数据；
5. 审批后入库结果查看；
6. 财务/个人/老板视图。

机器人承担主要录入交互：

1. 识别用户意图；
2. 读取对应入库规则；
3. 判断缺失字段；
4. 主动追问；
5. 生成结构化草稿；
6. 给用户确认摘要；
7. 发起审批或等待审批回调；
8. 审批通过后调用正式入库接口。

> 本文暂不展开飞书审批实现，只保留审批状态和审批引用边界。

---

# 1. 总体接口分层

## 1.1 后端能力分层

```text
A. 规则层 Rules
- 告诉机器人某类业务需要哪些字段、哪些字段可默认、哪些字段必须追问。

B. 解析/草稿层 Draft
- 机器人把自然语言整理成结构化草稿，后端负责校验、补全候选项、返回缺失字段。

C. 审批前预览层 Preview
- 生成用户/审批人能看懂的摘要，不直接入账。

D. 入库层 Commit
- 审批通过后，把草稿正式写入 subject/project/receivable/payable/fund_transaction 等事实表。

E. 查询层 Query
- 给机器人查询主体、项目、应收应付、资金流水、个人账派生结果。
```

## 1.2 机器人不应直接做的事

机器人不应直接：

1. 猜测关键财务事实；
2. 绕过审批写入正式账；
3. 直接操作底层表的任意字段；
4. 修改已锁定或已月结数据；
5. 删除历史事实记录；
6. 把审批当成账本事实源。

机器人应该调用业务级 API / MCP 工具，而不是直接调用数据库。

---

# 2. 业务意图 Intent 设计

机器人首先要判断用户说的是哪类业务。

一期建议支持这些 `intent`：

| intent | 中文 | 说明 |
|---|---|---|
| `record_reimbursement` | 记录报销 | 用户说“我要报销/记一笔报销/某人垫付了” |
| `record_receivable` | 记录应收 | 用户说“这个项目要收多少钱/合同款/尾款” |
| `record_fund_in` | 记录收款流水 | 用户说“某客户打款了/钱进来了” |
| `allocate_receipt` | 核销收款 | 用户说“这笔到账对应某项目/某尾款” |
| `record_payable` | 记录应付 | 用户说“供应商款/采购款/合同已签待付” |
| `record_fund_out` | 记录付款流水 | 用户说“已经打款/付款给供应商/还给个人” |
| `allocate_payment` | 核销付款 | 用户说“这笔付款对应哪些应付/报销” |
| `personal_injection` | 个人注资 | 用户说“某人打钱进公司/借给公司” |
| `company_repay_person` | 公司还个人 | 用户说“公司还了某人的款/报销款” |
| `query_project` | 查询项目 | 查项目收入、支出、应收应付、流水 |
| `query_personal_ledger` | 查询个人账 | 查某个人的派生余额/明细 |
| `query_subject_summary` | 查询主体汇总 | 查某主体现金、待收、待付等 |

---

# 3. 后端 REST API 草案

> 规则配置入口：本文的 `GET /api/intake/rules` 和 MCP 工具 `finance_get_intake_rules`，一期可先读取同目录下的 `intake_rules_一期草案.json`；详细字段要求和追问策略见 `一期机器人入库规则与追问策略.md`。

## 3.1 规则与校验 API

### `GET /api/intake/rules`

机器人获取某类业务入库规则。

Query：

| 参数 | 必填 | 说明 |
|---|---|---|
| `intent` | 是 | 业务意图，如 `record_reimbursement` |
| `version` | 否 | 规则版本，默认 current |

返回示例：

```json
{
  "intent": "record_reimbursement",
  "required_fields": [
    "applicant_subject",
    "payee_subject",
    "items[].amount",
    "items[].project_or_owner_subject",
    "items[].invoice_status",
    "items[].description"
  ],
  "optional_fields": [
    "occurred_at",
    "expense_category",
    "attachments",
    "advancer_subject"
  ],
  "defaulting_rules": [
    "如果有项目，则 owner_subject 由 project 推导",
    "如果无业务项目但有主体，则 project 使用该主体默认基础经营项目",
    "如果缺发生日期，可先标记待确认"
  ],
  "hard_blocking_missing_fields": [
    "amount",
    "project_or_owner_subject",
    "invoice_status"
  ]
}
```

### `POST /api/intake/validate`

校验机器人整理出的结构化草稿。

请求：

```json
{
  "intent": "record_reimbursement",
  "draft": {},
  "operator_subject_id": "sub_xxx"
}
```

返回：

```json
{
  "ok": false,
  "missing_fields": [
    {
      "field": "items[0].project_or_owner_subject",
      "level": "blocking",
      "question": "这笔报销是归哪个项目？如果不确定，请告诉我是哪个主体的基础经营支出。"
    }
  ],
  "warnings": [],
  "normalized_draft": {}
}
```

### `POST /api/intake/preview`

生成审批/用户确认前摘要。

返回：

```json
{
  "preview_text": "本次将创建一张报销草稿，总额 380 元，归属云汉寻真-基础经营，无发票，垫付人为钱丽云。",
  "risk_flags": ["无发票", "归属基础经营项目"],
  "will_create": ["reimbursement_order", "reimbursement_item"],
  "will_not_create_yet": ["fund_transaction"]
}
```

---

## 3.2 主体与项目 API

### `GET /api/subjects/search`

搜索主体。

Query：

```text
q=云汉&type=company&active=true
```

### `POST /api/subjects/resolve`

机器人根据用户口述解析主体，返回唯一命中、候选列表或需要追问。

```json
{
  "text": "云汉",
  "allowed_types": ["company", "sole_prop", "person"],
  "create_if_missing": false
}
```

### `POST /api/projects/resolve`

查找项目；如果用户说无明确项目，可返回主体默认基础经营项目。

```json
{
  "project_text": "国博供货",
  "owner_subject_text": "万物有灵",
  "allow_base_operation_fallback": true,
  "create_if_missing": false
}
```

返回：

```json
{
  "resolution": "matched",
  "project": {
    "id": "proj_xxx",
    "name": "国博供货",
    "owner_subject_id": "sub_xxx",
    "project_type": "BUSINESS"
  },
  "candidates": [],
  "question": null
}
```

### `POST /api/projects/ensure-base-operation`

确保某主体有默认基础经营项目。

```json
{
  "owner_subject_id": "sub_xxx"
}
```

---

## 3.3 应收 / 收款 API

### `POST /api/receivables/draft`

创建应收草稿。

```json
{
  "project_id": "proj_xxx",
  "commercial_order_id": "order_xxx",
  "name": "一期款",
  "amount": "50000.00",
  "expected_date": "2026-06-20",
  "expected_receiver_subject_id": "sub_xxx",
  "source_category_code": "project_income",
  "remark": "合同首款"
}
```

### `POST /api/fund-transactions/draft`

创建真实资金流水草稿。

```json
{
  "direction": "IN",
  "amount": "50000.00",
  "occurred_at": "2026-06-20",
  "payer_subject_id": "sub_customer",
  "receiver_subject_id": "sub_company",
  "receiver_cash_account_id": "acct_xxx",
  "source_category_code": "cash_inflow",
  "remark": "客户打款"
}
```

### `POST /api/receivable-settlements/draft`

把某笔资金流水分配到某个应收。

```json
{
  "receivable_id": "recv_xxx",
  "fund_transaction_id": "ft_xxx",
  "amount": "30000.00",
  "settled_at": "2026-06-20"
}
```

---

## 3.4 应付 / 付款 API

### `POST /api/payables/draft`

创建应付草稿。

```json
{
  "project_id": "proj_xxx",
  "paying_subject_id": "sub_company",
  "counterparty_subject_id": "sub_supplier",
  "expense_category_code": "supplier_purchase",
  "name": "打样采购款",
  "amount": "1200.00",
  "incurred_at": "2026-06-03",
  "due_date": "2026-06-10",
  "invoice_status": "PENDING_INVOICE"
}
```

### `POST /api/payable-settlements/draft`

把某笔资金流水分配到某个应付。

```json
{
  "payable_id": "pay_xxx",
  "fund_transaction_id": "ft_xxx",
  "amount": "1200.00",
  "settled_at": "2026-06-10"
}
```

---

## 3.5 报销 API

### `POST /api/reimbursements/draft`

创建报销草稿，一次包含主表和明细。

```json
{
  "applicant_subject_id": "person_qly",
  "payee_subject_id": "person_qly",
  "default_owner_subject_id": "sub_company",
  "items": [
    {
      "project_id": "proj_base_operation",
      "expense_category_code": "office",
      "source_category_code": "reimbursement_no_invoice",
      "advancer_subject_id": "person_qly",
      "amount": "380.00",
      "occurred_at": "2026-06-03",
      "invoice_status": "NO_INVOICE",
      "description": "办公用品采购",
      "personal_ledger_effect": true
    }
  ],
  "remark": "用户口述记录"
}
```

### `POST /api/reimbursements/{id}/payments/draft`

报销实际付款关联真实资金流水。

```json
{
  "fund_transaction_id": "ft_xxx",
  "amount": "380.00",
  "paid_at": "2026-06-05"
}
```

---

## 3.6 审批边界 API

当前不展开飞书审批实现，但保留接口边界。

### `POST /api/intake/drafts`

创建统一入库草稿，状态为 `DRAFT`。

```json
{
  "intent": "record_reimbursement",
  "normalized_payload": {},
  "created_by_agent": "finance-bot",
  "operator_subject_id": "person_qly"
}
```

### `POST /api/intake/drafts/{id}/submit-approval`

把草稿提交到审批层，返回审批引用。

```json
{
  "approval_channel": "feishu",
  "preview_text": "..."
}
```

返回：

```json
{
  "draft_id": "draft_xxx",
  "approval_status": "PENDING",
  "approval_ref": {
    "platform": "feishu",
    "instance_code": "approval_xxx"
  }
}
```

### `POST /api/intake/drafts/{id}/commit-approved`

审批通过后正式入库。

```json
{
  "approval_ref": {
    "platform": "feishu",
    "instance_code": "approval_xxx"
  },
  "commit_mode": "create_facts"
}
```

返回：

```json
{
  "ok": true,
  "created_records": [
    {"type": "reimbursement_order", "id": "reim_xxx"},
    {"type": "reimbursement_item", "id": "ri_xxx"}
  ]
}
```

---

# 4. MCP Tools 草案

MCP 工具应按“业务动作”封装，而不是按数据库表裸写。

## 4.1 规则类工具

### `finance_get_intake_rules`

用途：机器人先查规则，再决定追问用户什么。

输入：

```json
{
  "intent": "record_reimbursement"
}
```

输出：字段要求、缺失字段提问模板、默认规则、阻塞规则。

### `finance_validate_intake_draft`

用途：机器人整理出草稿后，请后端校验。

输入：

```json
{
  "intent": "record_reimbursement",
  "draft": {}
}
```

输出：是否可提交、缺失字段、风险提示、规范化后的草稿。

---

## 4.2 主体/项目解析工具

### `finance_resolve_subject`

用途：把用户口述的“云汉”“我”“万物有灵”等解析成主体。

### `finance_resolve_project`

用途：解析项目；如果找不到项目，返回候选或建议创建；如果用户说“无项目”，返回对应主体的基础经营项目。

### `finance_ensure_base_operation_project`

用途：确保某主体有默认基础经营/公共项目。

---

## 4.3 入库草稿工具

### `finance_prepare_reimbursement`

用途：创建报销草稿，不直接正式入账。

### `finance_prepare_receivable`

用途：创建应收草稿。

### `finance_prepare_payable`

用途：创建应付草稿。

### `finance_record_fund_transaction_draft`

用途：创建真实资金流水草稿。

### `finance_allocate_receivable`

用途：把资金流水分配到应收。

### `finance_allocate_payable`

用途：把资金流水分配到应付。

### `finance_link_reimbursement_payment`

用途：把资金流水关联到报销单付款。

---

## 4.4 审批/提交工具

### `finance_preview_intake`

用途：生成用户确认/审批摘要。

### `finance_submit_intake_for_approval`

用途：提交审批；当前只定义契约，不展开飞书实现。

### `finance_commit_approved_intake`

用途：审批通过后正式写入事实表。

---

## 4.5 查询工具

### `finance_search_records`

用途：机器人查询主体、项目、应收、应付、报销、资金流水。

### `finance_get_project_summary`

用途：查询项目收入、应收、实收、应付、实付、报销、毛口径成本。

### `finance_get_personal_ledger_preview`

用途：查询个人账派生明细和余额。

### `finance_get_subject_summary`

用途：查询某主体现金、待收、待付、个人往来等汇总。

---

# 5. 机器人对话入库流程

## 5.1 报销示例

用户：

```text
我今天买了 380 元办公用品，没发票，先走云汉公共经营，之后报给我。
```

机器人流程：

```text
1. 判断 intent = record_reimbursement
2. 调用 finance_get_intake_rules(record_reimbursement)
3. 解析主体：云汉 → subject
4. 确认/创建基础经营项目：云汉-基础经营
5. 整理草稿：金额、发票状态、项目、垫付人、收款人、说明
6. 调用 finance_validate_intake_draft
7. 如果缺字段，追问；如果完整，生成 preview
8. 用户确认后，调用 finance_submit_intake_for_approval
9. 审批通过后，调用 finance_commit_approved_intake
```

## 5.2 收款示例

用户：

```text
国博那个项目今天到账 5 万，是第一期款。
```

机器人流程：

```text
1. 判断 intent = record_fund_in + allocate_receipt
2. 查项目：国博项目
3. 查是否已有对应应收“一期款”
4. 没有则准备创建 receivable 草稿
5. 创建 fund_transaction(direction=IN) 草稿
6. 创建 receivable_settlement 草稿
7. 生成预览：本次到账 5 万，核销国博项目一期应收
8. 提交审批/确认
9. 审批通过后正式入库
```

## 5.3 付款示例

用户：

```text
今天给供应商打了 1200，是良渚样品打样款。
```

机器人流程：

```text
1. 判断 intent = record_fund_out / record_payable / allocate_payment
2. 查项目：良渚样品打样
3. 查供应商主体，没有则提示是否创建
4. 查是否已有 payable，没有则创建应付草稿
5. 创建 fund_transaction(direction=OUT) 草稿
6. 创建 payable_settlement 草稿
7. 生成预览并提交审批/确认
8. 审批通过后正式入库
```

---

# 6. 接口设计原则

## 6.1 幂等性

所有机器人写入接口都应支持 `idempotency_key`。

示例：

```json
{
  "idempotency_key": "feishu_msg_abc123_record_reimbursement_v1"
}
```

避免机器人重试导致重复入账。

## 6.2 草稿优先

机器人默认只创建草稿：

```text
DRAFT → APPROVAL_PENDING → APPROVED → COMMITTED
```

正式事实表写入应发生在审批通过或明确人工确认后。

## 6.3 缺失字段要可追问

后端校验结果必须返回适合机器人追问的 `question`。

错误示例：

```text
project_id required
```

正确示例：

```text
这笔费用归哪个项目？如果没有明确项目，请告诉我是哪个主体的基础经营支出。
```

## 6.4 返回值要给机器人可解释摘要

写入/预览接口都应返回：

```json
{
  "human_summary": "...",
  "risk_flags": [],
  "created_or_pending_records": [],
  "next_questions": []
}
```

机器人可以直接把 `human_summary` 发给用户确认。

## 6.5 不暴露底层随意写表能力

MCP 不应该提供：

```text
db_insert_any_table
update_any_field
delete_record
```

应该提供业务工具：

```text
finance_prepare_reimbursement
finance_record_fund_transaction_draft
finance_allocate_receivable
finance_commit_approved_intake
```

---

# 7. 一期最小 MCP 工具清单

如果一期只做最小可用，建议先封装 10 个工具：

1. `finance_get_intake_rules`
2. `finance_validate_intake_draft`
3. `finance_resolve_subject`
4. `finance_resolve_project`
5. `finance_prepare_reimbursement`
6. `finance_prepare_receivable`
7. `finance_prepare_payable`
8. `finance_record_fund_transaction_draft`
9. `finance_allocate_fund_transaction`
10. `finance_preview_intake`

其中第 9 个可以根据目标类型分配到：

```text
receivable / payable / reimbursement
```

---

# 8. 后续需要继续细化

1. 每个 MCP tool 的 JSON Schema；
2. 每个 intent 的 required/optional/default 字段；
3. 草稿表 `intake_draft` 是否进入 Prisma schema；
4. 审批状态字段是否统一放在 `intake_draft`，还是分散到业务表；
5. 机器人权限和操作人身份映射；
6. 幂等键生成规则；
7. 错误码与追问模板；
8. 查询类接口的分页、过滤、权限；
9. 前端只保留哪些最小管理/核对页面。
