36 KiB
客户 HTTP 接口与接口文档整改方案
修订日期:2026-09-14。状态:整改代码已提交并部署测试;验收边界见本轮发布记录。本文件由原《客户 HTTP 接口与对接文档评估》转为整改方案,保留原文件名 http-api-assessment-20260910.md,避免既有链接失效;末尾原评估作为2026-09-10历史证据保留,历史版本/计数不代表当前现场。
原文档讨论轮授权仅修改接口手册、整改方案及相关需求/用例/进度文档,不修改运行代码、页面、数据库、Redis、业务配置或发布工具,不提交、推送或部署,不发送或重投短信/回调。用户仍将继续提出问题,不能把讨论稿视为已定稿并自动开始实施。
2026-09-14 本轮实施设计(覆盖上述旧文档阶段授权说明)
本次用户已明确授权按本方案整改、提交、推送及测试环境发布;不含预生产发布、实际短信发送、历史短信/回调重投及业务配置变更。原讨论记录保留历史含义。
- R01:本次维持无体GET的
{}摘要、五行LF、PATH不含query和旧合法nonce。抽取唯一签名实现并用手册固定向量验证,不切换空字节协议。 - R02/R03回调:仅新事件进入版本化耐久投递记录;事件和投递同一PG事务。PG保存下一尝试时间、租约和尝试号,扫描器每15秒最多认领50条;Redis任务编号由deliveryId/attemptNo组成且不含冒号。投递前条件认领,尝试和最终/退避状态同事务;崩溃后租约到期恢复。HTTP可能已到达但ACK丢失时属于至少一次投递,客户按eventId幂等。旧记录不自动升级或补投,手工重投继续要求原权限和明确操作。
- R03发送:新请求的批次、消息采用确定性关联;消息创建、HTTP响应快照及发送待办在同一PG事务提交,之后才投Redis。已冻结的202快照不因后续Redis失败变成500/failed;待办仅认领本轮新增的耐久记录,使用现有消息ID去重与发送状态检查。业务前置风控/余额操作仍在既有服务执行,若在消息事务前中断,请求进入
requires_review,禁止超时自动重建/重发。历史processing只报告核对状态,不猜测关联或补投。追加迁移,不回填历史。 - R04/R05:保持四业务路径,显式DTO/schema/query;clientMessageId只接受string/null且最长128,limit必须正整数并保持应用上限裁剪。历史数字/对象clientMessageId、小数limit原属未承诺输入,本轮改为稳定400。未知异常和幂等重放统一INTERNAL_ERROR与固定公共文案;每次HTTP交互返回独立X-Request-Id,业务requestId保持幂等稳定。
- R06:用户明确禁止暴露通道字段,详情和列表直接采用公共投影,移除通道/供应商编号及内部事件、匹配诊断;详情允许返回当前客户自己的tenantId/applicationId。属于已确认的字段收紧,旧接入需移除内部字段依赖,不保留可选泄露通道的兼容视图。
- R07:复用现有有界协议日志,记录查询/鉴权失败/限流的关联ID、路径模板、结果和耗时,不保存正文、密钥、签名或完整鉴权头;不新增无界日志表。
- R08:公开页与客户端Tab复用阅读组件及单一MD源。API独立阅读器服务文档HTML并内嵌精确CSS/JS,客户端以同源iframe复用该阅读器与单一MD正文,不生成另一套长文案;无需新增Nginx资源白名单。独立文档入口只服务文档HTML/精确资源,不放开管理路由;OpenAPI JSON原地址保留。文档资源打包进入候选,页面支持目录、示例复制、MD/JSON下载、错误检索和三尺寸。配置失败/无应用仍可读通用正文,真实参数不得用默认值冒充。
- R09:维持默认24小时、应用级跨度与分页上限;不新增留存清理或扩大查询窗口。
验收按R01~R09分别记录:故障隔离用真实PG/Redis及受控接收端;禁止向现存业务队列创建探针。短信正向链路未获专项发送授权时明确未执行;测试环境发布与真实页面仍需独立核验。追加字段/表允许应用回退,回退不删除待办;保留恢复资产。需要测试机sudo时使用标准工具掩码入口。
一、目标与文档分工
- 客户接口手册:面向客户开发人员,说明当前可用调用方式、参数/响应、报文、接入步骤和限制;拟变更行为明确标注,不能伪装成已上线。
- 本方案:维护当前问题、已采纳原则、实施范围、兼容影响、页面整改和验收条件。历史HTTP-A01~A08作为问题来源,均未因文档修改而关闭。
- 需求:维护业务及页面要求;用例维护验收;进度维护实际执行证据。
- 顺序固定为:继续讨论并确定手册 → 完善/确定整改方案 → 用户授权后修改代码与验收 → 按独立授权提交/发布。文档可先完善,但不预先宣称业务功能通过。
二、本次已采纳的接口与表述原则
| 主题 | 已采纳原则 | 兼容与现状 |
|---|---|---|
| 上行时间 | 分开说明默认24小时、单次跨度默认31天、历史留存、游标有效期 | 单次跨度按应用配置;当前没有“只查最近31天”硬限制;90天保留配置未发现执行闭环,不承诺已生效 |
| 请求标识 | X-Nonce 中文名改为“请求唯一标识”,推荐标准库UUID v4 |
保留字段名和原8~128字符规则,不强制淘汰旧合法随机值;每次调用/重试生成新值 |
| 业务标识 | nonce、Idempotency-Key、clientMessageId分开讲解 | 同一发送的网络重试更新nonce/时间戳/签名,保持幂等键、客户编号和body字节 |
| 签名结构 | 保留现有五项固定顺序和LF,不改成&拼接协议 |
公式明确\n为0x0A,非字面反斜杠n、非CRLF、无结尾换行;提供固定向量 |
| query与空体 | 当前PATH不含query;GET空体按{}摘要是兼容现状,不是整改完成 |
不在本轮偷偷加入query签名;标准空体摘要的迁移策略需结合既有客户再确定 |
| 示例 | 四接口逐一提供cURL、成功/失败HTTP响应;上行含空数据/两页,回调含POST与ACK;单发另提供完整独立生成脚本和已计算演示向量 | 区分结构占位、动态生成和固定离线校对;演示凭据无业务权限,生成命令不等于执行或发送成功 |
| 协议能力 | 保留四公开接口、现有应用隔离和HMAC安全基础 | 不新增匿名试发、批量发送、余额/模板管理等范围 |
nonce表述和UUID示例不需要修改服务端鉴权规则。接口响应默认值、枚举、错误码等须以最终设计约定落实,不能把偶然实现行为长期固化为正确设计。
三、整改工作项与依赖
以下表保留方案阶段的目标和依赖;本轮实现、测试部署与未验证项以2026-09-14发布记录为准,“文档已补”本身不构成线上修复证据。
| 编号 | 来源 | 目标与最小范围 | 验收与依赖 |
|---|---|---|---|
| HTTP-R01 | A01 | 明确空体摘要、UTF-8/JSON原字节、LF及路径规则;签名函数与服务端共享测试向量;保留nonce头兼容 | 定向鉴权/重放/篡改测试和真实认证查询;先查既有客户兼容,当前手册暂保留{} |
| HTTP-R02 | A02 | 回调自动/手工任务编号采用安全稳定格式;禁止依赖BullMQ冒号兼容分支 | 真实隔离Redis/PG和受控回调端验证首次失败后确有后续任务、尝试/退避/上限一致 |
| HTTP-R03 | A03 | 请求与业务记录建立确定性关联,补处理中断和DB到Redis入队恢复机制 | 实施前补详细数据模型/事务和状态设计;不得超时后直接重新发送;未知结果进入核对状态 |
| HTTP-R04 | A04/A05 | 明确请求与响应DTO、类型/可空性、参数校验和公共错误;修Swagger重复/误必填及缺失schema/query | 生成契约检查、边界/权限/租户隔离真实验收;有历史兼容影响的字段收紧先列清单 |
| HTTP-R05 | A06 | 未知异常对外固定公共信息,内部保留关联诊断;首次/幂等重放错误一致 | 不泄露依赖异常,错误码/响应头/日志关联可核对 |
| HTTP-R06 | A07 | 上行详情改为公共响应映射,列表/详情字段语义一致 | 现有客户内部字段依赖先盘点;删字段需兼容策略,不直接破坏旧接入 |
| HTTP-R07 | A08 | 补查询、验签/限流失败的可定位性,区分请求唯一标识、平台requestId、业务编号和回调eventId | 控制日志量与隐私;增加的持久化/索引需单列设计,不将全部请求正文入日志 |
| HTTP-R08 | 页面/文档 | 定制公开开发者文档页,客户端文档Tab与公开页共用正文/组件,MD及OpenAPI形成一致的阅读与参考体系 | 采用第三种做法,详见第四节;保留其他四个Tab及企业应用切换 |
| HTTP-R09 | 时间边界 | 澄清查询跨度与保留策略;查询默认/游标行为覆盖测试 | 本轮只纠正说明;真正实现清理、归档或改变历史查询窗口须另外确定数据治理范围和保留授权 |
3.1 请求与回调恢复设计约束
R03不能仅通过“增加重试”关闭。后续详细设计至少说明:请求幂等记录、短信业务记录和投递待办的关联键;哪些在同一事务中提交;跨DB/Redis失败如何扫描并认领;多实例租约/唯一约束如何防重复;哪些状态能自动恢复、哪些必须人工核对;响应快照何时冻结。
区分业务重发和耐久任务恢复。原请求结果无法确定时,不自动新建短信;历史pending/retrying回调需先统计核对并取得专项处理授权,不在升级时批量补投。迁移应可追加、历史记录按可证明关联程度处理,不猜测旧记录关联关系。索引/迁移、账号权限、运行角色和扫描负载在设计细化后才实施。
3.2 签名兼容门槛
保留五行LF结构,不将签名方案整体重做。整改空体前核对客户是否使用{},明确新旧签名的识别方式、适用请求、过渡期限、日志观测和回退条件;不得无限制尝试不同body摘要或改变POST原始字节校验。兼容策略尚未在本稿定死,讨论完成前代码不得切换。
当前query未参与签名写清楚即可;如后续决定保护query,必须说明编码、排序、重复/空参数和签名版本,是单独协议变更,不能夹带在LF说明修订内。
四、定制开发者文档页与客户端文档整改(HTTP-R08)
4.0 已选方案:定制开发者文档页(2026-09-14补充)
用户选择第三种做法:按客户接入手册定制独立开发者文档页面,采用平台统一品牌和UI规范。此选择确立页面整改方向,当前仍仅修改文档,未开始页面开发。
- 公开入口
https://api.lisglo.com/api/client-docs后续展示定制页面,保持既有客户链接可用;不将仅换Swagger配色或换一个默认文档组件作为交付完成。 /api/client-docs-json继续提供机器可读OpenAPI,业务路径和鉴权边界保持不变。公开文档允许未开户用户阅读,不显示私有应用配置,不提供匿名试发。- 已登录客户端的“接口文档”Tab与公开页复用正文、代码示例和文档组件;客户端外层保留应用上下文及真实参数,公开页仅展示环境公共信息和明确标注的默认值。MD下载来自同一文档源。
- OpenAPI继续作为接口定义来源,提供下载入口并与叙述文档做契约校验;默认Swagger UI不再作为客户主要阅读入口。若仍需保留原调试UI,实施设计应先说明用途、地址与访问边界,不能顺带新增公开管理接口或自动执行请求。
- 入口切换可能涉及静态资源服务、路由和现有Nginx白名单,实施前核对实际部署方式。仅允许文档所需资源,不放开整个前端或admin/client业务路径;加载资源、深链接和原JSON地址均需独立验收。
桌面端采用“左侧目录+中间正文+右侧示例”的阅读结构:目录包括快速开始、鉴权、四个接口、回调、错误码;正文说明用途、参数和注意事项;示例区切换cURL、请求报文、成功与失败响应并支持复制。1366宽度或客户端内容区不足时将示例并入正文,保持内容完整;手机端目录可收起,内容单列,代码块独立滚动。顶部展示环境、版本、MD下载与OpenAPI下载。
验收必须同时覆盖公开页和客户端Tab:统一内容/版本、相同接口示例、无密钥或客户数据泄露、公开入口与旧链接兼容、三尺寸阅读、复制/下载/目录定位、资源与控制台正常。采用现有公共组件实现阅读体验,不改变本方案其他API修复和兼容边界。
4.1 问题和目标
当前文档Tab只显示签名原文、四条路径和简短回调说明,公开Swagger也不适合作为完整入门手册。整改为上述定制页面和客户端共用的文档内容,支持按步骤阅读、定位接口、复制示例;OpenAPI提供结构化参考,不让客户必须跳转后再自行猜字段。
4.2 页面结构
保留当前“接口概览 / 访问凭据 / 回调配置 / 接口文档 / 调用与回调记录”五个Tab、企业应用选择器、原鉴权/配置操作和日志入口。客户端仅重构文档Tab内容,并新增公开定制阅读外层,不借机调整其他业务流程。
- 顶部显示“HTTP接口接入文档”、接口版本、手册修订日期、当前环境/接口基础地址;入口为“下载MD”“OpenAPI文档”。版本来自发布的文档元数据,不用浏览器当天日期伪造更新。
- 页内目录:接入准备、鉴权、单条发送、短信状态、上行列表/详情、回调、错误码、限制与变更。桌面提供目录定位,窄屏改为可展开目录;目录不遮挡应用切换或内容。
- 每个接口采用相同顺序:用途 → 方法/路径 → 前置能力 → 参数表 → cURL/HTTP示例 → 成功/空数据/失败响应 → 易错点。四个接口分别可定位,不只放一个总清单。
- 鉴权区显示三种编号对照、明确拼接公式、可复制签名函数、固定离线校对向量;区分“签名LF”和“HTTP报文换行”,区分GET当前兼容与待整改事项。单发示例同时提供“结构示例 / 完整生成脚本 / 已计算示例”三个明确入口,完整脚本单独复制即可离线生成演示命令,不要求读者猜测头变量来源。
- 回调区分别展示回执/上行POST、验签函数、成功ACK、失败/重试规则和现存限制;不能将“平台交付成功”与“客户业务处理完成”混为一谈。
- 错误码可按code/关键词在本地文档内检索;检索只过滤文档内容,不查询客户短信。无结果显示真实空状态。
4.3 复制、下载及数据来源
- 复用现有Button等公共组件;代码块独立滚动或折行,不导致整页横向溢出。复制按钮支持键盘和可读名称,剪贴板失败提示可手动选择,不报假成功。仅文档规划,CSS实现前另按仓库规范完整阅读并验证。
- 复制示例不会执行请求,不增加“点击即发送”功能。示例保留虚构值/明确占位符,不自动填入Secret、真实短信正文或完整鉴权头;凭据只在原凭据流程展示。
- 上述限制针对真实客户鉴权信息;允许展示和复制明确标注为未开户演示凭据的完整计算样例,包括演示Secret、实际生成的UUID、时间戳、摘要和签名。页面须在示例旁持续标注“仅离线演示,未执行业务调用”,固定时间戳会过期;不能标为“测试发送成功”。示例脚本可以输出演示鉴权头,正式接入代码不得把真实完整鉴权信息输出到共享日志。
- 正文采用同一份受版本管理的文档内容生成页面和MD下载,禁止手工在TSX复制另一套长文案。若Markdown渲染支持HTML,必须禁用原始HTML或严格净化,校验链接协议,不能执行代码块。
- 参数、响应和错误契约以最终明确DTO及生成OpenAPI为准;叙述文档仍需交叉验证,并用契约检查防漂移,不宣称仅渲染同一个MD就保证与API一致。
- 环境地址/应用QPS/时间容差/跨度/分页等个性化参数从真实配置API读取;静态手册中的默认值必须标明是系统默认。文档通用内容可在配置失败时继续阅读,但不得把默认值显示为该应用真实配置。
- MD下载应包含接口/文档版本和范围,不夹带密钥、会话或客户私有配置。未开户可阅读公开文档;接口业务鉴权继续独立执行,不提供匿名发送能力。
4.4 页面状态与验收
覆盖已开通/未开通HTTP、子能力关闭、无可选应用、配置加载中/失败/重试、应用切换、首次进入、刷新、跨路由返回、深链接定位、复制成功/失败、MD下载及文档检索无结果。未开通仍能阅读通用文档,真实业务操作按权限受限;账户或角色无权访问客户端则沿用现有认证边界。
必须以1600×1000、1366×768、390×844三个尺寸核对参数表、长路径、代码块、目录和按钮;原其他Tab行为不回归。真实API参数、控制台、失败网络请求和MD/页面版本一致性均列为验收,不以隔离截图、mock或构建代替页面通过。
五、Swagger与报文示例整改
公开路径只包含四个客户业务接口。补齐三个GET响应schema、七个上行参数、可空字段、应用级限制说明和所有明确公共错误;删除重复幂等头,User-Agent可选,clientMessageId类型为string/null。回调两事件结构与独立签名规则形成完整文档定义。
每个接口必须有可见cURL请求、成功HTTP响应和典型失败响应;列表含空数据和连贯分页。公共动态签名辅助函数被各示例引用,不用过期固定签名冒充可运行示例。HTTP示例可以明确省略传输层自动生成头,但必须保留方法/路径、鉴权/内容类型、状态行和body。回调须展示平台请求及客户ACK。
验证至少包括:JSON/Python语法、Bash命令结构、GET/POST跨语言签名固定向量、LF/CRLF/字面转义差异、UUID每次变化、原body变化和重放、回调篡改、分页前后参数一致、字段/类型与生成契约一致。执行示例中的发送需专项授权,文档验证默认离线,不读取真实密钥。
5.1 最新手册完整生成示例的同步要求(2026-09-14)
本次核对工作区手册第11.2.1、11.2.2节:新增独立generate_sms_curl.py说明及演示凭据生成的完整cURL。只新增讲解与离线生成能力,没有改变签名协议、接口参数或发送授权。手册已有内容原样保护;本方案补齐其实施和验收要求。
- 结构示例可用Bash变量,但必须就近说明来源;完整生成脚本应自行产生当前时间、UUID v4、原始JSON摘要和HMAC,生成的演示命令不遗留未解释的头变量。它只构造并输出命令,不调用网络或启动curl。
- 固定已计算样例同时保存演示Secret、timestamp、nonce、原始body、SHA256和HMAC;能从公开的虚构输入完全复算。不得用真实账户凭据做公开测试向量,也不将旧时间戳当成实时可用值。
- 完整脚本与公共签名函数用同一组向量交叉验证;正文只序列化一次。校验shlex引用后cURL携带的body与签名字节一致,涵盖中文、引号、反斜杠和内容中的换行;注明Bash/UTF-8,不能冒称PowerShell可直接执行。
- 页面、MD下载和示例区共用这些版本化示例。复制脚本不得截掉import、演示值、生成逻辑或用途说明;固定样例不能被页面“刷新”成伪造成功结果,不要求在浏览器输入真实Secret生成签名。
- 离线验收应验证脚本UUID v4、HMAC、两个语言实现复算、Bash语法、原字节一致与无自动业务调用。真实客户鉴权和发送/回调闭环另列授权验收,不能用演示密钥无权限的结果代替。
本次离线复算确认手册新增固定摘要和HMAC一致,独立Node计算吻合,生成/固定cURL均通过Bash语法检查;没有执行curl或发送请求。对应TC-HTTP-REMED-DOC-08~09;页面实现及扩展特殊字符验收仍待执行。
六、分阶段交付与完成判定
| 阶段 | 交付物 | 完成标准 |
|---|---|---|
| D:文档讨论(当前) | 手册、整改方案、需求/用例/进度同步 | 四点建议及页面整改纳入,示例离线可核对;未确认的兼容/数据设计显式登记 |
| I:设计细化 | R01兼容清单、R03数据/恢复设计、R04/R06兼容映射、页面实施设计 | 业务规则和重大范围变化由用户决定;不自动按草稿实施 |
| C:代码实施 | 定向最小修复及页面/契约更新 | 后续明确授权后才执行;先复现后修改,不夹带旧工作区内容 |
| V:真实验收 | API/PG/Redis/受控回调及浏览器证据 | 定向/回归/类型/构建/质量门禁按范围通过,未执行项明确;真实短信发送另授权 |
| R:交付发布 | 精确提交与两环境独立验收 | 提交、推送、测试/预生产分别授权,使用标准发布工具;不因文档完成就发布 |
本轮验收用例新增TC-HTTP-REMED-DOC-01~06,原HTTP-A01~A08、TC-HTTP-ASSESS-*与TC-HTTP-GUIDE-*继续作为历史和后续验收来源。以下历史评估保留其原始时点结论,不是本次重新执行结果。
历史附录:2026-09-10评估原文(保留证据,非当前实施状态)
客户 HTTP 接口与对接文档评估(2026-09-10)
本轮为评估与只读核验,不实施运行代码修复、不修改客户配置、不发送短信或回调、不提交或发布。本报告补充现状和待办,不替代第一版需求的“HTTP 客户接口第一版”及后续自动投递规则;非 CMPP 拒绝分支的业务选择仍遵循最新引流方案,不在本评估中重新扩大回执范围。
1. 客户从哪里看
- 公网文档:客户 HTTP 接口 Swagger。2026-09-10 本轮直接 HTTPS GET 返回 200,HTML 3168 字节。
- 机器可读契约:OpenAPI JSON。直接 HTTPS GET 返回 200,JSON 4142 字节,公开路径只有四个客户接口。
- 登录客户平台后:左侧“接口对接”→选择企业应用→“接口文档”。前端路由为
#/client/http-api,另有接口概览、访问凭据、回调配置、调用与回调记录四个页签。该入口由当前源码确认,本轮未做登录页面浏览器验收。 - 客户接口基础地址:
https://api.lisglo.com/api/openapi/v1。管理站域名不能替代客户接口域名。
公开 Swagger 当前主要是接口清单;客户登录页另有签名原文和回调验签简述。两者均不等于一份可以让新客户独立完成对接的完整手册。
2. 版本和现场证据
| 项目 | 本轮事实 |
|---|---|
| 本地 | main,HEAD 86947827cc80dbc363514b3b52c2576546f5337b |
| 真实远端 | git ls-remote 返回 6d63eb5452ffc7c802960d044bf598cc8646564d,本地领先 3 个提交 |
| 预生产 | 2026-09-10 14:31:27 CST 读取 .deployed-commit 为 809175b544f2526891ba6d2dece1a50eadaf57a0 |
| 代码对照 | 预生产与本地的 service、guard、controller、exception filter、body parser、main、客户 HTTP 页面共 7 个源文件逐行内容一致;文件原始字节摘要不同,未将其冒称字节一致 |
| 队列库 | 本地与预生产 BullMQ 均为 5.79.2,现场校验逻辑与隔离验证使用版本一致 |
| PostgreSQL | 只读事务:HTTP 配置 16 条,其中 enabled 为 true 的 3 条;OpenApiRequest completed=1、failed=2,超过 10 分钟 processing=0;HttpWebhookDelivery 与 HttpWebhookAttempt 均无记录 |
| Redis | 只读取 HTTP 回调队列计数:wait=0、active=0、delayed=0、failed=0、completed=3。历史完成任务数不能代替数据库业务投递记录或客户 ACK 证据 |
| 工作区保护 | 暂存区为空;保留原有发布工具、治理文档、metrics 等修改;评估期间发现另一会话更新发布记录,同样保留。未切换分支 |
结论依据是当前公开契约、上述源码、真实数据库和队列只读证据。现有业务样本极少,不能据此宣称客户生产接入稳定、重试成功或容量达标。
3. 能力评价
当前范围明确为单号码提交、短信状态查询、上行列表和上行详情四个接口,另有回执/上行 Webhook 的配置与异步投递实现。
已经具备有价值的基础:应用凭据确定身份,应用范围查询,HMAC-SHA256、时间容差、Redis nonce 防重放和应用级 QPS、独立 IP/CIDR 白名单、密钥加密存储、数据库幂等唯一约束与响应快照。单发接入已有签名/模板和发送链路,而不是直接伪造成功。回调目标有 HTTPS、DNS/IP 安全校验、禁止重定向,事件与投递有数据库唯一约束。
总体判断:基础接入框架已形成,但在正式扩大客户自助接入前,应先处理下面的签名一致性、回调重试和故障恢复问题,并补齐客户文档。批量发送、余额查询、模板管理或多语言 SDK 属于可选后续范围,缺少这些不自动构成本期 Bug。配置中的 QPS 数字也不是实测吞吐能力。
4. 问题与优先级
HTTP-A01 / P1:无请求体 GET 的签名规则与文档不一致
位置:api/src/open-api/open-api-auth.guard.ts:51-53、api/src/http-body-limits.ts、src/apps/client/ClientHttpApiPage.tsx:120-126。
公开接口鉴权计算 SHA256(request.rawBody ?? JSON.stringify(request.body ?? {}))。使用实际 Nest 初始化参数和项目 body parser,在仅含探针控制器的本地隔离应用中发起真实无 body GET:无论是否带 JSON Content-Type,rawBody 和 body 均不存在,最终摘要是 {} 的 44136f…aff8a,不是空字节的 e3b0c4…b855。文档却只写 SHA256(rawBody)。
客户按通常的空请求体签名方式实现 GET,会与服务端验签计算不一致。应明确 UTF-8 原始字节、空体、路径和 query 的规范,并统一实现与测试;变更前调查现有客户是否已使用 {} 兼容规则,不能直接无提示切换协议。当前代码的 PATH 不含 query,也须明确说明,不将未签 query 单独断言为已发生安全事件。
证据等级:实际解析器隔离 HTTP 运行 + 鉴权源码;未用真实客户凭据执行线上认证查询。
HTTP-A02 / P1:自动回调重试任务无法正常建立
位置:api/src/open-api/open-api.service.ts:714-736。
首次可重试失败后,代码先把数据库投递状态改为 retrying,再使用 ${deliveryId}:${attemptNo + 1} 添加延迟任务。BullMQ 5.79.2 的实际校验对这种带一个冒号的编号抛出 Custom Id cannot contain :。隔离调用实际依赖校验器已复现;首次编号 delivery-example 通过,重试编号 delivery-example:2 被拒绝。
因此在走到该分支时,预期重试任务建不起来,数据库还可能停在 retrying。应使用不含冒号的稳定编号,并覆盖数据库状态与 Redis 入队失败后的恢复。官方也要求自定义编号避免冒号,见 BullMQ Job IDs。
手工重试当前编号有两个冒号,在该版本兼容分支下通过校验;不能据此误报“手工重试必然同样失败”,但后续宜一并统一编号规则。预生产本次无投递记录,未证明已经造成实际客户回调故障;没有触发、补发或重投回调。
HTTP-A03 / P1:幂等请求与回调入队存在故障恢复窗口
位置:api/src/open-api/open-api.service.ts:272-370,532-559,714-736。
发送请求先落 processing,业务创建和响应快照更新是后续独立操作;进程在中间终止可能留下永久 REQUEST_PROCESSING,而实际业务是否已创建需要核对。回调事件/投递落库与 Redis 入队也分步执行。在本次检索的 OpenAPI 路径中没有找到清理或重建过期 processing/pending/retrying 的耐久恢复机制。
建议先设计关联请求与业务记录的确定性标识、状态恢复和持久化待办,再补故障注入验收;不能简单把超时请求重新发送,也不能自动重投历史回调。预生产当前无过期 processing/retrying,该项是代码窗口风险,不是现场已发生故障。
HTTP-A04 / P1:客户接口契约不完整且部分标注错误
位置:公开 client-docs-json、api/src/open-api/open-api.controller.ts:21-45、open-api.dto.ts:32-33。
- 三个 GET 的 200 响应均无 schema、字段定义和示例。
- 上行列表的
startTime/endTime/mobile/accessNumber/keyword/limit/cursor七项查询参数全部未出现在 Swagger。 - POST 的 Idempotency-Key 被以不同大小写重复标注,User-Agent 被标成必填,而实现参数可选。
- 响应
clientMessageId被生成成 object,可实际为 string/null。 - 错误响应、业务码、回调请求体、验签范例、ACK 与重试规则没有形成完整公开契约。
客户难以独立构造请求、解析响应或生成可靠客户端。应以显式请求/响应 DTO 和实际错误为唯一契约来源,自动检查生成的 OpenAPI,不手工维护另一份漂移 JSON。
HTTP-A05 / P2:公共入参约束需要完整落实
位置:api/src/open-api/open-api.dto.ts:3-15、open-api.service.ts:260-313,464。
公开 DTO 只有 Swagger 注解,未施加运行时 class-validator 约束。服务层校验手机号、正文非空和幂等键,但 clientMessageId 的类型和文档所写 128 长度未完整校验;上行 limit 用 Number/clamp,未拒绝小数,可能把非整数 take 传给 Prisma。应补明确 DTO,覆盖类型、长度、整型边界、非法日期/cursor,并返回稳定 4xx。
证据为源码审查;未向预生产发畸形请求,也未把推断的 Prisma 报错作为现场复现。
HTTP-A06 / P2:未知异常会向客户返回内部错误正文
位置:api/src/open-api/open-api-exception.filter.ts:8-17。
非 HttpException 直接取 exception.message 作为 detail。隔离运行传入标记异常后,返回的 500 正文原样包含该内部标记;数据库或其他依赖异常因此有泄露实现细节的风险。应向客户固定返回公共错误文案与关联 ID,内部日志保留诊断信息;同时统一首次失败与幂等重放的错误码。
未发现或导出真实客户秘密;该结论不等于已发生凭据泄露。
HTTP-A07 / P2:上行详情直接返回数据库模型
位置:api/src/open-api/open-api.service.ts:503-512。
查询有 applicationId 与 matched 限定,这是正确隔离基础;但详情不设 select/响应映射,直接返回模型,公开了内部租户、应用、通道、事件和匹配字段,并让客户契约跟着模型变化。应只输出客户需要的字段,并保持列表/详情命名一致。本轮没有跨租户泄露证据。
HTTP-A08 / P2:对接故障排查和测试覆盖不足
当前调用记录主要从 OpenApiRequest 读取发送请求,不代表全部查询请求、验签失败和限流日志。客户文档还缺少“哪个编号提供给客服”、时间偏差、白名单、nonce 重用、429 与 409 的处理方法。
本次现有 OpenAPI service 与管理 DTO 定向测试为 2 套 14 项通过,但没有因此发现 GET 空体签名、自动重试 jobId 等问题,说明测试没有覆盖完整协议和真实队列失败路径。应增加契约/鉴权/失败恢复测试,不以增加普通 mock 用例数量代替。
5. 客户文档最小补齐范围
- 快速开始:申请开通、基础地址、凭据与 Secret 的区别、权限/白名单、可复制的完整签名示例。先给安全的查询示例;发送示例明确 202 只是受理。
- 签名规范:五行原文、UTF-8、原始 JSON 字节、秒级时间戳、nonce 格式、大小写、PATH 与 query 边界、空体规则;每次网络重试更新 timestamp/nonce,保持同一业务 Idempotency-Key 和相同原始 body。
- 四个接口:全部参数、默认值/上限、类型/可空性、成功和失败示例、状态枚举;说明 messageId 与 clientMessageId 的查询关系、上行游标和时间窗口。
- 回调协议:两类事件完整 payload、独立回调密钥、原始体验签、eventId 去重、2xx ACK、超时和重试策略、乱序/重复及人工处理流程。写明当前实现缺陷修复前的限制,不能把预期重试写成已验证能力。
- 排错与兼容:公共错误码及处理动作、关联 ID、QPS、时间与编码、密钥轮换、版本和变更记录、支持范围。不宣传未验收的峰值容量。
建议先修 A01/A02,并同步文档;A03 单独形成跨持久化边界的设计与故障恢复验收;随后完成公共 DTO/契约和客户手册。此建议不构成本轮运行代码修改或发布授权。
6. 验证记录及交付状态
- 已执行:公开 Swagger/JSON HTTPS GET;预生产版本及 7 文件只读对照;PostgreSQL 只读聚合;Redis 队列计数;真实 Nest body parser 的本地隔离 GET;当前 BullMQ 校验器和异常过滤器隔离复现;现有定向 Jest 2 套 14 项通过(36.39 秒)。
- 未执行:真实客户凭据认证、短信发送、Gateway/供应商链路、真实回调与重试、客户页面浏览器交互、容量与故障注入。未启动完整业务 AppModule,不创建发送/回调 Worker;不使用隔离结果冒称真实业务闭环。
- 本轮只有本报告与测试用例/进度文档追加,无运行代码修改;未提交、未推送、未部署测试、未部署预生产。纯评估文档无需重跑全量构建或 CSS 门禁。
- 证据目录:
%TEMP%/cmpp-http-api-assessment-20260910/,包括openapi.json、swagger.html、preproduction-readonly.json、preproduction-sources.json、isolated-probes.cjs、isolated-results.json。只读现场脚本只输出计数、版本和源码,不导出凭据或业务正文。 - 后续验收见系统用例的
TC-HTTP-ASSESS-*;执行状态见测试进度。
原手册维护附录归档(内部实施记录,不进入公开阅读正文)
附录:本稿范围与维护依据
本稿是客户阅读版,供审阅后继续完善;尚未替换客户端页面或上线 Swagger。它解释当前接口,不修改鉴权、计费、发送、投递业务规则,也不替代HTTP 接口整改方案(含历史评估)。其中缺陷说明保留到实际修复及验收后再更新。
2026-09-14 核验:本地、真实 Git 远端、预生产部署标记均为 d13ca0713abd6afbea5a62af39bcbb876b8bb186。预生产鉴权 guard、OpenApiService、controller 经换行规范化的文本摘要与本地一致;公开 Swagger/JSON 均 HTTP 200。文档依据当前 controller、DTO、guard、service、异常过滤器、回执/上行事件生产代码和 Prisma 字段;未把历史数据库样本当作本次业务验收。
内部追踪:测试用例、测试与实施进度。后续实现发生变化时,签名函数、字段表、错误码、回调示例和在线契约须一起更新。
2026-09-14 真实HTTP全量验收发现的Webhook DNS缺陷
测试环境 f0e8434 上,专用模拟应用已产生真实送达回执,但5个Webhook事件均在2次尝试后失败,lastError为 Invalid IP address: undefined,受控HTTPS接收端无请求。Node自动地址族选择以 all=true 调用自定义lookup,旧实现仍返回单地址三参数,违反回调契约。保留原SSRF解析、私网拒绝及地址固定,只按all选项返回固定地址数组或原单地址。未改变重试、计费、发送与租户规则。真实Node HTTPS连接旧实现失败/修正实现200,本地真实HTTP连接回归及API全量73套783项、类型构建通过;线上修复与Webhook全场景验收尚待标准发布完成。用户已另行授权本修复提交、推送及测试发布,不涉及预生产。完整结果将登记 HTTP全量验收。