All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m52s
- 受控配置新增代理在线自充允许范围(仅微信/仅支付宝/同时支持),读侧与创建侧取允许范围与可用商户池交集,两侧失败关闭 - 新增允许范围查询与修改端点,读限代理与平台账号、写限超级管理员,复用受控配置写服务留痕 - tb_agent_recharge_record 新增交易流水号、线下收款方式三列快照与其他凭证列(成对迁移 000213) - 线下申请校验启用的收款方式字典项与必填交易流水号,交易流水号独立于在线渠道交易号、不参与去重 - 扩展 offline_recharge_approval 场景可映射字段白名单与字典引用保护 - 新增付款凭证识别能力与交易流水号预填接口,识别不落库、日志不记录载荷
45 lines
5.2 KiB
Markdown
45 lines
5.2 KiB
Markdown
# Gateway 接入契约
|
||
|
||
## 元数据
|
||
|
||
- Owner:IoT Gateway 适配维护人
|
||
- 实现:`internal/gateway/`
|
||
- 核验日期:2026-09-11
|
||
- 证据:`internal/gateway/client.go`、`crypto.go`、`card_status.go`、`flow_card.go`、`device.go`、`payment_voucher.go`
|
||
|
||
## 当前实际使用范围
|
||
|
||
Gateway 是运营商流量卡、实名、停复机、限速和设备信息的统一封装入口。具体路径、请求字段和响应字段以同目录详细协议与 `internal/gateway/*.go` 的实际调用交集为准;文档中出现但代码未调用的接口不视为系统能力。
|
||
|
||
付款凭证识别(`POST /ai/ocr/extract-payment`,入参 `image_base64`)由 `internal/gateway/payment_voucher.go` 封装,当前唯一调用方是代理线下预存款申请的「交易流水号表单预填」。该能力只消费响应中的 `order_number`(作为交易流水号预填值);`amount`、`remark`、`payment_method`、`payee`、`payment_time` 不进入本系统响应、不预填、不落库,识别结果不是资金事实。核验证据:`internal/gateway/payment_voucher.go` 的类型定义只对外暴露支付单号,`internal/application/agentrecharge/payment_voucher_ocr.go` 只返回该字段,接口响应 DTO 仅含 `external_transaction_no`。
|
||
|
||
付款凭证识别刻意不走 `doRequest` / `doRequestWithResponse`:前者在 Info 级别打印加密前完整请求体、后者在 Info 级别打印完整原始响应,会把凭证图片内容与识别原始结果写进日志。该能力改用 `Client.doRequestWithoutPayloadLog`,仅记录路径、耗时与结果字节数摘要;既有能力的请求与日志语义保持不变(`internal/gateway/client.go` 中 `executeWithRetry` 的 `logPayload` 分支)。
|
||
|
||
### 付款凭证识别的已知限制
|
||
|
||
- **长号码可能不完整**:对位数较多的转账单号,该接口可能只返回前若干位,实测存在识别值与凭证图片所示号码不一致的情况(位数少于凭证所示)。连续多次识别同一凭证所得长度与内容稳定,属上游侧确定性截断,而非本系统侧裁剪;预填值**必须**由提交人对照凭证人工核对,系统以人工确认值为准。
|
||
- **单号缺失即失败**:响应未给出单号时,本系统按识别失败返回明确失败(`CodeGatewayInvalidResp`,中文提示),不返回空值。
|
||
- **字段类型会漂移**:响应 `data` 中 `amount` 为 JSON 数值而非字符串。本系统只解码 `order_number`,不声明其余字段,故不受类型漂移影响;新增消费字段前必须重新核对上游类型。
|
||
- **解析失败不回显原文**:响应解码失败时只返回固定中文提示,不携带底层解析错误,避免第三方库的错误消息把识别原始结果带进日志与错误上下文。
|
||
- **单次识别只接受单个附件键**,图片由后端读取对象存储后编码,Gateway 凭证不下发前端;识别结果不落库、不构成资金事实。
|
||
|
||
本条限制的核验方式(可复现、不依赖样本取值):对同一图片凭证**连续三次**调用该识别接口,比较三次返回值的**位数与内容是否一致**——一致说明是上游确定性行为而非随机抖动;再将该位数与凭证图片所示号码的位数(用等长掩码计数,只比位数)对照,得出是否缺位。判定责任方时看本系统的解码路径 `internal/gateway/payment_voucher.go`:它只对返回值做 `strings.TrimSpace`,无截断、无按长度裁剪、无正则截取,因此位数差异只能来自上游。识别结果不落库,复核该接口的返回值需重新发起识别调用,不能从业务表反查。
|
||
|
||
## 配置、认证与报文
|
||
|
||
配置键为 `gateway.base_url`、`gateway.app_id`、`gateway.app_secret`、`gateway.timeout`。业务参数先包装为 `{"params": ...}`,使用 AppSecret 做 AES-128-ECB 加密;外层请求含 `appId`、`data`、`sign`、`timestamp`,签名使用 MD5。HTTP 方法统一为 POST,内容类型为 `application/json;charset=utf-8`。HTTP 200 且 Gateway `code=200` 才算成功,`data` 再按具体能力解码。
|
||
|
||
## 超时、重试与幂等
|
||
|
||
客户端默认超时 60 秒;生效配置可覆盖。默认最多重试 2 次,即最多 3 次尝试,退避为 100ms、200ms,更多重试时封顶 300ms。仅客户端超时、连接和 DNS 等网络级错误重试;用户 Context 取消、HTTP 非 200、响应解析失败和 Gateway 业务码失败不重试。每次尝试重新生成时间戳与签名。
|
||
|
||
查询天然只读;停复机、限速等写操作的幂等和状态条件由调用它的业务 Service 承担,Gateway 客户端本身不提供幂等键。
|
||
|
||
## 安全、错误与验证
|
||
|
||
AppSecret、加密前业务数据、完整身份标识不得写入本文或普通日志;当前客户端存在请求结构日志,运行环境必须依赖日志脱敏策略。Gateway 错误映射为项目 `CodeGatewayError`、`CodeGatewayTimeout` 或 `CodeGatewayInvalidResp`。
|
||
|
||
可复现静态证据:`internal/gateway/client.go`、`internal/gateway/crypto.go` 及同目录能力文件。真实写操作可能改变卡或设备状态,本次只允许静态核对和隔离账号只读验证,不调用生产写接口。
|
||
|
||
协议、路径、认证、成功码、超时或重试分类变化时更新本文。
|