# 客户 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时使用标准工具掩码入口。 ## 一、目标与文档分工 - [客户接口手册](client-http-api-guide.md):面向客户开发人员,说明当前可用调用方式、参数/响应、报文、接入步骤和限制;拟变更行为明确标注,不能伪装成已上线。 - 本方案:维护当前问题、已采纳原则、实施范围、兼容影响、页面整改和验收条件。历史HTTP-A01~A08作为问题来源,均未因文档修改而关闭。 - [需求](first-version-development-requirements.md):维护业务及页面要求;[用例](system-functional-test-cases.md)维护验收;[进度](testing-progress.md)维护实际执行证据。 - 顺序固定为:继续讨论并确定手册 → 完善/确定整改方案 → 用户授权后修改代码与验收 → 按独立授权提交/发布。文档可先完善,但不预先宣称业务功能通过。 ## 二、本次已采纳的接口与表述原则 | 主题 | 已采纳原则 | 兼容与现状 | | --- | --- | --- | | 上行时间 | 分开说明默认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发布记录](release-20260914-test-http-api.md)为准,“文档已补”本身不构成线上修复证据。 | 编号 | 来源 | 目标与最小范围 | 验收与依赖 | | --- | --- | --- | --- | | 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内容,并新增公开定制阅读外层,不借机调整其他业务流程。 1. 顶部显示“HTTP接口接入文档”、接口版本、手册修订日期、当前环境/接口基础地址;入口为“下载MD”“OpenAPI文档”。版本来自发布的文档元数据,不用浏览器当天日期伪造更新。 2. 页内目录:接入准备、鉴权、单条发送、短信状态、上行列表/详情、回调、错误码、限制与变更。桌面提供目录定位,窄屏改为可展开目录;目录不遮挡应用切换或内容。 3. 每个接口采用相同顺序:用途 → 方法/路径 → 前置能力 → 参数表 → cURL/HTTP示例 → 成功/空数据/失败响应 → 易错点。四个接口分别可定位,不只放一个总清单。 4. 鉴权区显示三种编号对照、明确拼接公式、可复制签名函数、固定离线校对向量;区分“签名LF”和“HTTP报文换行”,区分GET当前兼容与待整改事项。单发示例同时提供“结构示例 / 完整生成脚本 / 已计算示例”三个明确入口,完整脚本单独复制即可离线生成演示命令,不要求读者猜测头变量来源。 5. 回调区分别展示回执/上行POST、验签函数、成功ACK、失败/重试规则和现存限制;不能将“平台交付成功”与“客户业务处理完成”混为一谈。 6. 错误码可按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。只新增讲解与离线生成能力,没有改变签名协议、接口参数或发送授权。手册已有内容原样保护;本方案补齐其实施和验收要求。 1. 结构示例可用Bash变量,但必须就近说明来源;完整生成脚本应自行产生当前时间、UUID v4、原始JSON摘要和HMAC,生成的演示命令不遗留未解释的头变量。它只构造并输出命令,不调用网络或启动curl。 2. 固定已计算样例同时保存演示Secret、timestamp、nonce、原始body、SHA256和HMAC;能从公开的虚构输入完全复算。不得用真实账户凭据做公开测试向量,也不将旧时间戳当成实时可用值。 3. 完整脚本与公共签名函数用同一组向量交叉验证;正文只序列化一次。校验shlex引用后cURL携带的body与签名字节一致,涵盖中文、引号、反斜杠和内容中的换行;注明Bash/UTF-8,不能冒称PowerShell可直接执行。 4. 页面、MD下载和示例区共用这些版本化示例。复制脚本不得截掉import、演示值、生成逻辑或用途说明;固定样例不能被页面“刷新”成伪造成功结果,不要求在浏览器输入真实Secret生成签名。 5. 离线验收应验证脚本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) 本轮为评估与只读核验,不实施运行代码修复、不修改客户配置、不发送短信或回调、不提交或发布。本报告补充现状和待办,不替代[第一版需求](first-version-development-requirements.md)的“HTTP 客户接口第一版”及后续自动投递规则;非 CMPP 拒绝分支的业务选择仍遵循最新引流方案,不在本评估中重新扩大回执范围。 ## 1. 客户从哪里看 - 公网文档:[客户 HTTP 接口 Swagger](https://api.lisglo.com/api/client-docs)。2026-09-10 本轮直接 HTTPS GET 返回 200,HTML 3168 字节。 - 机器可读契约:[OpenAPI JSON](https://api.lisglo.com/api/client-docs-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](https://docs.bullmq.io/guide/jobs/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. 客户文档最小补齐范围 1. **快速开始**:申请开通、基础地址、凭据与 Secret 的区别、权限/白名单、可复制的完整签名示例。先给安全的查询示例;发送示例明确 202 只是受理。 2. **签名规范**:五行原文、UTF-8、原始 JSON 字节、秒级时间戳、nonce 格式、大小写、PATH 与 query 边界、空体规则;每次网络重试更新 timestamp/nonce,保持同一业务 Idempotency-Key 和相同原始 body。 3. **四个接口**:全部参数、默认值/上限、类型/可空性、成功和失败示例、状态枚举;说明 messageId 与 clientMessageId 的查询关系、上行游标和时间窗口。 4. **回调协议**:两类事件完整 payload、独立回调密钥、原始体验签、eventId 去重、2xx ACK、超时和重试策略、乱序/重复及人工处理流程。写明当前实现缺陷修复前的限制,不能把预期重试写成已验证能力。 5. **排错与兼容**:公共错误码及处理动作、关联 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`。只读现场脚本只输出计数、版本和源码,不导出凭据或业务正文。 - 后续验收见[系统用例](system-functional-test-cases.md)的 `TC-HTTP-ASSESS-*`;执行状态见[测试进度](testing-progress.md)。 ### 原手册维护附录归档(内部实施记录,不进入公开阅读正文) ## 附录:本稿范围与维护依据 本稿是客户阅读版,供审阅后继续完善;尚未替换客户端页面或上线 Swagger。它解释当前接口,不修改鉴权、计费、发送、投递业务规则,也不替代[HTTP 接口整改方案(含历史评估)](http-api-assessment-20260910.md)。其中缺陷说明保留到实际修复及验收后再更新。 2026-09-14 核验:本地、真实 Git 远端、预生产部署标记均为 `d13ca0713abd6afbea5a62af39bcbb876b8bb186`。预生产鉴权 guard、OpenApiService、controller 经换行规范化的文本摘要与本地一致;公开 Swagger/JSON 均 HTTP 200。文档依据当前 controller、DTO、guard、service、异常过滤器、回执/上行事件生产代码和 Prisma 字段;未把历史数据库样本当作本次业务验收。 内部追踪:[测试用例](system-functional-test-cases.md)、[测试与实施进度](testing-progress.md)。后续实现发生变化时,签名函数、字段表、错误码、回调示例和在线契约须一起更新。