2. 接口基础

在工程合同发票匹配与付款风险控制系统中,API 是前后端及第三方系统交互的桥梁。本章聚焦 RESTful API 的核心设计原则、HTTP 方法语义、状态码规范以及幂等性设计,为后续接口调用实战打下坚实基础。

2.1 RESTful API 六大设计原则

REST(Representational State Transfer)是一种架构风格,而非协议。遵循以下原则可构建出易维护、可扩展的工程财务接口:

原则 说明 工程合同场景示例
资源导向 每个 URL 代表一个资源(名词),而非操作(动词) /api/contracts 表示合同集合,/api/invoices 表示发票集合
无状态 服务端不保存客户端上下文,每次请求包含全部必要信息 付款审批请求需携带 、合同 ID、发票 ID,服务端不依赖 session
统一接口 使用标准 HTTP 方法操作资源,资源表述格式统一(JSON) 所有增删改查均通过 GET/POST/PUT/PATCH/DELETE 完成
可缓存 响应隐式或显式标记为可缓存或不可缓存 合同基础信息(如合同编号、供应商名称)可缓存,付款状态不可缓存
分层系统 客户端无法直接感知是否与最终服务器或中间层通信 API 网关、负载均衡、微服务之间透明调用
按需代码(可选) 服务器可向客户端传输可执行脚本(如 JavaScript) 工程财务系统中较少使用,通常保持纯数据接口
实战要点:在合同发票匹配系统中,资源命名应使用复数形式(如 /api/payment-schedules),避免使用动词(如 /api/getPayment)。URL 中仅包含名词,操作通过 HTTP 方法表达。

2.2 HTTP 方法语义与工程财务场景

每个 HTTP 方法对应一种标准操作。在付款风险控制接口中,正确使用方法可提升接口的可读性和安全性。

方法 语义 幂等 工程合同场景 请求体 响应体
GET 查询资源 是 查询合同列表、获取发票详情 无 资源对象或集合
POST 创建资源 否 创建新合同、提交发票匹配请求 创建所需数据 新创建的资源(含 ID)
PUT 全量替换资源 是 更新合同全部字段(如合同总价变更) 完整的资源数据 更新后的资源
PATCH 部分更新资源 否(需设计) 仅更新发票匹配状态、修改付款比例 仅包含要修改的字段 更新后的资源
DELETE 删除资源 是 删除作废的发票记录、撤销付款申请 无 204 No Content
常见误区:不要使用 GET 请求修改资源状态(如 GET /api/contracts/123/approve),这违反了 REST 语义且可能被缓存或爬虫触发。应使用 PATCH /api/contracts/123 并传入 {"status": "approved"}。

2.3 HTTP 状态码:精准表达接口结果

状态码是客户端判断请求结果的直接依据。在付款风险控制中,错误的状态码可能导致资金操作异常,必须严格遵循规范。

2xx 成功类

  • 200 OK:GET、PUT、PATCH 请求成功,响应体包含资源数据。
  • 201 Created:POST 请求成功创建资源,响应头 Location 指向新资源 URL。
  • 204 No Content:DELETE 请求成功,或某些无需返回体的操作(如批量确认)。

3xx 重定向类

  • 301 Moved Permanently:资源 URL 已永久变更,客户端应更新缓存。
  • 304 Not Modified:条件请求(如 If-None-Match)命中缓存,无需传输资源。

4xx 客户端错误类

  • 400 Bad Request:请求参数错误(如发票金额格式不正确)。
  • 401 Unauthorized:未提供或提供了无效的身份凭证。
  • 403 Forbidden:已认证但无权限(如普通用户尝试审批付款)。
  • 404 Not Found:资源不存在(如查询不存在的合同 ID)。
  • 409 Conflict:资源当前状态冲突(如发票已被其他付款单锁定)。
  • 422 Unprocessable Entity:请求体语义正确但业务校验失败(如合同金额与发票金额不匹配)。
  • 429 Too Many Requests:超出 API 调用频率限制。

5xx 服务端错误类

  • 500 Internal Server Error:服务器内部异常,需排查日志。
  • 502 Bad Gateway:上游服务不可用(如数据库连接池耗尽)。
  • 503 Service Unavailable:服务暂时过载或维护中。
工程财务最佳实践:对于付款接口,使用 409 Conflict 表示发票已被其他流程占用,使用 422 Unprocessable Entity 表示合同与发票金额不一致。避免将所有业务错误都返回 400 Bad Request,这会使客户端难以区分错误类型。

2.4 幂等性设计:保障付款安全的关键

幂等性(Idempotency)指同一操作执行多次与执行一次的结果相同。在付款场景中,网络重试可能导致重复扣款,幂等性设计是核心防线。

HTTP 方法的幂等性矩阵

方法 天然幂等 说明
GET 是 多次查询返回相同结果(不考虑并发修改)
PUT 是 全量替换,多次执行结果一致
DELETE 是 第一次删除成功,后续返回 404 或 204,资源状态不变
POST 否 多次创建会生成多个资源(如重复提交付款申请)
PATCH 否(需设计) 若操作是“金额加 100”,多次执行结果不同

幂等性实现方案

  1. 幂等键(Idempotency-Key):客户端在 POST/PATCH 请求头中传入唯一键(如 UUID),服务端记录已处理的键,重复请求直接返回首次结果。
  2. 乐观锁(版本号):资源携带 version 字段,更新时校验版本号,防止并发覆盖。
  3. 业务唯一约束:利用数据库唯一索引(如 contract_id + invoice_id + payment_seq 联合唯一),重复插入直接报错。
付款场景示例:客户端发起 POST /api/payments 创建付款单,携带 Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000。若网络超时后重试,服务端检测到相同 key,直接返回已创建的付款单 ID,避免重复扣款。
RESTful API 请求-响应流程与幂等性设计 客户端 API 网关 业务服务 POST /payments Idempotency-Key: xxx 转发请求 幂等性检查 数据库 查询/写入 201 Created 201 Created 幂等性关键点: • 客户端生成唯一 Idempotency-Key,服务端缓存处理结果 • 重复请求直接返回首次响应,不重复执行业务逻辑

2.5 接口设计综合示例:合同发票匹配

以下是一个完整的 RESTful 接口设计示例,涵盖合同与发票的匹配流程:

功能 HTTP 方法 URL 请求体示例 成功状态码 幂等性
查询合同列表 GET /api/contracts?status=active 无 200 是
创建合同 POST /api/contracts {"contractNo":"C2025-001","amount":500000} 201 否(需幂等键)
更新合同总价 PUT /api/contracts/123 {"contractNo":"C2025-001","amount":550000,"status":"active"} 200 是
修改合同状态 PATCH /api/contracts/123 {"status":"approved"} 200 否(需幂等键)
删除作废发票 DELETE /api/invoices/456 无 204 是
提交发票匹配 POST /api/matchings {"contractId":123,"invoiceId":456,"amount":50000} 201 否(需幂等键)
代码示例:幂等键校验中间件(Node.js 伪代码)
// 幂等性中间件
const idempotencyStore = new Map(); // 生产环境应使用 Redis

function idempotencyMiddleware(req, res, next) {
  if (['POST', 'PATCH'].includes(req.method)) {
    const key = req.headers['idempotency-key'];
    if (!key) {
      return res.status(400).json({ error: 'Idempotency-Key header is required' });
    }
    if (idempotencyStore.has(key)) {
      // 返回首次缓存的结果
      return res.status(idempotencyStore.get(key).status)
                .json(idempotencyStore.get(key).body);
    }
    // 保存原始 send 方法以缓存响应
    const originalSend = res.send.bind(res);
    res.send = function(body) {
      idempotencyStore.set(key, { status: res.statusCode, body: JSON.parse(body) });
      originalSend(body);
    };
  }
  next();
}
HTTP 方法选择决策树(工程财务场景) 操作类型? 查询资源 创建资源 更新资源 删除资源 只读 新资源 修改 移除 GET POST + 幂等键 PUT / PATCH DELETE 全量/部分 提示:PUT 用于全量替换(幂等),PATCH 用于部分更新(需幂等键)。工程财务中推荐优先使用 PATCH 减少传输量。

2.6 常见设计反模式与避坑指南

  • 反模式 1:在 URL 中使用动词 —— 如 /api/approveContract。应改为 PATCH /api/contracts/123 并传入 {"status":"approved"}。
  • 反模式 2:忽略幂等性 —— 付款接口未实现幂等键,网络重试导致重复扣款。必须为 POST/PATCH 添加幂等性支持。
  • 反模式 3:状态码使用不当 —— 所有错误都返回 400 或 500。应区分 400(参数错误)、409(冲突)、422(业务校验失败)。
  • 反模式 4:GET 请求带请求体 —— 某些客户端在 GET 中携带 JSON body,违反 HTTP 规范。查询参数应放在 URL query string 中。
  • 反模式 5:嵌套资源过深 —— 如 /api/contracts/123/invoices/456/items/789。建议扁平化或使用查询参数:/api/items?contractId=123&invoiceId=456。
付款风险红线:任何涉及资金变动的接口(付款、退款、调账)必须实现幂等性校验,并在接口文档中明确标注幂等键的传递方式。未实现幂等性的付款接口不得上线。