# CMPP 短信平台第一版开发需求文档 ## 0. 文档前提 本文基于当前前端设计原型整理,用于交给 Codex 或开发团队执行第一版落地开发。 当前确认:第一版保留短信业务,排除彩信功能;账户按现金余额和授信额度计费,人工充值和充值记录进入第一版开发范围,套餐、短信余量、账单流水页面和公开交易查询 API 不进入第一版。彩信服务、彩信应用/签名/模板 Tab,以及运营端彩信相关菜单标记为“待开发”;业务性能指标为“平台可稳定入队并调度 500 条短信/秒,实际向通道 submit 受通道限速配置控制”。 ### 0.1 运营端细节要求(2026-07-15) - 企业签名的站点字段统一显示为“引流信息”,列表、表单和详情不展示引流信息提交时间。 - 企业应用的企业代码必须始终等于 CMPP 6 位账号,由系统同步且不可单独编辑;账号留空时,创建应用时自动生成二者。每任务号码数超过应用上限时拒绝整个任务并提示拆分,不允许静默截断。 - 企业应用支持配置纯数字“应用扩展码”。开启“客户接入号填充”后才显示并允许编辑纯数字填充前缀,客户侧 `Src_Id` 等于“填充前缀 + 应用扩展码”;关闭时前缀必须清空且客户侧 `Src_Id` 等于应用扩展码。计算结果全局唯一且不超过 CMPP 的 21 位边界。 - 客户接入号填充只解决客户系统的最小长度兼容,NestJS 必须在创建任务、冻结余额和入队前精确校验客户 `Src_Id`。上游实际 `Src_Id` 始终为“通道基础接入号 + 应用扩展码”,不得把客户填充前缀带入上游;新短信保存客户侧接入号和应用扩展码快照,补发继续使用原快照。未配置扩展码的历史应用保持通道基础接入号行为。 - 手机号段支持真实删除;报备字段库展示被通道报备配置引用的通道数,引用数大于 0 时前后端均禁止删除,未引用字段才允许真实删除。 - 企业应用连接详情只展示当前已连接会话;断开或心跳超时会话从活跃连接表删除,不保留为连接历史。 - 短信审核批量通过必须基于明确勾选,只处理已选待审核任务;不得默认通过当前筛选结果或全部数据。 - 下游投递记录支持创建日期范围筛选,并将日期条件下推 PostgreSQL 列表与 Dashboard 聚合。 - 下游投递记录和恢复状态管理的多条件筛选区必须遵循共享查询控件规范:普通条件、日期范围和操作按钮使用统一宽度,空间不足时按完整控件自动换行,不允许为维持单行而压缩、重叠或截断控件;移动端条件整行展示,查询与重置按钮保持清晰的独立操作区。 - 短信模板变量插入到文本框当前选区或光标位置,插入后光标移动到变量之后。 - 运营端短信记录使用自适应信息卡片,发送详情按概览、短信内容、通道回执、状态和分片审计分组,失败原因使用独立警示区域突出;该项只调整前端展示,不改变短信记录后端语义。 - 人工充值弹窗不要求填写操作人,操作身份从当前登录会话和后端操作日志取得。 ## 1. 项目目标 建设一个短信平台第一版,支持企业客户在客户端完成短信应用、模板、签名、号码导入、短信发送、批量任务查询、发送明细查询、上行短信查询;支持运营端完成企业管理、企业应用/签名/模板管理、审核、通道配置、通道签名报备、报备任务导出/回执导入、发送监控、任务进度、短信记录、上行记录、安全控制和系统管理。 第一版必须满足: - 支持最高 500 条短信/秒的提交吞吐。 - 支持批量任务异步发送,前端提交后可查询任务进度。 - 支持通道级限速、失败重试、回执同步和发送记录追踪。 - 支持企业、应用、签名、模板、通道、通道组、报备任务等核心配置数据的后台维护。 - 彩信功能仅保留菜单占位或隐藏,不进入第一版开发范围。 - 账户现金余额、授信额度、人工充值和充值记录进入第一版范围,并与发送记录形成可对账闭环;不提供套餐或短信余量。 ## 2. 角色与权限 ### 2.1 客户端企业管理员 可管理本企业短信应用、短信模板、短信签名、发送任务、上行短信、企业认证、企业用户和系统日志。 ### 2.2 客户端普通用户 可按授权查看应用、模板、签名、发送任务和发送明细;是否允许创建和发送需由企业管理员配置。 ### 2.3 运营端管理员 可管理全平台企业、应用、签名、模板、审核、通道、通道组、报备任务、发送监控、短信记录、上行记录、黑名单、敏感词、用户、手机号段库和引流字段库。 ### 2.4 运营审核员 可处理企业认证审核、短信审核、短信模板审核、签名/模板/发送内容相关审核。 ## 3. 第一版范围 ### 3.1 客户端范围 保留并开发: - 工作台 - 短信发送 - 查看批量任务 - 短信发送详情 - 查看上行短信 - 短信应用 - 模板管理 - 签名与引流信息 - 账户余额与充值记录 - 账单流水 - 企业认证 - 用户管理 - 系统日志 第一版不展示独立“账号设置”菜单,退出登录、修改密码统一放在右上角用户头像下拉菜单。 暂不开发: - 彩信服务分组下全部菜单 ### 3.2 运营端范围 保留并开发: - 运营看板、发送监控、数据统计、客户管理 - 企业管理、企业应用管理、企业签名管理、企业模板管理 - 企业认证审核、短信审核、短信模板审核 - 短信通道管理、短信通道组管理 - 报备任务、报备记录 - 短信任务进度、短信记录、短信上行记录 - 充值记录、账单流水、计费规则 - 企业黑名单、全局黑名单、敏感词管理 - 用户管理、手机号段库、引流信息字段库 暂不开发: - 彩信模板审核 - 彩信通道管理 - 彩信任务进度 - 彩信记录 - 企业应用管理的彩信应用 Tab - 企业签名管理的彩信签名 Tab - 企业模板管理的彩信模板 Tab ## 4. 核心业务流程 ### 4.1 企业入驻与认证 1. 客户端企业管理员提交企业认证资料。 2. 运营端在企业认证审核中查看资料并审核通过或驳回。 3. 审核通过后企业可创建短信应用、签名、模板并发起短信发送。 4. 驳回时必须记录驳回原因,客户端可查看并重新提交。 ### 4.2 短信应用管理 1. 客户端创建短信应用,填写应用名称、场景、回调地址、限额等信息。 2. 运营端可查看企业应用,并支持按企业名称、应用名称、状态搜索。 3. 运营端可启用、停用、编辑应用。 4. 应用必须归属企业,并关联后续签名、模板和发送任务。 5. 短信应用必须配置发送队列等级:普通队列或优先队列。未配置时默认普通队列;优先队列用于验证码、登录确认、交易通知等高时效短信,普通队列用于营销、通知等常规短信。 6. 发送队列等级属于真实业务配置,必须保存到后端数据库,并在客户端、运营端创建/编辑应用时展示和可修改;不得只作为前端展示字段。 7. 运营端代企业新增短信应用时,第一步选择企业必须使用项目通用 Select/下拉控件,选项来自真实企业 API,支持加载中、空数据、错误态,不允许写死企业列表。 8. 短信应用必须有独立 6 位数字 CMPP 接入账号 `cmppAccount`;运营端添加/编辑应用时可显式配置,留空时后端自动生成且全局唯一。 9. 短信应用必须有应用级客户侧企业代码 `cmppEnterpriseCode`,运营端添加/编辑应用时可自定义;不得从上游通道 `SmsChannel.enterpriseCode` 透传。 10. 短信应用接口密码 `passwordCipher` 新建时默认随机生成 16 位 UUID 片段,运营端可手工修改;编辑时留空不覆盖原密码。 11. 应用 `AppID` 是平台内部应用标识,用于页面展示、复制参数和工单定位,不作为 CMPP bind/login 认证参数。 12. 短信应用必须可配置客户侧 CMPP 最大连接数 `cmppMaxConnections`;客户侧提交窗口 `cmppWindowSize` 后端保留默认值,当前第一版不在运营端展示或要求运营配置,待 Gateway 入站侧按应用窗口真正限流后再开放为高级配置。 13. 短信应用必须恢复设计基线中的“短信接口”开关,字段为 `interfaceEnabled`,默认开通;关闭后客户端/API 发送链路、客户侧 CMPP Gateway bind/login 和 submit 都必须被真实后端拒绝,不允许只在前端隐藏入口。 14. CMPP 协议类型当前第一版仅允许 `CMPP2.0`,字段保持 `interfaceType=cmpp20`;HTTP 不写入该字段,而是通过独立的 `SmsApplicationHttpConfig` 总开关和子能力配置开通。前后端仍必须拒绝把 `interfaceType` 直接改成 `http` 等无效协议值。 ### 4.3 签名与引流信息 1. 客户端和运营端新增、编辑短信签名时,签名名称必须填写完整中文黑括号格式,例如 `【某某科技】`;输入过程中禁止录入普通空格、换行、制表符、不换行空格、零宽字符、BOM、变体选择符等空白或不可见字符,非法按键/粘贴不得进入受控输入值并需立即给出错误提示。缺少括号、英文方括号、重复括号、空括号或括号外附加文本均不得提交。NestJS API 必须执行同样的字符与格式校验并以完整格式写入 PostgreSQL,不能只依赖前端按钮状态。 2. 运营端企业签名管理查看签名资料。 3. 签名需完成企业内部审核和通道报备,状态包括草稿、待审核、已通过、已驳回、报备中、报备通过、报备失败。 4. 已通过且报备通过的签名才允许发送。 5. 签名列表、详情、审核、报备、导入导出、模板选择和短信预览统一展示恰好一层完整黑括号;历史非规范签名通过 migration 规范化,任何页面不得再次拼成 `【【签名】】`。 ### 4.4 模板管理与审核 1. 客户端创建短信模板,填写模板名称、短信内容、变量、应用、签名。 2. 短信模板必须选择签名,模板内容必须以完整中文括号签名 `【签名】` 开头。客户端和运营端选择签名时自动把完整签名填入内容开头,切换签名时替换原前缀而不是重复追加;内容输入框明确提示该规则,字符数和计费条数按“签名 + 正文”完整内容计算。 3. NestJS 创建、编辑和提交审核时必须校验所选签名属于模板企业/应用,且模板内容以该签名开头;不得只依赖前端自动填充。 4. 系统校验敏感词、字数、变量格式和签名匹配。 5. 运营端短信模板审核可通过或驳回模板。 6. 运营端在企业模板管理中代企业添加的短信模板,保存后应直接置为已通过 `approved`;客户端自行创建并提交的模板仍按审核流程处理。 7. 审核通过的模板才允许在短信发送中选择。 ### 4.5 短信发送 1. 客户端选择短信应用、签名、模板。 2. 输入或导入接收手机号,支持手动输入和文件导入。 3. 系统校验号码格式、黑名单、手机号段、企业余额/额度、模板变量。 4. 客户端选择立即发送或定时发送。 5. 提交后生成批量任务,任务进入待审核或待发送状态。 6. 系统执行风控规则,命中拒绝规则则直接拒绝并记录原因,命中人工审核规则则进入运营端短信审核。 7. 若命中审核策略,运营端短信审核通过后进入发送队列,审核页面必须展示进入审核的原因。 8. 发送服务按通道组路由、通道限速、企业限速执行提交。 9. 平台批量任务只记录客户端创建的发送任务;API 调用和 CMPP 对接发送不进入批量任务。 - 运营端和客户端“短信任务进度”只查询 `SmsBatchTask.sourceType=client`。 - CMPP/API/通道测试可使用内部批次承载风控、计费、队列、重试和回执关联,但不得出现在客户批量任务列表。 - 客户端批量任务列表、详情、短信明细和取消操作必须同时校验当前企业和 `sourceType=client`。 10. 所有来源的短信,包括平台批量任务、API 调用、CMPP 对接发送,全部按手机号维度进入短信记录。 11. 任务进度、发送详情和短信记录实时或准实时更新。 12. 企业应用“不符合模板的短信”配置为 `manual_review` 时,合法的 CMPP Submit 在模板不匹配后进入人工审核;配置为 `reject` 时直接拒绝并返回 `REJECTD` Deliver Receipt;配置为 `direct_send` 时必须识别并绑定已审核通过的完整括号签名,继续执行风控、余额、通道组路由、具体通道签名报备和 Gateway 真实提交,不得因模板未匹配落入 `reject`,也不得绕过其他发送校验。 - CMPP 入站模板匹配必须支持模板正文中的 `${variable}` 占位符。固定文本需完整匹配,占位符至少匹配一个字符;同名占位符重复出现时取值必须一致。匹配成功后应绑定真实 `templateId`,并将提取的变量值传入风控,不能只用整段正文数据库精确相等判断。 13. CMPP 模板不匹配审核支持短窗口内容指纹聚合:只有同一企业应用、同一 CMPP 账号、规范化后内容 SHA-256 完全一致且位于同一时间窗口的短信才能合并为一个审核任务。默认窗口 10 秒,可通过 `CMPP_TEMPLATE_REVIEW_WINDOW_MS` 调整。 14. 聚合审核不合并短信记录、计费或回执:每个手机号仍有独立 `SmsMessageRecord/messageId/sequenceId`。审核通过后逐条进入真实路由和上游提交;审核驳回后逐条释放冻结并产生客户侧 `REJECTD` 回执。 15. 人工审核只覆盖模板不匹配;签名必须以完整中文中括号前缀 `【签名】` 识别,并使用包含中括号的完整名称匹配签名库。入站候选签名只要求 `auditStatus=approved`,不得以全局 `reportStatus` 提前拒绝;报备通过状态必须在后续路由和最终提交前按具体通道校验。签名不合法、风控直接拒绝或余额不足不得因内容聚合而绕过。 16. 发送入队必须按短信应用的队列等级分流到优先队列或普通队列;同等条件下优先队列消息必须先于普通队列消息被 Send Worker 消费并提交 Gateway。 17. 优先队列只能改变待发送消息的调度顺序,不得绕过企业/应用状态、签名模板审核、通道报备、余额/授信、黑名单、风控、通道组路由、通道限速和 Gateway 连接可用性校验。 18. 同一队列内部按创建时间、任务顺序和手机号拆分顺序保持 FIFO 或可解释的稳定排序;优先队列插队时必须可在 trace 或任务日志中追踪队列等级和入队时间。 ### 4.6 通道配置与路由 1. 运营端配置短信通道,包括通道名称、运营商、单价、网关地址、端口、企业代码、账号、密码、接入号、协议参数、启停状态。 2. 通道运营商支持移动、联通、电信和三网;三网通道可作为移动、联通、电信的通配通道。 3. 通道必须配置发送地区,发送地区为全国或 34 个省级地区之一;单个通道只能选择一个发送地区。 4. 运营端配置短信通道组,通道组必须选择且只能选择一个运营商:移动、联通或电信;通道组不允许选择三网。通道组定义通道优先级、运营商分配、省网路由、全国路由、权重、失败补发开关和补发时间上限。 5. 企业不配置默认通道组;企业应用必须单独配置至少一个运营商通道组,否则页面不可保存,发送时也必须直接失败。 6. 一个企业应用可以分别绑定移动、联通、电信通道组;可以只绑定其中一类或两类,但不能一个都不绑定。 7. 发送时先按运营商识别结果分流:移动短信走移动通道组,联通短信走联通通道组,电信短信走电信通道组;识别不出的号码走移动通道组。 8. 运营商识别以可配置的号码前缀正则表达式为准,通常匹配手机号前 3 到 4 位;手机号段库只提供省份/城市识别,其 carrier 字段仅作后台校验或提示,不参与发送运营商判定。 - 预发布运营商规则按公开码号资料覆盖中国移动、中国联通、中国电信及其移动转售号段;中国广电 `192` 号段按当前业务约定归入中国移动路由。 - 号码前缀表示原始码号分配关系;携号转网号码无法仅凭前缀识别当前签约运营商,如后续要求按实时在网运营商路由,必须接入可信的携号转网/HLR 查询能力。 9. 路由规则只能表达应用到通道组的绑定关系,不能直接绑定单个通道,也不在规则层配置省份;省份和全国路由在通道组内部处理。 10. 发送服务必须使用手机号段库识别手机号省份和城市;无法识别省份时走对应运营商的全国通道组路由。 11. 通道组内必须先匹配省网路由;省网未匹配时走同一运营商通道组内的全国通道。省网发送失败后,当前版本立即跳到该通道组第一个全国通道补发,不再尝试同省第二省网通道。 12. 省网配置当前版本按通道组内省份唯一:同一个通道组内山东只能选择一个通道、河南只能选择一个通道,依此类推;该校验针对通道组明细里的省份配置,不是校验通道本体属性。省网明细的省份必须与引用通道的发送地区一致,例如山东省网不能引用发送地区为河南的通道。 13. 全国通道可配置多个,并按优先级依次补发;同一通道组内全国通道优先级禁止重复,本期不支持权重分流。 14. 通道组明细 `carrier` 必须保留并参与发送逻辑,且必须等于通道组运营商。三网只允许作为通道本体能力 `SmsChannel.carrier=all`,表示该通道可被运营分配到移动、联通或电信通道组;一旦放入某个通道组,只能服务该通道组所属运营商。 15. 通道组明细引用通道时必须校验通道能力:移动组只能引用移动通道或三网通道,联通组只能引用联通通道或三网通道,电信组只能引用电信通道或三网通道。 16. 未命中企业应用通道组或无可用通道时不得 fallback 到全局第一个 active 通道,应将该短信标记为 failed,并记录可读失败原因、trace 和系统日志;客户端本期不展示通道细节。 17. 可发送通道必须同时满足:通道业务状态 active、CMPP 连接状态 connected、期望连接数大于 0、当前连接数大于 0、心跳未失败;auth_failed、heartbeat_timeout、reconnecting、disconnected、failed 或当前连接数为 0 的通道均不可被选中。Gateway 旧回写词 online/open 仅允许在 API 入库时兼容归一化为 connected,不作为发送判断状态。 18. 多连接通道只要至少 1 条连接 connected 且可用即可参与路由;心跳失败应及时更新连接状态,连续 3 次心跳失败后进入重连,重连成功前不可发送。 19. 新建或启用通道后,系统应立即创建/更新默认 CMPP 连接状态为 connecting 并通知 Go Gateway 发起连接;如果 connecting 超过 30 秒仍未收到 Gateway 回写 connected 或 failed,API 后台兜底任务必须将该连接标记为 failed,写入 `lastError=Gateway connection request timed out after 30 seconds` 和连接日志。默认扫描间隔 5 秒,可通过环境变量调整。 20. 通道异常时应支持熔断、降级、切换备用通道和失败重试;失败补发必须在同一企业应用授权的通道组范围内执行,并使用触发补发时的当前通道组配置,保证计费、退款、幂等和 trace 可追踪。 21. 失败补发除以下情况外均应触发:短信状态为 unknown;距离客户提交时间超过 72 小时;距离客户提交时间超过通道组配置的补发时间上限;通道组关闭失败补发。 22. 通道组补发时间上限由运营端配置,交互为“小时 + 分钟”,默认 12 小时 0 分钟,最小 1 分钟,最大不得超过 72 小时;真实发送链路按分钟级上限判断是否继续补发。本期不配置最大补发次数、补发间隔、失败类型白名单或人工重发能力,进入最终 failed/timeout 后不再人工重发。 23. 提交 accepted 后立即按企业应用配置的客户费率扣费;补发过程中最终成功只扣一次,submit failed 未真正发出时释放冻结且不扣费;failed receipt 导致最终全失败时退款;本期客户计费不使用通道成本价。 24. 签名全局 `reportStatus` 仅用于运营汇总展示,不得作为发送的一票否决条件。签名部分通道报备通过时允许发送,但路由候选必须只包含该签名 `ChannelSignatureReportTask.status=approved` 的通道;主通道未通过而备用通道通过时允许选择备用通道。所有在线候选通道均未报备通过时拒绝发送,补发切换通道时必须重新执行相同校验。 ### 4.7 通道签名报备 1. 运营端先在“报备字段库”维护字段编码、名称、类型、是否必填等标准定义;字段代码只允许阿拉伯数字和英文大小写字母,字段类型只允许字符串、图片、文件三种。字段库支持将任意字段分别配置为“通用签名报备资料”或“通用引流信息报备资料”,通用配置可真实新增和删除。通道报备详情仍可选择包括通用字段在内的任意字段库字段,并指定用途为签名报备、引流信息报备或两者共用,不得在通道内另建同名孤立字段。 2. 通道组配置通道,企业应用通过路由规则选择通道组。企业签名和引流信息添加/编辑时,系统必须合并“全局通用字段”和“企业应用 -> 生效路由规则 -> 通道组 -> 组内通道 -> 通道报备字段”;即使签名暂未绑定应用,也必须展示并校验对应类型的通用字段。 3. 同一字段被多个通道引用时按字段库记录去重;任一通道将该字段配置为必填,则企业资料中按必填处理,并保留该字段来源的全部通道用于后续分别报备。 4. 企业签名弹窗不再维护固定的签名依据、资质凭证、企业信息或责任人信息分组,所有签名报备资料均由通用字段和通道字段动态生成;每条引流信息同样只使用动态引流报备字段。文件字段走真实对象存储上传,其他字段保存真实值,必填校验同时在运营端、客户端和 NestJS API 执行。 5. 签名动态资料保留在签名记录;每条引流信息必须保存为独立 `SmsDrainageInfo` PostgreSQL 实体,包含所属企业、签名、应用、站点、地址、动态字段、审核状态和驳回原因,不得再以签名 JSON 数组作为引流审核事实来源。 6. 客户端上传签名资料后进入签名审核;签名审核通过后方可新增引流信息。客户端新建或修改引流信息均自动进入 `pending`,运营端必须在独立“引流信息审核”页面查看资料后通过或带原因驳回。运营端在企业签名管理中新增或修改引流信息视为运营操作,自动审核通过并写审核记录。 7. 引流信息审核通过前不得创建新的通道报备任务、写入可导出的引流报备材料或人工修改通道报备状态;已报备引流信息再次修改时,原通道任务冻结为 `waiting_review` 且旧材料停止使用。审核通过后系统按应用当前真实路由通道生成/重置 `reportType=drainage` 任务与材料,并写报备记录。 8. 运营端在报备任务或通道报备详情页导入通道回执,系统根据回执同步签名在各通道的报备状态。 9. 报备记录保留每次导出、导入、状态变更和操作人。 10. 发送前必须校验最终选中通道上的签名报备任务为 approved;补发切换到新通道时必须重新按新通道校验报备状态,未通过则该次发送失败。 11. 企业签名页、通道报备详情页和报备任务页均允许人工修正报备状态,但三个入口必须操作同一份 `ChannelSignatureReportTask` 通道级事实并写 `ChannelSignatureReportRecord`;企业签名页修改时必须展示应用当前通道组内的具体通道矩阵,不允许直接修改移动/联通/电信汇总标签。 12. 每次人工状态变更或回执导入后,系统必须按应用当前生效路由规则重新汇总各运营商目标通道状态和签名全局 `reportStatus`。新增目标通道但尚无任务时按未报备计入分母;移出当前配置的历史通道不参与当前汇总,但任务和记录继续保留。 13. 报备任务和报备记录页面必须可按签名/引流信息类型筛选,并显示引流信息的站名称、地址和所属签名。报备记录查询必须关联真实任务、通道和引流实体,完整展示审核通过后创建、重置、冻结、导出、回执和人工状态变化。 ### 4.8 CMPP Gateway 与外部接入 第一版发送链路必须区分两类 CMPP 连接,不允许用页面状态、HTTP 占位或模拟器结果替代真实协议能力: - 上游通道连接:平台作为 SP 客户端,连接运营商或供应商 SMSC,将平台已路由的短信提交到目标通道。 - 下游客户接入:企业客户作为外部 SP 客户端,连接平台暴露的 CMPP 监听端口(生产端口 `17890`),通过 CMPP 协议向平台提交短信。 #### 4.8.1 上游通道连接能力 1. Gateway 必须按运营端通道配置连接上游 SMSC,使用通道的 `gatewayHost/gatewayPort/account/passwordCipher/srcId/cmppVersion` 完成 CMPP 2.0/3.0 connect/login。 - 运营端通道创建/编辑必须提供 CMPP 2.0/3.0 版本选项,默认 CMPP 2.0;保存后 Gateway 连接与 SubmitCommand 均必须使用真实保存的 `cmppVersion`。 - 运营端通道“发送测试”必须走真实闭环:前端提交手机号和短信内容到 NestJS API,后端校验通道在线后创建独立 `SmsMessageRecord`、`SmsSubmitRecord`,并向 Redis Stream `gateway.submit.commands` 写入真实 `SubmitCommand`;短信记录页面必须能查询到测试短信,不允许只返回占位成功或只关闭弹窗。 - 通道测试短信是运营侧上游通道连通性验证,不绑定企业、企业应用或 `SmsBatchTask` 发送任务;submit result 和 receipt 只记录在短信记录、提交记录、回执记录和分片审计中,不触发客户侧 Deliver 推送、企业账务、任务进度或业务补发。 2. Gateway 必须校验上游 connect/login 返回码,区分 connected、auth_failed、connect_timeout、network_error、protocol_error 等状态,并回写 NestJS 真实连接状态。 3. Gateway 必须支持每个通道配置期望连接数,建立多条长连接,并按连接维度维护 currentConnections、lastConnectedAt、lastHeartbeatAt、lastError、reconnectCount。 - `desiredConnections`、`windowSize` 是平台对上游通道连接池和提交窗口的运行配置,必须通过运营端通道配置页面保存到真实后端;它们不是 CMPP 标准 PDU 字段,也不是 gocmpp 的原生配置字段。 4. Gateway 必须实现 ActiveTest 心跳与超时检测;连续心跳失败后连接进入 heartbeat_timeout/reconnecting,重连成功前该连接不可参与发送。 5. Gateway 必须支持断线自动重连、指数退避或固定退避、最大重试间隔、重连日志和状态回写。 6. Gateway 必须维护 CMPP sequenceId 与平台 messageId、submitId、channelId 的映射,submit resp 和 deliver 回执必须能追溯到原短信记录和提交尝试。 7. Gateway 必须实现真实 CMPP Submit,包括短信内容编码、长短信拆分、RegisteredDelivery、serviceId、srcId、destTerminalId、msgFmt、feeType/feeCode 等字段映射。 8. Gateway 必须消费 NestJS 投递的 `SubmitCommand` 队列或等价内部接口;提交成功、提交失败、超时均必须回传 `SubmitResult`,不得只停留在 API 侧入队。 9. Gateway 必须按通道连接和窗口容量控制并发,处理窗口满、SMSC 慢响应、sequence 回绕、连接断开时的在途消息状态。 10. Gateway 必须对每个物理通道执行 Redis 分布式 TPS 限速。`SmsChannel.rateLimitPerSecond` 是单通道上限,不是平台总上限;同一通道被多个通道组或多个 Gateway 实例使用时共享同一额度,不同通道独立计数。通道连接命令下发的配置值是最终上限,提交命令携带的值只能进一步降低、不能放大该上限。超流速消息必须继续保留在 Redis Stream pending 中等待可用时隙,不能因等待直接标记发送失败;Gateway 重启后仍可由 consumer group 恢复。worker 必须并发处理一个读取批次,让不同通道独立等待,不能因低 TPS 通道造成其他通道队头阻塞;单通道实际并发仍由 Redis 限速和 CMPP 窗口共同约束。 11. Gateway 不承担业务审核、计费、签名报备、通道组路由、黑名单或敏感词判断;这些由 NestJS 完成,Gateway 只执行已授权通道提交与协议事件回传。 #### 4.8.2 下游客户 CMPP 接入能力 1. Gateway 必须监听生产 CMPP 端口 `17890`,作为平台侧 CMPP Server 接收企业客户系统连接;该端口不是 HTTP 健康检查或控制接口。 2. Gateway 必须按企业应用生成的 CMPP 接入参数校验客户 connect/login,包括客户侧企业代码、账号、密码、CMPP 版本、源 IP 白名单、短信接口开关、应用状态、企业状态、连接数上限;客户侧企业代码必须来自 `SmsApplication.cmppEnterpriseCode`,且 `interfaceEnabled=false` 时必须拒绝新的鉴权。已完成 bind 的连接若随后被停用,其后续参数合法 Submit 必须按业务失败记录并通过 Deliver Receipt 回执。Gateway 必须根据 CONNECT `Version` 为每条 TCP 连接独立协商 CMPP2.0/2.1/3.0 解包与响应类型,不得用固定 CMPP3.0 结构解析 CMPP2.0 Submit。 3. 企业应用列表中的 CMPP 连接数和连接状态必须表示客户连接到 Gateway `17890` 的下游 CMPP 会话,不得复用上游 `SmsChannel` 连接状态。Gateway 必须在 bind、CMPP `ACTIVE_TEST`、Submit、Deliver 写入失败等事件时回写应用级连接记录;列表只将最近心跳未超时的会话计为正常连接。超过心跳阈值的会话必须标记为心跳超时/断开,并保留远端 IP、协议版本、建立时间、最后心跳、最后 Submit、最后 Deliver 与断开原因供运营查询。 3. 客户端应用的 IP 白名单必须对 CMPP 下游连接生效;未命中白名单、应用停用、企业停用、密码错误、超过连接数上限时必须拒绝连接并记录系统日志。 4. Gateway 必须维护应用级下游连接状态,回写 applicationId、tenantId、connectionId、currentConnections、desiredConnections、lastHeartbeatAt、lastError,运营端企业应用列表和连接详情必须来自这些真实状态。 5. Gateway 必须实现下游 ActiveTest、Terminate、异常断开处理;断开后连接数和状态必须及时回写。 6. Gateway 必须处理客户提交的 CMPP Submit,将手机号、内容、源地址、企业应用、客户消息序号等转换为平台发送请求。应用身份必须来自当前已鉴权 TCP 连接绑定的 `cmppAccount`;Submit `MsgSrc` 是独立的企业代码,必须与该应用 `cmppEnterpriseCode` 匹配,不得把 `MsgSrc` 当作登录账号查找应用。 7. 下游 CMPP Submit 进入平台后,不创建客户可见的批量发送任务,但必须按手机号维度创建 `sms_message_record`,source 标记为 `cmpp`,并保留客户侧 sequence/msgId 映射。系统可使用内部批次承载计费、风控和队列,不得把该内部批次误展示为客户端手工批量任务。 8. 下游 CMPP Submit 必须复用 NestJS 发送前校验:企业/应用状态、IP 白名单、签名/模板报备、模板匹配策略、风控、黑名单、余额/授信、运营商识别、通道组路由。 9. 对客户 Submit 的响应必须符合 CMPP 协议:鉴权失败、账号或源 IP 不合法、PDU/手机号等参数不合法时直接返回非零 SubmitResp,且不创建短信记录。账号已识别且参数合法后必须先创建可追踪平台 messageId 和短信记录,再返回成功 SubmitResp;模板/签名/报备、风控、余额、应用后续停用、无可用通道以及上游最终提交失败等业务失败必须保存真实失败记录,并通过客户侧 Deliver Receipt 返回失败,不得因业务校验失败丢失客户 Submit 审计。 10. Gateway 必须支持平台最终回执向下游客户连接投递 Deliver Receipt;若客户连接已断开,应按策略缓存、重试或记录投递失败,不能丢失平台最终状态。 11. Gateway 必须支持下游客户上行接入场景:收到运营商上行后,按接入号、手机号、应用、时间窗口匹配并向客户连接推送 Deliver,上行同时入库。 12. 下游客户连接与上游通道连接必须隔离管理:客户侧账号密码不能用于连接上游通道,上游通道账号密码也不能作为客户接入凭据。 13. Gateway 必须为客户侧 CMPP2.0/3.0 Submit 记录可检索日志:收包时记录协议版本、账号、客户 IP、sequenceId、号码、srcId、编码、分片序号和内容长度;响应时记录 result、平台 messageId、CMPP Msg_Id、耗时和失败阶段。NestJS 拒绝 Submit 时,Gateway 日志必须保留 API 返回的真实业务原因,不能只记录 HTTP 状态码;短信正文不得明文写入 Gateway 日志,仅记录字符数和哈希。 14. 客户已按 CMPP 标准 UDH 拆分的下游长短信必须先重组再进入业务发送链。Gateway 应识别 8 位 `05 00 03` 和 16 位 `06 08 04` 拼接头,去除 UDH 后按 `MsgFmt` 解码正文,并将引用号、总片数、片序号和编码传给 NestJS;NestJS 必须在 PostgreSQL 持久化分组和分片,支持乱序、同片幂等、冲突片拒绝、进程重启恢复和超时终止。分片未齐全前不得创建内部批次、`SmsMessageRecord`、计费或路由;齐全后只创建一条完整正文主记录,并使用第一片 `Sequence_Id` 建立客户消息映射。每个合法分片仍应分别取得一个 `CMPP_SUBMIT_RESP`,但不能把 UDH 字节作为正文、不能按分片生成多条短信记录。 #### 4.8.3 回执、上行与幂等 1. Gateway 必须解析上游 deliver receipt,将 DELIVRD、UNDELIV、EXPIRED、REJECTD 等供应商状态归一化为平台 delivered、failed、unknown、timeout 等状态。 2. Gateway 必须解析上游普通 deliver 上行短信,携带手机号、接入号、内容、接收时间、通道和原始报文摘要回传 NestJS。 3. Gateway 回传 `SubmitResult`、`ReceiptEvent`、`UplinkEvent` 时必须包含 traceId、messageId、submitId 或可映射字段、channelId、gatewayMessageId、sequenceId,保证短信详情能展示完整历史尝试。 4. 重复 submit resp、重复 receipt、迟到旧通道 receipt 必须交由 NestJS 幂等处理;Gateway 不得因本地缓存丢失而生成无法追踪的重复业务事件。 5. Gateway 需要保留最小运行日志和指标:连接数、登录失败次数、心跳失败次数、submit TPS、submit resp 延迟、receipt 延迟、队列积压、重连次数、协议错误。 6. Gateway 控制服务 `/health` 只能表示进程存活;真实验收必须检查上下游连接状态、队列消费、submit/receipt/uplink 事件闭环。 #### 4.8.4 当前实现缺口标记 截至当前版本,Go Gateway 已有 HTTP 控制服务、健康检查、连接上游 SMSC 的 `ConnectChannel` 控制入口、gocmpp 协议 spike、队列消息结构,并已补齐第一阶段下游 CMPP 入站、上游提交和下游回执/上行推送能力: - 已实现 `17890` 入站 CMPP Server 监听,生产部署由 `GATEWAY_CMPP_ADDR=0.0.0.0:17890` 启动。 - 已实现下游客户 connect/login 鉴权:CMPP `Source_Addr` 使用企业应用独立 6 位 `cmppAccount`,密码使用应用 CMPP 参数中的 `passwordCipher`,Gateway 将 CMPP `AuthSource/Timestamp` 交由 NestJS 根据真实数据库校验。 - 已实现客户端应用 IP 白名单、应用状态、企业状态和企业认证状态校验;校验失败返回 CMPP connect 失败。 - 已实现下游 CMPP Submit 到平台发送请求的转换:Gateway 解码 CMPP 3.0 submit 内容,调用 NestJS 真实入站接口,NestJS 复用模板/签名/风控/余额/路由/队列优先级发送链路,接受后返回 CMPP submit_resp。 - 已实现 NestJS 校验通过后的异步 Gateway 提交:`SubmitCommand` 保留 BullMQ 审计/兼容投递,同时写入 Redis Stream `gateway.submit.commands` 主命令流;Go Gateway 以 consumer group 独立消费该命令流,携带通道 `gatewayHost/gatewayPort/account/passwordCipher/cmppVersion` 作为 SP 客户端连接上游 SMSC 并发送 CMPP Submit。 - 已实现上游 submit_resp 回传:Gateway 将 accepted/rejected/timeout 转换为 `SubmitResult` 调用 NestJS,NestJS 继续执行 accepted 扣费、失败/超时补发或释放冻结等既有逻辑。 - 已实现上游 deliver receipt 和普通 deliver 上行解析:Gateway 在上游连接读循环中解析 receipt/uplink,调用 NestJS `/gateway/events/receipt`、`/gateway/events/uplink` 写入真实发送记录、回执和上行表。 - 已实现上游长短信第一版拆分和上行长短信重组:Gateway 按 CMPP 标准 6 字节 UDH 将超过 140 字节的 Submit 内容拆成多个分片,设置 `PkTotal/PkNumber/TpUdhi` 后逐包提交;上游普通 Deliver 携带 UDH 分片时,Gateway 在同一连接内按通道、主叫、被叫、引用号和总片数缓存并重组后再回传 NestJS。 - 已实现上游多连接和窗口控制第一版:NestJS 将通道配置中的 `desiredConnections/windowSize` 写入 `SubmitCommand.upstream`,Go Gateway 按通道建立连接池,每条连接独立维护 submit pending 映射、receipt/uplink 处理和窗口令牌;窗口满时等待可用窗口或按提交超时返回。 - 已实现 SubmitCommand 在途恢复第一版:Go Gateway submit worker 在消费新消息前会对 Redis Stream consumer group 中空闲超过阈值的 pending 命令执行 `XAUTOCLAIM`,重新提交并按正常成功路径 ack,避免 Gateway 重启后命令永久滞留在 PEL。 - 已实现上游连接断开时的 pending submit 状态补偿第一版:如果某条上游 CMPP 连接在收到 submit resp 前断开,Gateway 会立即唤醒该连接上等待中的 pending submit,请求返回 `timeout/CONNECTION_LOST`,由 NestJS 进入既有补发或释放冻结逻辑,不再只依赖固定超时。 - 已实现“上游可能已受理但 submit resp 丢失”场景的保守补偿第一版:Gateway 在 receipt 事件中补充手机号;NestJS 对无法按 `messageId/gatewayMessageId` 精确命中的回执,只在“同通道、同手机号、72 小时窗口内、且仅存在 1 条 `timeout + gatewayMessageId=null` 的 submit 记录”时才回填并接收该回执,避免误绑到其他短信。 - 已实现 Gateway 提交异常治理:Go Gateway 对多次处理仍失败的 `SubmitCommand` 不再无限滞留在 PEL,而是按阈值写入 NestJS 真实 `GatewaySubmitDeadLetter` 表(数据库表名和内部接口保留技术兼容名,页面在“网关异常”的“提交异常”Tab展示)。运营端 `/admin/gateway-submit-exceptions` 提供真实分页、筛选、汇总、脱敏详情和单条重新入队;原始 payload、密码、密钥不得返回浏览器。重新入队必须要求近期认证、填写原因、勾选“已确认上游未受理”,并校验短信尚未 accepted/submitted/delivered/unknown、通道 active 且 connected、人工次数小于 3;服务端以 pending 到 requeueing 的原子状态抢占防止重复点击,成功写回 Redis Stream 后记录操作人、原因、Stream ID 和时间。收到后续 SubmitResult 时必须将对应异常记录闭环为 resolved。 - Gateway 提交异常重新入队必须使用“异常记录 ID + 下一次人工次数”的稳定幂等键,通过 Redis Lua 原子完成“检查幂等键、XADD、保存 Stream ID”;API 在 XADD 成功后宕机或数据库落账失败时,超时恢复扫描必须复用同一幂等键完成落账,不能再次产生 Stream 消息。Gateway 重复上报同一个原始 Stream 异常不得把 `requeued/resolved` 回退成 `pending`。 - 已实现 Gateway 通道级 Redis 限速:NestJS 入队前保留业务层通道限速,Go Gateway 在真正调用上游 Submit 前再次按通道 ID 预约发送时隙;连接命令把权威 TPS 写入 Redis,提交按权威值与消息值的较小者执行。普通 Stream 消息在等待期间不 ACK、不转失败,多实例共同使用同一限速状态;worker 对同批消息并发调度,低 TPS 通道等待不阻塞其他通道。 - 已实现 Gateway 重启后的 active 上游通道恢复:部署先重启 Gateway 再重启 API;API 启动后从 PostgreSQL 读取 active 通道,重新下发连接命令,恢复 Gateway 内存连接池、真实连接状态和 Redis 权威 TPS key,不得继续沿用重启前的 connected 状态。 - 已实现客户侧下游投递重试第二版:客户系统负责断线后重连;平台在客户离线或投递失败时把 Deliver Receipt/上行 Deliver 保留在 `CmppDownstreamDelivery`,客户 bind 成功后立即拉取 pending,且 Gateway 会对当前在线账号周期补投;超过重试上限后转 `failed` 并写失败审计。 - 已实现下游投递失败审计与人工重投第一版:运营端后端与页面可分页查看 `CmppDownstreamDelivery` 的 pending/awaiting_ack/failed/unconfirmed/rejected/delivered 记录,支持按状态、类型、应用和关键字筛选,并可对非 `awaiting_ack` 记录执行人工重投,真实调用 Gateway `/downstream/receipt` 或 `/downstream/uplink`。主记录必须分开保存自动重试次数 `retryCount`、人工重投次数 `manualRetryCount` 和最近人工重投时间 `lastRetriedAt`,操作日志保留重投前状态与自动重试次数。 - 同一下游投递的人工重投必须使用 `id + status + updatedAt` 条件更新原子认领;认领后先进入 `manual_requeueing`,避免运营并发请求或 Gateway pending 恢复扫描同时双发。调用完成后进入 `awaiting_ack` 或失败状态;进程中断超过默认 2 分钟后自动转回 `pending`,由 Gateway 单一路径恢复投递。阈值可通过 `CMPP_DOWNSTREAM_MANUAL_REQUEUE_STALE_MS` 调整。 - 已实现下游投递批量重投第一版:运营端可在当前页勾选多条 `pending/failed` 下游投递记录,调用真实批量接口逐条重投并返回成功/失败汇总,不允许用前端循环假装成功。 - 下游人工重投必须提供完整且可见的操作反馈:单条和批量操作均先使用平台确认弹窗明确重复处理风险,确认提交期间禁用重投操作以防重复点击;真实接口返回后继续在弹窗中展示成功、部分成功或失败结果。单条成功须展示当前真实状态和人工重投次数,批量结果须展示总数、成功数、失败数及逐条失败原因;请求异常时必须提醒先核对列表状态和人工重投次数再决定是否重新提交,不能静默刷新或仅依赖页面顶部弱提示。 - 下游投递的 `pending` 展示必须结合真实尝试字段:自动与人工次数均为 0 时显示“待首次投递”,`retryCount > 0` 时显示“等待自动重试”,`manualRetryCount > 0` 时显示“人工重投排队中”。人工重投可重置新一轮自动重试预算,但不得把记录伪装成从未投递。 - 下游投递不得无限停留在 `pending`:Gateway 对未写出的结果必须返回明确的 `retryable/reasonCode/errorMessage`。状态回执在原消息映射已经丢失且缺少 `submitSequenceId` 时属于不可恢复错误,立即转为 `failed` 并保留失败原因和操作日志;客户端暂时离线属于可恢复错误,按既有重试次数与指数退避处理。所有 pending 从创建时间或最近人工重投时间起最多保留 72 小时,可通过 `CMPP_DOWNSTREAM_PENDING_TIMEOUT_HOURS` 调整,超时后自动终结为 `failed`。 - 新产生的客户侧状态回执投递 payload 必须保存短信原始 `cmppSubmitSequenceId`,使 Gateway 重启后仍能结合账号和平台消息 ID 重建与原 `CMPP_SUBMIT_RESP` 一致的非 0 `Msg_Id`;不得用伪造或为 0 的 `Msg_Id` 冒充成功投递。 - 已实现下游投递告警统一口径:运营看板、下游投递 Dashboard 和应用告警排行必须基于同一组真实 `CmppDownstreamDelivery` 条件统计:`pending` 超过积压阈值、`awaiting_ack` 超过 `ackDeadlineAt`,以及最近失败窗口内的 `failed/unconfirmed/rejected`。默认积压阈值为 10 分钟,最近失败窗口为 1 小时,可分别通过 `CMPP_DOWNSTREAM_ALERT_PENDING_MINUTES` 和 `CMPP_DOWNSTREAM_ALERT_RECENT_FAILED_HOURS` 覆盖。右上角“待审核任务”仅汇总审核业务,不显示下游投递告警菜单或数字。 - pending 积压告警必须从本轮排队开始时间计算:从未人工重投的记录使用 `createdAt`,人工重投记录使用 `lastRetriedAt`;刚执行人工重投的记录不得因旧 `createdAt` 立即重新计入积压告警。 - 已实现下游投递 Dashboard 第一版:运营端“下游投递记录”页面顶部新增真实聚合总览,直接按 `tenantId/applicationId/deliveryType` 统计投递总量、pending/awaiting_ack/delivered/failed/unconfirmed/rejected、积压告警、ACK 超时告警、按类型分布、重试压力分布和应用告警排行,数据源必须来自 `CmppDownstreamDelivery`,不能靠前端本地汇总。应用告警排行只统计满足统一告警时间窗的记录,不得将新创建的 `pending` 或超出最近窗口的历史失败永久累加为告警。 - 已实现下游连接映射持久化第一步:Gateway 在客户 CMPP 账号 bind 成功、下游 submit 建链和回执/上行下发时,会把账号在线状态、实例标识、最近活跃时间写入 Redis presence;该状态不再只保留在 Gateway 进程内存中,为后续“Gateway 重启后的 pending 恢复”提供外部状态基础。 - 已实现下游连接映射持久化第二步:Gateway 启动时会读取 Redis presence 与当前内存在线账号,形成“恢复候选视图”,并通过控制面 `GET /downstream/recovery-candidates` 暴露候选账号列表,供后续恢复逻辑与运维排查使用;本阶段仍不等同于自动恢复 pending 投递。 - 已实现下游 pending 恢复执行第一版:Gateway 启动后会立即按恢复候选账号拉取真实 `CmppDownstreamDelivery.pending`,后续每轮补投周期也会继续扫描恢复候选;若账号已有可用下游连接则继续推送回执/上行,若账号尚未重连则保持 `pending` 等待后续恢复,不能因为 Gateway 重启就把未投递记录误标成失败。 - 已实现下游恢复控制第一版:Gateway 对恢复候选账号增加账号级恢复锁、失败/等待连接退避和恢复状态持久化,避免同一账号被并发重复恢复或每轮高频空转;控制面新增 `GET /downstream/recovery-statuses` 可查看最近一次恢复状态、重试次数、下一次可恢复时间和错误原因。 - 已实现下游恢复观测第一版:控制面新增 `GET /downstream/recovery-overview`,一次性返回恢复候选账号与恢复状态,便于联调和生产排查。 - 已实现下游恢复状态回流第一版:Gateway 在每次恢复状态变化后,调用 NestJS `/api/gateway/events/downstream/recovery-status` 真实回传账号恢复状态;NestJS 将状态写入 Prisma/PostgreSQL `GatewayDownstreamRecoveryStatus`。 - 已实现下游恢复状态运营化第一版:运营端新增独立“恢复状态管理”页面,明确说明该页用于观察客户重连或 Gateway 重启后的未完成下游投递恢复,每个账号展示当前或最近一次恢复状态;支持按最近更新时间筛选(默认近 7 天)、真实列表、分页、详情查看和当前筛选结果 CSV 导出。原“下游投递记录”页面只保留逐条投递记录并默认查询近 7 天,不再混放恢复状态区块。 - 已实现下游恢复失败分类第一版:Gateway/NestJS 共同维护 `failureCategory`,覆盖 `client_disconnected`、`backoff`、`lock_contended`、`lock_lost`、`flush_failed`、`partial_delivery_failed`、`unknown`;运营端“恢复状态管理”页面支持失败分类筛选、分类分布统计、详情展示和导出字段。 - 已实现多 Gateway 恢复抢占协调第一版:恢复锁从单纯实例名升级为 Redis token 租约,状态记录 `lockOwner/lockExpiresAt`;恢复完成时必须通过 Lua 原子校验锁 token,只有持锁实例才能写入最终恢复状态并释放锁,避免旧实例超时后误删新实例锁或覆盖新实例恢复结果;运营端详情/列表可查看锁持有实例。 - 已实现长短信分片审计第一版:Gateway `SubmitResult` 回传真实 `segments[]`,包含 `segmentTotal/segmentIndex/sequenceId/gatewayMessageId/submitStatus/submittedAt`;NestJS 写入 Prisma/PostgreSQL `SmsMessageSegmentAudit`,回执按 `gatewayMessageId` 回填分片回执状态,补偿归因可记录 `compensationType`;运营端短信记录详情可查看真实分片提交、回执和补偿审计。 - 下游投递重试已改为指数退避第一版:首次失败后按基础间隔重试,随后按 2 倍递增,并受最大退避上限约束,避免客户长时间离线时平台每分钟机械重试。 - 下游状态必须以客户确认作为终态:Gateway `SendPkt` 成功后只能写 `awaiting_ack`,仅收到匹配连接、`Sequence_Id`、`Msg_Id` 且 `CMPP_DELIVER_RESP.Result=0` 后才能写 `delivered`;超时、非零 Result 和历史未留存 ACK 的记录分别按未确认、拒绝或历史未确认展示,不能再把 TCP 写出冒充客户已收到。 - 企业应用必须分别提供“回执自动重试投递”和“上行短信自动重试投递”开关,默认开启。开关按投递创建时快照保存;关闭只阻止已经写出但未获 ACK/被拒绝后的自动重发,不阻止离线队列在客户首次上线时完成首次投递。手工重投不受开关限制,但必须提示重复业务处理风险并二次确认。 - 客户 Submit 的失败状态回执必须严格晚于对应 `CMPP_SUBMIT_RESP` 写出,且 Deliver 中的业务 `Msg_Id` 必须非 0、与该 SubmitResp 返回的 `Msg_Id` 完全一致;`Result=0` 但 `Msg_Id=0` 只能表示客户端协议栈收包,不能标记业务回执已确认。平台必须持久化原 Submit Sequence_Id,使 Gateway 重启或客户重连后的补投仍可重建相同业务 `Msg_Id`。 - 每一次客户侧 Deliver 投递都必须单独持久化发送时间、物理连接 ID、`Sequence_Id`、业务 `Msg_Id`、ACK 截止时间和 `DELIVER_RESP` 结果;运营端详情可按尝试查看,不得只保留最后一次结果而覆盖历史。客户连接关闭时必须清理该连接的发送时序状态;无法匹配的 `DELIVER_RESP` 也必须写通讯日志。 - 及时失败回执必须使用“当前客户物理连接上的 SubmitResp 写出屏障”:从开始处理 Submit 到该包 `CMPP_SUBMIT_RESP` 实际写出前,同一连接不得下发该 Submit 产生的失败 Deliver;长短信以最终分片 SubmitResp 写出为释放时点。屏障只控制协议写出顺序,不修改通道路由、连接配置或连接池。 - 供应商 Deliver Receipt 必须先持久化到幂等收件箱,再返回成功 `CMPP_DELIVER_RESP`;持久化失败时不得向供应商虚假确认成功。收件箱异步匹配内部短信,支持失败退避、最大尝试/存留时间和进程异常后的 `processing` 超时恢复。 - 上游长短信必须在每个分片收到 `SubmitResp` 后立即保存该分片的 `Sequence_Id`、供应商 `Msg_Id`、提交状态和时间,不能等待全部分片完成才批量落库;后到的提交成功聚合结果不得覆盖已经由早到失败回执形成的终态。 - 运营端允许对 `delivered`(客户端已确认)记录再次手工重投,但单条和批量入口都必须明确提示可能造成下游重复处理;`awaiting_ack` 状态在确认窗口内不得并发重投。 - 已实现客户侧最终 Deliver 推送的第一版能力:Gateway 在下游 Submit 被接受后记录 messageId 到客户连接的内存映射;NestJS 收到最终 receipt/uplink 并入库后调用 Gateway `/downstream/receipt`、`/downstream/uplink`,Gateway 向仍在线的客户 CMPP 连接下发 Deliver Receipt 或普通 Deliver。 - 已实现客户侧 Deliver 持久化第一版能力:NestJS 收到最终 receipt/uplink 后写入 `CmppDownstreamDelivery` 待投递记录;在线推送成功标记 delivered,客户断线或 Gateway 不可达时保留 pending 并记录 retry 信息;客户重新 bind 后 Gateway 按账号拉取 pending 记录补发。 - 已实现普通上行匹配与人工认领第一版:优先按 messageId 精确匹配;无 messageId 时按接入号匹配应用路由;仍无唯一应用时按手机号和最近下发时间窗口匹配;多候选标记 ambiguous 并写入 `SmsUplinkMatchCandidate` 候选,运营端可人工认领候选应用/下发记录;认领后更新上行记录、保留候选审计,并创建真实客户侧上行 Deliver 投递记录。 仍缺少生产完整闭环能力: - 应用级下游连接数限制和连接状态回写尚未完整产品化。 - 当前下游 CMPP Submit 通过 `sourceType=cmpp` 的系统批次兼容承载,尚未拆成完全独立于批量任务模型的单条发送模型。 - Gateway 控制面 `/upstream/submit` 仅保留为本地调试、人工补偿和运维验证入口;生产主链路应由 Gateway submit worker 消费 Redis Stream `gateway.submit.commands` 触发。 - 客户侧 Deliver 重投当前已经具备账号级周期恢复、退避、Redis token 租约锁和控制面状态观测;恢复状态已同步到 NestJS 持久化审计表并进入运营端独立页面,且具备第一版失败分类分析和多 Gateway 抢占协调。 - 普通上行匹配已覆盖 messageId、接入号、手机号时间窗口和共享接入号多候选人工认领;后续仍需补更复杂的批量认领、认领规则推荐和认领准确率指标。 - 长短信分片当前已具备真实分片提交/回执/补偿审计,运营端短信详情可查看 `SmsMessageSegmentAudit`;后续仍需补“按单个分片自动重投”和分片级人工补偿操作。 - 多连接窗口当前覆盖单进程内连接池和窗口满等待;在途恢复当前覆盖 Redis Stream pending claim、连接断开时的 pending submit 唤醒、receipt 驱动的保守唯一候选补偿、Gateway 提交异常转存/安全人工重新入队,以及下游客户在线时的周期补投、Gateway 重启后的恢复候选扫描、账号级 Redis token 租约锁/退避/状态观测、恢复状态入库/运营端可视化;尚未实现连接级状态持久化、窗口指标回写、提交异常后台自动重试策略、长恢复任务锁续租,以及“上游已受理但 submit resp 丢失”场景的强确认或完整幂等补偿。 基于当前真实代码,CMPP 端到端链路剩余缺口可以明确收敛为以下几类: 1. 下游恢复审计产品化: - 恢复状态已能写回 NestJS/Prisma/PostgreSQL,并已在运营端“恢复状态管理”页面提供独立列表、详情、导出和失败分类分布。 - 后续仍需补恢复吞吐、趋势、耗时、连续失败账号等更细指标看板。 2. 高阶幂等与跨实例协调: - 多 Gateway 实例下的账号级恢复抢占协调已具备 token 租约和完成校验;后续仍需增强分片级/消息级更细粒度去重,以及长恢复任务中的锁续租。 - “上游已受理但 submit_resp 永久丢失”的强确认补偿还不完整。 3. 分片和复杂场景补偿: - 长短信分片级提交明细、回执和补偿归因已进入真实审计表和运营端短信详情;后续仍需补分片级自动重投/人工重投。 - 共享接入号、多候选普通上行已具备第一版人工认领和认领后下游投递;后续仍需补批量认领、人工认领复核和指标分析。 4. 运营观测与指标: - 窗口利用率、连接级心跳、恢复吞吐、恢复耗时趋势等指标尚未回写到运营端真实页面。 这些缺口未补齐前,可以把客户 connect/login、IP 白名单、客户 submit 入平台、NestJS 业务校验、Gateway 上游连接池 submit、窗口满等待、长短信基础拆分/重组、submit_resp、receipt/uplink 入库、分片级提交/回执/补偿审计、共享接入号上行人工认领,以及在线或重连客户 Deliver 推送、Gateway 重启后的 pending 恢复、恢复状态入库、运营端恢复状态独立页/详情/导出和失败分类分布作为第一版真实链路验收;不能把连接级指标回写、分片级自动重投、批量认领和认领指标分析作为“生产已验收通过”。 ### 4.9 回执与上行 1. 通道回执接入后更新发送记录状态。 2. 回执状态至少包括提交成功、提交失败、发送成功、发送失败、未知、超时。 3. 提交后 72 小时仍为未知的短信转超时,返回客户失败。 4. 超时前允许人工同步、重新拉取回执或接收供应商二次回执;所有二次回执必须记录历史。 5. 旧通道迟到的 failed receipt 如果对应短信已经由新通道 delivered,不得覆盖短信记录最终 delivered 状态;短信详情弹窗必须能看到该历史 failed receipt。 6. 重复提交结果或重复回执必须幂等处理,不得重复扣费、重复释放冻结或重复退款。 7. 上行短信接入后优先按手机号、接入号、企业/应用、时间窗口匹配下发记录。 8. 接入号匹配不到企业/应用时,展示近期平台对此手机号下发的短信供人工判断。 9. 完全匹配不到的上行短信仍需入库,并展示为未匹配。 10. 客户端可查看本企业上行短信,运营端可查看全平台上行短信。 ### 4.10 账户计费 1. 客户端可查看现金余额和充值记录;充值由运营端人工入账。 2. 发送创建时按短信内容计费条数和企业应用客户单价生成预估费用,计费条数只按 70/67 字规则拆分;不按移动、联通、电信配置不同客户价。 3. 平台发送前只判断 `现金余额 + 授信额度 > 0`;授信额度可为正数、负数或 0,和小于等于 0 时禁止发送。 4. 发送链路需记录计费条数、计费单价、计费金额、账务状态。 5. 账单流水与短信记录可追溯关联,支持按企业、应用、任务、手机号、时间对账。 6. 最终失败、超时失败需要退费。 7. 三网通道成本只用于平台内部成本核算,不影响客户扣费金额。 8. 当前版本计费口径固定为提交 accepted 扣费、最终 failed receipt/timeout 退款。 9. 所有面向用户展示的金额、余额、充值金额和单价统一以人民币元展示并固定保留四位小数;内部使用 `0.0001 元`整数金额单位持久化,不以浮点数执行账务计算。 10. API 必须定时扫描提交成功但超过 72 小时仍未收到明确最终回执的短信,转为 timeout 并退还已扣金额;扫描需覆盖 `submitted` 和 `unknown`,且用条件更新避免多实例重复退款。 ## 5. 功能需求 ### 5.1 客户端工作台 - 展示现金余额、今日发送量、成功率、待审核事项、快捷入口。 - 展示最近发送批次、发送趋势、签名/模板状态。 - 数据范围限定为当前企业。 ### 5.2 客户端短信发送 - 支持选择短信应用、签名、模板。 - 支持模板变量填充。 - 支持手机号手动输入、粘贴、文件导入。 - 导入号码文件格式支持 CSV、TXT,最大文件大小 20 MB。 - 单任务最大号码数默认 100 万条。 - 支持去重、非法号码提示、黑名单拦截提示。 - 支持立即发送和定时发送。 - 不允许修改已审核模板主体,只允许填写模板变量;如需修改主体,必须新建模板重新审核。 - 不符合模板的短信按应用配置处理:拒绝发送、进入人工审核或直接发送。 - 敏感词命中第一期直接拒绝发送,并记录拒绝原因。 - 提交后展示任务编号和任务状态。 ### 5.3 客户端批量任务 - 支持按任务编号、应用、发送时间、状态搜索。 - 展示任务总量、成功数、失败数、未知数、发送进度、创建人。 - 支持查看任务详情和导出明细;默认企业管理员可导出,普通用户需授权。 ### 5.4 客户端发送详情 - 支持按手机号、内容、回执、时间搜索。 - 展示手机号、短信内容、计费条数、通道、提交时间、回执时间、状态。 - 支持查看单条发送链路。 ### 5.5 客户端上行短信 - 支持按手机号、内容、接收时间搜索。 - 展示上行内容、接入号、匹配下发记录、接收时间。 ### 5.6 客户端短信应用 - 支持新增、编辑、启用、停用应用。 - 支持配置回调地址、IP 白名单、应用密钥、日发送限额。 - 支持配置发送队列等级:普通队列、优先队列;默认普通队列,变更后只影响新创建或新入队的发送消息。 - 支持配置“不符合模板的短信”处理策略:拒绝发送、人工审核、直接发送。 - 支持配置应用级风控阈值:单任务最大号码数、重复号码比例、非法号码比例、黑名单命中比例、非工作时间大批量发送策略、短时间任务创建频控。 - 默认阈值:重复号码比例 30%、非法号码比例 10%、黑名单命中比例 5%、同一企业/应用 10 分钟内最多创建 10 次任务。 - 应用密钥需支持重置,并记录操作日志。 ### 5.7 客户端模板管理 - 支持新增、编辑、提交审核、撤回、删除草稿。 - 支持变量识别和字数/计费条数预估。 - 支持查看审核记录和驳回原因。 ### 5.8 客户端签名管理 - 支持新增、编辑、提交审核、上传证明材料。 - “签名与引流信息”采用签名父级、引流信息子级的可展开工作台,提供真实后端统计、签名/应用/状态筛选、审核状态、已交资料数、驳回修改说明及新增、修改、删除操作。 - 签名审核通过后支持维护短信中使用的网站、应用页面等引流信息;新增或修改后进入真实审核流程,客户端只展示“待提交、资料审核中、审核通过、需修改”等客户可理解的状态。 - 客户端页面和 `/client` API 均不得暴露内部通道、通道组、路由规则、运营商报备汇总、报备任务及内部资料要求快照。动态审核字段接口只返回字段名称、类型、必填规则等客户填报所需信息;通道级报备状态仅限运营端查看。 ### 5.9 客户端账户计费 - 账户余额:展示当前现金余额和人工充值记录,不提供客户端购买套餐或创建充值订单入口。 - 账单流水:展示充值、冻结、扣费、退费、调整等流水。 - 账单流水需关联短信任务、短信记录或人工调整单据。 - 客户端账号设置属于系统管理,不允许客户自行配置通道或通道组。 ### 5.10 运营端看板与监控 - 展示总发送量、成功率、通道健康度、待处理审核数。 - 发送监控展示通道状态、发送趋势、失败率、积压队列。 - 数据统计支持按企业、应用、通道、日期统计。 - 运营概览一级菜单下只保留运营看板、发送监控、数据统计;客户管理独立作为一级业务域展示,避免重复菜单。 - 右上角消息铃铛展示所有待审核任务总数,并按企业认证、短信审核、短信模板审核、签名审核等分类展示;点击分类跳转到对应审核页面。 - 新审核任务进入时,运营端应触发浏览器通知或站内提醒;提醒数据必须来自真实待审核数量接口,不得只写死前端数字。 - 全局导航首次加载、每 30 秒轮询、窗口重新获得焦点及审核完成后的角标刷新,必须调用独立轻量待审核数量接口;该接口只统计五类待审核数量,不得调用或复用包含发送、账务、连接、下游投递和趋势查询的完整运营看板聚合。 ### 5.11 运营端客户与企业 - 支持企业列表、企业详情、新增、编辑、启用、停用。 - 支持企业认证资料审核。 - 支持查看企业下应用、签名、模板、发送记录、报备记录。 - 企业管理列表不提供无业务意义的“详情”按钮;需要查看明细时从企业应用、签名、模板、发送记录等业务入口进入。 - 企业列表按真实 PostgreSQL 当日短信消费金额从大到小排序;金额相同时按企业名称和 id 稳定排序。 - 企业应用管理编辑保存后返回企业应用管理列表。 - 企业应用列表展示 CMPP 连接数;点击连接数打开连接详情弹窗,内容包含连接 id、状态、连接建立时间、最近心跳时间、上次提交时间、窗口占用等。 - 企业应用连接详情支持删除连接;删除连接必须调用真实后端接口或 Gateway 回写接口,并写入系统日志。 - 企业应用列表提供 CMPP 连接参数查看与一键复制能力,参数来源于真实应用/通道配置,不允许只在前端拼接假数据。 - 企业应用新增/编辑必须展示发送队列等级,并真实保存普通队列或优先队列配置;列表或详情应能看到该配置,便于运营核对高优先级应用。 - 企业应用新增时选择企业必须使用通用下拉控件和真实企业接口;下拉控件的视觉、尺寸、禁用态、错误态应与系统内其他 Select 保持一致。 - 企业应用列表按真实 PostgreSQL 当日发送数量从大到小排序;数量相同时按应用名称和 id 稳定排序,不能只对前端当前结果临时排序。 - 所有企业和企业应用下拉控件必须支持按名称搜索;企业应用管理列表不展示 AppID。 - 企业短信模板列表必须采用自适应布局,在常用桌面及平板视口下无需水平滚动即可看到编辑、删除等操作。 - 运营端“企业模板管理”以响应式列表行展示模板、归属、内容、状态和更新时间;预览、编辑、删除操作在常用视口内始终可见,不得依赖水平滚动。 - 运营端“企业签名管理”的移动、联通、电信报备状态必须将状态文字与通过数/总数允许分行展示;报备详情、报备状态、编辑、删除四个操作按钮在空间不足时按每行两个排列,不得将按钮文字挤成单字换行。 - 运营端企业签名的状态颜色必须按统一业务语义展示:全部目标通道通过为绿色、部分通过为蓝色、审核中/报备中/资料待补充为橙色、审核或报备失败为红色、未提交/未报备/不适用为灰色;卡片总体色先遵循签名审核状态,再汇总真实通道报备状态,不能把“报备中”和“部分通过”混成同一种颜色。 ### 5.12 运营端审核 - 企业认证审核:通过、驳回、查看材料。 - 企业认证详情必须展示客户提交的主体资料、统一社会信用代码、法定代表人、注册地址、营业执照附件、对公账户验证资料、联系人信息、提交时间、审核备注、驳回原因等;通过/驳回必须更新认证记录和租户认证状态,并写操作日志。 - 短信模板审核:通过、驳回、敏感词提示、变量检查。 - 短信模板审核必须支持按客户、应用、模板内容、审核编号和审核状态搜索;搜索应由真实后端 API 支持,前端只做展示和交互。 - 短信审核:查看短信内容、号码量、计费条数、进入审核原因、命中规则、风险原因;支持单条通过、单条驳回、批量通过和批量驳回。批量驳回必须选择待审核任务、填写统一驳回原因并调用真实批量 API,逐项驱动发送链路拒绝处理。 - 审核动作集中在审核中心;企业应用/签名/模板管理页面只做查询、维护、停用、备注和查看审核记录。 ### 5.13 运营端通道管理 - 支持新增、编辑、删除、启用、停用短信通道。 - 删除通道采用软删除或停用归档,不能破坏历史发送、报备、日志外键;删除、启用、停用、复制等高影响操作必须二次确认并写系统日志。 - 支持复制通道:复制后新建一个通道,除 id/code 自动生成外,通道配置、CMPP 参数、通道报备字段、签名/引流报备材料和个性化字段配置均需从源通道复制;名称默认追加“副本”;若源通道为 active,新副本默认保存为 disabled,避免复制后立即占用上游连接。 - 支持发送测试短信。 - 支持查看通道成功率、未知率、失败率、累计发送量。 - 通道列表的通道成本以“分”为单位展示并保留两位小数,例如 `3.00 分`;展示格式不改变 PostgreSQL 中的真实成本单价及计费快照。 - 支持进入通道报备详情。 - 通道报备详情必须按签名任务和引流信息任务展示真实“提交报备时间、报备成功时间、上次发送成功时间、今日发送”数据。“今日发送”至少拆分成功、未知、回执失败、提交失败四项并同时显示数量和比例;提交被拒绝或超时单独计入提交失败,不得混入已接受短信的回执失败率。签名任务汇总该签名在当前通道下的全部引流信息,具体引流任务只统计自身;上次发送成功时间取同一统计范围内最近一条最终成功短信,不得使用报备更新时间或静态演示值替代。 - 通道报备详情页中,签名下的引流信息默认收起,用户点击后展开;展开/收起只影响页面展示,不改变报备数据。 - 通道列表状态区域展示“连接日志”入口;点击后弹窗展示真实连接日志,包括连接请求、连接成功、断开、心跳、重连、异常等事件,日志来源于 Gateway 回写或 OperationLog。通道连接状态、连接数和最近错误必须来自 Gateway 真实上游连接池回写,不能以一次性探测拨号成功代替长连接在线状态。 - 通道操作按钮应保持一致的两列布局,报备详情、编辑、复制、发送测试、启停、删除等操作文案清晰。 ### 5.14 运营端通道组管理 - 支持新增、编辑通道组。 - 支持配置运营商、区域、优先级、权重、备用通道、限速。 - 支持设置企业或应用绑定关系。 ### 5.15 运营端报备任务 - 支持按企业、签名、通道、任务状态、时间搜索。 - 支持生成报备任务。 - 支持在报备任务中导出通道报备资料。 - 支持在报备任务中导入回执。 - 支持查看报备任务详情和处理历史。 - 企业签名、报备任务、通道报备详情三个状态修改入口必须向统一状态接口传递入口标识,并持久化到报备记录,不能只靠前端文案推断。 ### 5.16 运营端报备记录 - 记录报备任务生成、导出、导入、状态同步、失败原因。 - 支持按企业、签名、通道、状态、操作时间搜索。 - 列表必须显示真实通道名称,不以通道 ID 代替;明确展示变更主体为签名或引流信息,并展示签名内容,或“所属签名 + 引流站点 + URL + 备注”。动作和前后状态统一显示中文。 - 人工状态变化必须显示实际修改入口:企业签名修改、报备任务修改或通道信息修改;系统自动审核、冻结、导入等动作显示为系统自动处理,历史无入口字段的数据明确标注为历史记录。 ### 5.17 安全控制 - 企业黑名单:企业应用级号码拦截,同一企业不同短信应用的黑名单互不影响。 - 全局黑名单:平台维度号码拦截。 - 敏感词管理:发送前和审核时命中提示或拦截。 - 手机号段库:用于运营商识别和路由;号段与运营商区分规则使用真实服务端分页、搜索和总数。通用 Tab 位于页面标题下、搜索条件上;每个 Tab 只显示本类统计数字,不同时展示另一类统计。 - 引流信息字段库:用于签名/报备资料结构化采集。 - 企业应用级黑名单、全局黑名单、敏感词管理必须提供搜索、添加、启停/删除功能;所有操作调用真实后端 API,写入系统日志。 - 企业黑名单必须绑定到具体短信应用,支持按企业、应用、手机号、入库原因、状态搜索;发送预览、风控和发送链路只能拦截当前应用的 active 黑名单号码,不得把同企业其他应用的黑名单串用;全局黑名单支持按手机号、原因、状态搜索;敏感词支持按词、分类/级别、状态搜索。 - 全局黑名单“入库时间”、敏感词“创建时间”及运营端其他日期时间字段统一显示为北京时间 `YYYY-MM-DD HH:mm:ss`;格式化必须显式使用 `Asia/Shanghai`,不得直接展示 UTC ISO 字符串或依赖访问者设备时区。 - 企业模板管理的企业名称、企业应用、模板名称、模板内容必须是互相独立且可组合的服务端查询条件;企业签名管理的企业名称、企业应用、签名名称/用途和引流信息同样独立查询。按引流信息搜索时,以签名为父级、命中的引流信息为子级分组展开。 - 企业签名、企业模板和其他删除治理入口成功后必须先展示“删除已完成”、操作单号及幂等重放状态;用户关闭结果弹窗后再刷新父列表,不能因目标卡片提前卸载而丢失成功反馈。 - 企业应用管理提供企业名称、企业应用名称和状态三个独立服务端查询条件。企业黑名单搜索区提供企业名称、企业应用、手机号码、入库原因和状态五个独立条件。 - 运营端充值记录和短信记录表头统一左对齐。 - “引流信息字段库”菜单命名为“报备字段库”,编辑、删除按钮使用通用操作按钮样式。 ### 5.18 风控规则闭环 - 运营端提供风控规则配置页面,可按平台、企业、应用维度配置规则。 - 第一版支持规则:单任务最大号码数、重复号码比例、非法号码比例、黑名单命中比例、非工作时间大批量营销发送、短时间任务创建频控、模板变量异常。 - 默认阈值:重复号码比例 30%、非法号码比例 10%、黑名单命中比例 5%、同一企业/应用 10 分钟内最多创建 10 次任务。 - 每条规则需配置启停、阈值、处理动作、适用范围、优先级。 - 处理动作包括:拒绝发送、进入人工审核、仅记录预警。 - 命中规则后必须生成风控命中记录,包含规则编号、规则名称、阈值、实际值、处理动作、命中时间。 - 进入短信审核的任务必须展示进入审核原因和命中的风控规则。 - 直接拒绝的任务必须向客户端返回可读原因,并写入系统日志。 ### 5.19 运营端账户计费 - 充值记录:支持运营人员人工充值和负数冲正。 - 账单流水:支持冻结、扣费、退费、解冻、人工调整、失败返还。 - 计费规则:支持按短信计费条数和企业应用单价计算费用;发送准入额度为现金余额加授信额度。 - 账务流水必须与短信记录形成可追溯关系,支持对账导出。 - 最终失败、超时失败需要退费。 - “今日返还金额”按当天实际恢复到企业可用余额的消息级流水汇总:包含已扣费后的 `refunded`,以及提交前失败后按 `sms_message_record` 释放的 `released`;任务冻结转扣费过程中按 `sms_batch_task` 产生的内部释放不得计入返还。 - 计费口径可配置为按提交成功计费或按回执成功计费。 #### 5.19.1 报表对账 - 在“数据详单”之后增加“报表对账”一级菜单,包含“对账单”和“利润报表”两个二级菜单;页面必须读取真实 NestJS API 与 PostgreSQL 报表表,不得在前端按明细临时拼接或使用静态数据。 - 对账单按发送日期、企业、企业应用汇总日发送条数和成功条数。发送条数、成功条数均按短信计费条数 `billingUnits` 统计,成功以最终 `delivered` 状态为准。 - 利润报表按发送日期汇总日发送条数、成功条数、消费金额、成本金额、利润和利润率,支持在“企业应用”和“通道”两个统计维度间切换。 - 企业应用维度的消费金额只统计仍为 `charged` 的客户账单,最终失败并退款的短信不再形成收入;成本金额按每次真实提交的通道成本单价快照乘以该次提交最终成功的短信分片数计算。补发只有产生成功分片时才增加对应通道成本,失败、未知或尚未收到成功回执的分片不计成本。 - 通道维度按实际上游 `accepted` 提交统计发送量,按分片回执统计成功量和成本;客户收入只归属最终有效提交,避免补发时重复计算收入。通道成本单价必须在提交记录创建时快照,后续修改通道单价不得改写历史成本;历史缺少分片审计但存在明确成功回执时,才按该次短信计费分片数兼容计算。 - 利润等于消费金额减成本金额;利润率等于利润除以消费金额,消费金额为 0 时利润率按 0 展示。所有金额使用 `0.0001 元`整数金额单位持久化并按四位小数展示。 - 报表按北京时间 T+1 生成,不生成当天未完整数据;每日刷新时必须在同一事务内重新生成 T-4 至 T-1 四个完整自然日,使 72 小时内到达或变化的回执能够修正发送成功和利润结果。 - API 启动后自动补生成最近四个完整自然日,并按日执行滚动刷新;报表查询支持服务端日期、企业、应用、通道和维度过滤及分页。 - “报表对账”增加“发送质量报表”,包含企业应用、通道、签名、引流信息四个 Tab。每个 Tab 按发送日期和对应维度展示发送条数、成功条数、成功率、平均到达时长,并默认按发送条数从大到小排序。 - 发送质量的发送和成功条数均按 `billingUnits` 统计,成功以 delivered 为准;企业应用、签名和引流信息按短信最终状态统计,通道按真实 accepted 提交及同一 Gateway 消息的 delivered 回执统计。 - 平均到达时长只使用成功且时间有效的短信,按 `deliveredAt - submittedAt` 计算;每个日期、每个维度组先计算 P95,剔除大于 P95 的最慢 5% 样本后再求平均。无成功或无有效时间样本时展示为空,不以 0 冒充。 - `SmsMessageRecord` 必须固化实际使用的 `drainageInfoId`。新短信在同签名已审核通过的引流信息中按正文精确包含 URL 匹配,优先最长 URL;最长 URL 出现多个同长度候选时视为歧义并不关联。历史短信使用同一规则回填,未命中或歧义统一归入“未关联引流信息”,不得把一条短信复制到签名下所有引流信息造成重复统计。 - 发送质量报表与对账、利润报表共用 T+1 及 T-4 至 T-1 滚动重算任务,每次重算在同一日期事务内重建四个质量维度。 - 对账单、利润报表、发送质量报表均提供导出功能。导出必须由真实 API 按页面当前筛选条件查询完整结果并生成 CSV,不得只导出当前分页或在浏览器内拼接静态数据。 - 报备字段库采用自适应卡片布局,分开展示统计概览、签名/引流信息通用字段和字段定义;卡片明确展示通道引用数及通用配置数,已被引用的字段不可删除。 - 运营端和客户端用户管理页的新增用户按钮使用标准小尺寸操作按钮,不得占用大块页面空间。 - 项目通用 `Select` 下拉面板默认通过页面级 Portal 渲染,不得被弹窗正文、底部操作栏、卡片或滚动容器裁剪;控件根据视口剩余空间自动向上或向下展开,跟随页面滚动和窗口尺寸变化重新定位,并继续支持名称搜索和滚动浏览全部真实 API 选项。企业签名的企业和企业应用选择等所有页面统一复用该控件,不得另写页面专用下拉实现。 ### 5.20 数据保存与清理 - 发送记录保存 12 个月。 - 支持运营端手动按月清除发送记录。 - 冷热分层由技术方案自行设计,但不得影响最近 12 个月在线查询与对账。 ### 5.21 系统管理与审计 - 用户管理支持角色、权限、启停、重置密码;客户端用户角色第一版限定一个企业管理员,避免多个企业管理员导致租户管理边界不清。 - 用户管理删除按钮使用统一危险操作样式;删除、启停、重置密码必须二次确认并写操作日志。 - 客户端和运营端右上角用户头像提供下拉菜单,支持退出登录、修改密码;账号设置独立菜单第一版不展示。 - 客户端和运营端系统日志均支持分页、搜索和详情展示;详情列内容较长时使用详情卡/弹窗展示,不能被表格窄列截断。 - 系统日志记录登录、退出、配置变更、审核、发送、导入导出、密钥重置、通道复制、通道启停、通道删除、连接状态变化、安全控制变更等操作。 - CMPP 心跳、Submit、Deliver 和 Gateway 周期状态同步属于运行指标或当前状态更新,不得每次追加永久系统日志;连接建立、断开、超时、认证失败,以及恢复状态、实例、锁持有者或失败原因发生真实变化时才写系统日志。 - 系统日志列表必须在数据库侧完成级别、租户、模块、时间和关键词筛选后再分页;所有新旧日志查询接口均限制每页最多 100 条,不允许无界返回全表。 - `OperationLog` 在线保留期默认 180 天;到期日志以小批量事务搬入 `OperationLogArchive`,按 `archiveMonth=YYYY-MM` 形成逻辑月度归档。归档记录不自动删除,归档失败不得删除源记录;归档表达到千万级或维护窗口不满足要求时再评估 PostgreSQL 月度分区。 ## 6. 非功能需求 ### 6.1 性能 - 平台峰值提交能力:500 条短信/秒。 - 批量任务提交接口响应:创建任务后 2 秒内返回任务编号,不同步完成发送。 - 查询接口 P95 响应时间:列表查询小于 500 ms,详情查询小于 800 ms。 - 发送进度延迟:任务进度统计延迟不超过 5 秒。 - 回执处理:单通道回执接入后 10 秒内完成状态更新,异常积压需告警。 ### 6.2 可用性 - 发送链路服务可横向扩容。 - 通道异常不影响其他通道发送。 - 队列消息必须可重试、可追踪、可补偿。 - 关键配置变更必须记录审计日志。 ### 6.3 数据安全 - 企业数据必须按租户隔离。 - 手机号、通道密码、应用密钥等敏感字段加密或脱敏展示。 - 导入文件需做格式校验、大小限制、病毒/恶意内容防护。 - 导出文件需鉴权并记录日志。 ### 6.4 合规与风控 - 支持敏感词拦截。 - 支持黑名单拦截。 - 支持按企业、应用、通道设置日限额、秒级限速。 - 营销类短信不单独硬编码强制审核,由应用配置和风控规则共同决定处理动作。 ## 7. 推荐技术栈 ### 7.1 前端 - React 19 - TypeScript - Vite - React Router - Zustand 或 TanStack Query - ECharts - Lucide React - CSS Modules 或现有全局 CSS 体系 说明:现有原型已使用 React、TypeScript、Vite、React Router、Zustand、ECharts、Lucide,可延续。 ### 7.2 后端 采用 TypeScript 业务后台 + Go CMPP 网关: - 后端 API:NestJS + TypeScript - DB:PostgreSQL - ORM:Prisma - 队列:Redis + BullMQ - 缓存/限速:Redis - 文件:MinIO - API 文档:OpenAPI/Swagger - 监控:Prometheus + Grafana - 日志:Loki 或 ELK - 网关:Go 实现真实 CMPP Gateway,第一版必须接入真实 CMPP 长连接能力 ### 7.3 短信网关 - Go CMPP Gateway 作为独立服务部署,不和 NestJS 业务后台耦合。 - 第一版支持真实 CMPP 2.0/3.0 长连接能力,后续可扩展 SGIP、SMGP、HTTP 通道。 - 网关负责通道连接、登录认证、长连接保活、submit、deliver、active test、重连、滑动窗口、sequence 管理、回执与上行接收。 - 网关必须同时覆盖上游通道客户端能力和下游客户服务端能力;两类连接的账号、密码、IP 白名单、连接数、状态回写和消息映射必须隔离管理。 - NestJS 业务后台负责企业、应用、模板、签名、报备、审核、计费、风控、路由决策和发送任务编排。 - NestJS 与 Go Gateway 之间通过 Redis/BullMQ 队列或内部 gRPC/HTTP 通信;第一版建议使用队列提交发送指令、队列回传提交结果和回执事件。 - Go Gateway 不直接承担业务审核、账务扣费、签名报备判断,只执行已被业务后台路由后的通道提交。 ### 7.4 Go CMPP 开源项目评估结论 可参考开源项目,但不建议直接把完整开源网关作为核心生产服务不加改造使用。建议策略: - 协议层优先评估并复用成熟 Go CMPP 协议库,降低从零实现 CMPP PDU、编解码、sequence、滑动窗口、deliver 解析的风险。 - 网关服务层由本项目自研,围绕本系统的通道配置、限速、发送队列、回执幂等、监控、日志、故障切换、账务关联来设计。 - 对开源网关项目可作为参考实现或测试工具,不直接绑定业务模型。 初步候选: - `bigwhite/gocmpp`:Go CMPP 协议库,适合作为协议层参考或依赖候选。 - `JoeCao/cmpp-gateway`:基于 gocmpp 的网关项目,包含 gateway、backend、simulator 等模块,适合作为架构参考和联调模拟参考。 落地前需要完成一次技术 Spike: 1. 拉取候选项目,确认 License、维护活跃度、CMPP 2.0/3.0 支持情况。 2. 用模拟 SMSC 完成 connect、submit、deliver、active test、disconnect 流程。 3. 压测单连接和多连接 submit 能力,验证 500 条/秒调度下的网关稳定性。 4. 检查异常场景:断线重连、sequence 回绕、窗口满、回执重复、SMSC 慢响应。 5. 决定最终依赖方式:直接依赖协议库、fork 改造、或完全自研协议层。 ## 8. 建议系统架构 ### 8.1 服务拆分 第一版可采用模块化单体加独立发送 Worker,避免过早微服务化: - Web/API 服务:认证、企业、应用、签名、模板、审核、通道、报备、查询。 - Send Worker:消费发送队列,执行路由、限速、提交通道。 - Receipt Worker:处理通道回执、更新发送状态。 - Report Worker:处理报备资料导出、回执导入、状态同步。 - Gateway Adapter:CMPP 通道连接与协议适配。 后续可拆为独立服务: - tenant-service - config-service - audit-service - send-service - gateway-service - report-service - statistics-service ### 8.2 发送链路 1. API 创建批量任务。 2. 拆分号码和变量,生成 message_record。 3. 写入数据库并准备投递发送队列。 4. 根据短信应用的队列等级写入 priority_send.queue 或 normal_send.queue,或在同一 BullMQ 队列中写入可验证的 priority 值;数据库中的 message_record/submit trace 必须保留队列等级。 5. Send Worker 优先消费优先队列,再消费普通队列;多实例部署时必须避免普通队列持续抢占导致优先队列失效。 6. Send Worker 消费消息,校验黑名单、敏感词、限额。 7. 使用可配置号码前缀正则识别运营商;识别不出时按移动处理。 8. 使用手机号段库识别号码省份和城市;识别不出省份时按全国路由处理。 9. 按企业应用绑定的对应运营商通道组执行路由:通道组只能是移动、联通、电信之一,发送时必须同时满足路由规则运营商、通道组运营商、通道组明细 carrier 与号码识别运营商一致;再校验通道本体 carrier 为对应运营商或三网;最后先匹配省网通道,再匹配全国通道,不得直接绑定或 fallback 到非授权单通道。 10. 过滤业务 disabled、连接离线、认证失败、心跳超时或无可用连接数的通道。 11. 通过通道限速器控制 TPS;优先队列不得突破通道配置的供应商 TPS 和连接窗口上限。 - TPS 按物理通道独立计数,不是整个平台共享一个总额度。例如通道 A、B 均配置 100 TPS 时,各自最多 100 TPS,平台理论合计为 200 TPS。 - 同一物理通道被多个通道组使用时,共享 `SmsChannel.rateLimitPerSecond` 的同一额度;通道组成员不提供单独流速配置,避免多个组并发使用同一供应商账号时重复计算额度。 12. 调用 Gateway Adapter 提交短信。 13. 写入 submit 状态。 14. Submit rejected、submit timeout、Gateway 连接断开或未提交成功、receipt failed 等场景按通道组策略补发到下一可用全国通道;unknown、超过 72 小时、超过通道组补发时间上限或关闭补发时不再补发。 15. Receipt Worker 接收回执并更新最终状态。 16. 补发过程必须保持幂等、计费一致和 trace 可查,最终成功只扣一次客户费率。 17. 统计任务进度。 ### 8.3 500 条/秒实现建议 - 创建任务与发送解耦,提交接口只负责入库和投递队列。 - Send Worker 多实例部署,每实例处理固定通道分片或队列分片。 - 发送调度必须支持优先队列和普通队列:优先队列消息在同等通道和限速条件下先被消费;普通队列不能被永久饿死,应通过批次配额、老化策略或可配置调度比例保证恢复。 - 使用 Redis Token Bucket 或本地令牌桶加 Redis 协调实现通道限速。 - 单条发送记录以 message_id 全局唯一,所有提交和回执处理幂等。 - 批量插入 message_record,避免逐条事务。 - 按任务、日期或企业对发送明细表做分区。 - 热查询统计写入 Redis 或统计表,避免实时 count 大表。 ## 9. 核心数据模型 ### 9.1 租户与用户 - tenant:企业主体。 - tenant_certification:企业认证资料。 - user:用户。 - role、permission、user_role:权限。 - operation_log:操作日志。 - operation_log_archive:超过在线保留期的操作日志归档,保留原始日志 id、租户、操作者、动作、资源、详情、发生时间和归档月份。 ### 9.2 短信配置 - sms_application:短信应用。 - sms_application.queue_priority 或等价字段:应用发送队列等级,取值 normal/priority,默认 normal。 - sms_signature:短信签名。 - signature_material:签名证明材料。 - sms_template:短信模板。 - template_variable:模板变量。 - audit_record:审核记录。 ### 9.3 通道与路由 - sms_channel:短信通道。 - sms_channel_group:通道组,必须配置单一运营商 `mobile/unicom/telecom`,不支持三网通道组。 - sms_channel_group_item:通道组通道明细,`carrier` 必须等于所属通道组运营商;省网明细按 `groupId + province` 唯一,全国明细可多条。 - channel_route_rule:路由规则。 - channel_health_metric:通道健康指标。 ### 9.4 报备 - channel_report_field:通道报备字段配置。 - signature_report_material:签名报备资料。 - channel_signature_report_task:通道签名报备任务。 - channel_signature_report_record:报备记录。 - report_export_file:导出文件。 - report_receipt_import:回执导入批次。 ### 9.5 发送 - sms_batch_task:批量任务。 - sms_message_record:单号码发送记录,所有平台任务、API 调用、CMPP 对接发送均进入此表。客户 CMPP Submit 的 `DestUsrTl/DestTerminalId` 包含多个号码时,Gateway 和 NestJS 必须按目标号码拆分为多条独立记录,每个号码分别执行模板/签名、风控、余额、计费、路由、上游提交和回执处理;同一客户 Submit 仍只返回一个 CMPP SubmitResp/Msg_Id,后续 Deliver Receipt 以该 Msg_Id 与各自 `DestTerminalId` 区分。所有拆分记录必须持久化同一 Submit 分组消息 ID,使 Gateway 重启后仍能重建客户最初收到的 Msg_Id。任何目标号码格式非法时必须在落库前拒绝整包,禁止返回成功后只处理首号码。 - sms_api_request:API 调用批次记录。 - cmpp_submit_session:CMPP 对接提交会话或批次记录。 - sms_submit_record:通道提交记录。 - sms_receipt_record:短信回执。 - sms_uplink_message:上行短信。 - sms_send_audit:短信发送审核。 ### 9.6 风控与基础数据 - enterprise_blacklist:企业黑名单。 - global_blacklist:全局黑名单。 - sensitive_word:敏感词。 - risk_rule:风控规则配置。 - risk_rule_hit:风控规则命中记录。 - phone_segment:手机号段库。 - drainage_field:引流字段库。 ### 9.7 账户计费 - tenant_account:企业账户。 - account_balance:企业现金余额。 - account_transaction:账户流水。 - billing_rule:计费规则。 - sms_billing_record:短信计费记录。 - recharge_order:充值订单或人工充值记录。 ## 10. 接口范围 ### 10.1 客户端 API - `POST /api/client/auth/login` - `GET /api/client/dashboard` - `GET /api/client/applications` - `POST /api/client/applications` - `PUT /api/client/applications/{id}` - `POST /api/client/applications/{id}/secret/reset` - `GET /api/client/signatures` - `POST /api/client/signatures` - `POST /api/client/signatures/{id}/submit` - `GET /api/client/templates` - `POST /api/client/templates` - `POST /api/client/templates/{id}/submit` - `POST /api/client/sms/tasks` - `GET /api/client/sms/tasks` - `GET /api/client/sms/tasks/{id}` - `GET /api/client/sms/messages` - `GET /api/client/sms/uplinks` - `GET /api/client/billing/plans` - `POST /api/client/billing/orders` - `GET /api/client/billing/transactions` - `POST /api/client/enterprise-certification` - `GET /api/client/users` - `POST /api/client/users` - `PUT /api/client/users/{id}` - `DELETE /api/client/users/{id}` - `POST /api/client/users/{id}/status` - `POST /api/client/users/{id}/password/reset` - `POST /api/client/auth/password/change` - `GET /api/client/system-logs` ### 10.2 运营端 API - `GET /api/admin/dashboard` - `GET /api/admin/monitor` - `GET /api/admin/statistics` - `GET /api/admin/enterprises` - `POST /api/admin/enterprises` - `PUT /api/admin/enterprises/{id}` - `GET /api/admin/enterprise-applications` - `PUT /api/admin/enterprise-applications/{id}` - `GET /api/admin/enterprise-applications/{id}/connections` - `DELETE /api/admin/enterprise-applications/{id}/connections/{connectionId}` - `GET /api/admin/enterprise-applications/{id}/cmpp-params` - `GET /api/admin/enterprise-signatures` - `GET /api/admin/enterprise-templates` - `GET /api/admin/enterprise-certifications` - `GET /api/admin/enterprise-certifications/{id}` - `POST /api/admin/enterprise-certifications/{id}/approve` - `POST /api/admin/enterprise-certifications/{id}/reject` - `GET /api/admin/audit-summary` - `POST /api/admin/audits/{id}/approve` - `POST /api/admin/audits/{id}/reject` - `GET /api/admin/channels` - `POST /api/admin/channels` - `PUT /api/admin/channels/{id}` - `POST /api/admin/channels/{id}/status` - `DELETE /api/admin/channels/{id}` - `POST /api/admin/channels/{id}/copy` - `POST /api/admin/channels/{id}/test` - `GET /api/admin/channels/{id}/reports` - `GET /api/admin/channels/{id}/connections` - `GET /api/admin/channels/{id}/link-logs` - `POST /api/admin/channel-groups` - `GET /api/admin/report-tasks` - `POST /api/admin/report-tasks/generate` - `POST /api/admin/report-tasks/{id}/export` - `POST /api/admin/report-tasks/{id}/receipt-import` - `GET /api/admin/report-records` - `GET /api/admin/sms/tasks` - `GET /api/admin/sms/records` - `GET /api/admin/sms/uplinks` - `GET /api/admin/dictionaries/blacklists/enterprise` - `POST /api/admin/dictionaries/blacklists/enterprise` - `POST /api/admin/dictionaries/blacklists/enterprise/{id}/status` - `DELETE /api/admin/dictionaries/blacklists/enterprise/{id}` - `GET /api/admin/dictionaries/blacklists/global` - `POST /api/admin/dictionaries/blacklists/global` - `POST /api/admin/dictionaries/blacklists/global/{id}/status` - `DELETE /api/admin/dictionaries/blacklists/global/{id}` - `GET /api/admin/dictionaries/sensitive-words` - `POST /api/admin/dictionaries/sensitive-words` - `POST /api/admin/dictionaries/sensitive-words/{id}/status` - `DELETE /api/admin/dictionaries/sensitive-words/{id}` - `GET /api/admin/risk-rules` - `POST /api/admin/risk-rules` - `PUT /api/admin/risk-rules/{id}` - `GET /api/admin/risk-rule-hits` - `GET /api/admin/billing/plans` - `POST /api/admin/billing/plans` - `PUT /api/admin/billing/plans/{id}` - `GET /api/admin/billing/transactions` - `POST /api/admin/billing/recharges` - `GET /api/admin/phone-segments` - `GET /api/admin/drainage-fields` - `POST /api/admin/drainage-fields` - `PUT /api/admin/drainage-fields/{id}` - `DELETE /api/admin/drainage-fields/{id}` - `GET /api/admin/users` - `POST /api/admin/users` - `PUT /api/admin/users/{id}` - `DELETE /api/admin/users/{id}` - `POST /api/admin/users/{id}/status` - `POST /api/admin/users/{id}/password/reset` - `POST /api/admin/auth/password/change` - `GET /api/admin/system-logs` ### 10.3 通道回调 API - `POST /api/gateway/receipts/{channelCode}` - `POST /api/gateway/uplinks/{channelCode}` 如使用 CMPP 长连接,回执和上行由 Gateway Adapter 直接消费协议消息,不一定暴露 HTTP 回调。 ## 11. 状态定义 ### 11.1 审核状态 - draft:草稿 - pending:待审核 - approved:已通过 - rejected:已驳回 ### 11.2 报备状态 - not_required:无需报备 - waiting_material:待资料 - pending:待报备 - exporting:导出中 - submitted:已提交通道 - approved:报备通过 - rejected:报备失败 - partial_approved:部分通过 ### 11.3 发送任务状态 - draft:草稿 - pending_audit:待审核 - rejected:审核驳回 - scheduled:待定时发送 - queued:排队中 - sending:发送中 - completed:已完成 - canceled:已取消 - failed:失败 ### 11.4 单条短信状态 - created:已创建 - rejected:风控拒绝或规则拒绝 - filtered:已拦截 - queued:待发送 - submitted:已提交 - submit_failed:提交失败 - delivered:发送成功 - undelivered:发送失败 - unknown:未知 - timeout:超时 ### 11.5 短信来源 - platform_task:客户端平台批量任务 - api:企业 API 调用 - cmpp: 企业 CMPP 对接发送 ### 11.6 风控处理动作 - reject:拒绝发送 - manual_audit:进入人工审核 - warn_only:仅记录预警 ### 11.7 账务状态 - estimated:已预估 - frozen:已冻结 - charged:已扣费 - refunded:已退费 - released:已解冻 - adjusted:人工调整 ## 12. 实施步骤 ### 阶段 1:工程初始化 1. 确认第一版范围,关闭或隐藏彩信菜单,保留账户计费菜单。 2. 建立前端、NestJS API、Go CMPP Gateway 的仓库结构或 monorepo 工作区。 3. 配置代码规范、环境变量、构建、Docker Compose。 4. 建立 Prisma schema 和数据库迁移流程。 5. 建立 OpenAPI 文档和前端 API Client 生成流程。 6. 建立 Go Gateway 的配置、日志、监控、健康检查和本地模拟 SMSC 联调环境。 ### 阶段 2:基础能力 1. 实现登录、用户、角色、权限。 2. 实现租户隔离。 3. 实现文件上传和对象存储。 4. 实现操作日志。 5. 实现基础字典:手机号段、敏感词、黑名单、引流字段。 6. 实现现金余额账户和账务流水基础模型。 ### 阶段 3:企业与配置 1. 实现企业认证。 2. 实现短信应用。 3. 实现短信签名和材料。 4. 实现短信模板和变量解析。 5. 实现应用级不符合模板短信处理策略。 6. 实现审核记录与审核工作台。 ### 阶段 4:通道与报备 1. 实现短信通道 CRUD。 2. 实现通道组与路由规则。 3. 实现通道签名报备字段配置。 4. 实现报备任务生成。 5. 实现报备资料导出。 6. 实现回执导入和报备状态同步。 ### 阶段 5:发送链路 1. 实现批量任务创建。 2. 实现号码导入、校验、去重、拆分。 3. 实现风控规则配置、规则命中、审核原因展示。 4. 实现发送审核策略。 5. 实现发送前账户校验、预估、冻结或扣费。 6. 实现发送队列。 7. 实现 Send Worker。 8. 实现 Go CMPP Gateway,支持真实 CMPP 长连接、submit、deliver、active test、重连。 9. 实现回执处理、72 小时超时和上行处理。 10. 实现任务进度统计。 ### 阶段 6:查询与统计 1. 实现客户端批量任务、发送详情、上行短信。 2. 实现运营端任务进度、短信记录、上行记录。 3. 实现客户端账户余额和充值记录。 4. 实现运营端充值记录和账务流水。 5. 实现运营看板、发送监控、数据统计。 6. 实现导出权限和导出日志。 ### 阶段 7:性能与稳定性 1. 压测任务创建接口。 2. 压测队列消费和发送 Worker。 3. 压测回执处理。 4. 验证 500 条/秒提交能力。 5. 验证风控规则命中、审核原因、账务流水一致性。 6. 验证通道限速、故障切换、重试、幂等。 7. 加入监控告警和慢查询优化。 ### 阶段 8:验收 1. 按原型完成页面功能联调。 2. 完成核心流程端到端测试。 3. 完成权限测试。 4. 完成数据隔离测试。 5. 完成账务对账测试。 6. 完成性能测试报告。 7. 完成 Linux 部署方案。 8. 完成上线回滚方案。 ## 13. Codex 开发执行建议 ### 13.1 推荐任务拆分 1. 根据本文创建数据库迁移脚本和实体模型。 2. 实现认证、租户、权限基础模块。 3. 实现企业、应用、签名、模板 CRUD 与审核。 4. 实现现金余额计费和账务流水。 5. 实现通道、通道组、报备任务。 6. 实现风控规则、审核原因、规则命中记录。 7. 实现 NestJS Send Worker、BullMQ 队列和 Redis 限速。 8. 实现 Go CMPP Gateway,并完成与 NestJS 队列事件的联调。 9. 实现发送任务、发送明细、回执、上行。 10. 前端将 mock service 替换为真实 API。 11. 增加集成测试、性能测试和部署脚本。 ### 13.2 给 Codex 的单任务提示模板 ```text 请基于 docs/first-version-development-requirements.md,实现【模块名】。 要求: 1. 遵循现有原型页面和字段命名。 2. NestJS 后端实现 Prisma schema、migration、Service、Controller、DTO、单元测试。 3. Go Gateway 实现配置、连接管理、CMPP submit/deliver/active test、回执事件发布、单元测试。 4. 前端将对应 mock 数据替换为 API 调用,保留现有视觉样式。 5. 原型阶段的 mock、localStorage 或静态数据只能用于早期界面占位;进入系统功能验收后不得继续作为兜底通过路径。审核、通道、连接、日志、黑名单、敏感词、报备字段、用户、账务等闭环必须接入真实 API、数据库或 Gateway 回写;API 不可用时应展示错误态或空态,并将用例标记为阻塞或未通过。 6. 补充错误处理、权限校验、操作日志。 7. 运行构建和相关测试,并更新测试进度文档。 ``` ## 14. 已确认关键决策 ### 14.1 通道协议与报备 - 运营商通道协议参数和回执格式先按公开 CMPP 文档实现。 - 签名报备字段模板由运营端自行配置和手工维护。 - 第一期不考虑通过 API 向运营商传送签名报备资料。 ### 14.2 发送能力 - 需要支持定时发送。 - API 实例启动后必须自动扫描并派发到期任务,管理端手工触发仅作为运维补偿入口,不能作为正常发送的前置操作。 - 多 API 实例只能有一个实例成功认领同一定时任务;认领后进程退出或 Redis 短暂不可用时,任务超时后必须可恢复,且不得重复冻结余额或重复创建队列作业。0 元短信同样适用恢复和幂等要求。 - 导入号码文件格式支持 CSV、TXT。 - 导入文件最大 20 MB。 - 单任务最大号码数默认 100 万条。 ### 14.3 风控默认阈值 - 重复号码比例默认阈值:30%。 - 非法号码比例默认阈值:10%。 - 黑名单命中比例默认阈值:5%。 - 短时间任务创建频控默认阈值:同一企业/应用 10 分钟内 10 次。 ### 14.4 计费与退费 - 最终失败、超时失败需要退费。 - 计费口径可配置为按提交成功计费或按回执成功计费。 - 短信计费规则只按 70/67 字拆分。 ### 14.5 数据保存与更正 - 发送记录保存 12 个月。 - 支持手动按月清除发送记录。 - 冷热分层由技术方案自行设计。 - 超时后供应商又返回成功回执时,不允许自动覆盖业务失败状态。 - 系统需保留二次回执记录,并提供人工更正功能。 ### 14.6 认证、部署与账号体系 - 第一版不考虑多租户独立域名和企业 SSO,只支持一个平台域名。 - 部署环境为 Linux。 - 最终需要给出 Linux 部署方案。 - 企业认证资料采用人工上传、人工审核,不接第三方实名认证或营业执照 OCR。 ### 14.7 待确认问题 暂无阻塞性待确认问题。后续进入详细设计或开发时,如遇具体运营商协议参数、生产部署资源规格等执行细节,再按模块补充确认。 ## 15. 第一版落地执行路线 ### 阶段 0:技术 Spike 目标:优先验证最大技术风险,避免业务代码写完后发现网关链路不可用。 任务: 1. Go CMPP Gateway Spike - 基于 `gocmpp` 验证 CMPP connect、submit、deliver、active test、terminate。 - 参考 `JoeCao/cmpp-gateway` 的连接、重连、SEQID/MSGID 追踪、模拟器设计。 - 搭建最小 Go Gateway,支持配置一个模拟或真实 CMPP 通道。 2. BullMQ 通信 Spike - NestJS 通过 Redis/BullMQ 下发发送指令。 - Go Gateway 消费发送指令或监听队列事件。 - Go Gateway 回传提交结果、回执、上行事件。 3. 500 条/秒链路 Spike - 不接完整业务,只压测 NestJS 入队、Go Gateway 消费、模拟通道响应。 - 验证平台可稳定入队并调度 500 条短信/秒。 - 输出压测结果、瓶颈和下一步优化建议。 验收标准: - 能跑通一条模拟短信:NestJS 入队 -> Go Gateway 提交 -> submit resp -> deliver 回执 -> NestJS 更新状态。 - Go Gateway 断线后可重连,并能继续消费后续消息。 - 所有消息都有 messageId、channelId、sequenceId 或可追踪映射。 - Spike 文档记录是否采用 `gocmpp`、是否需要 fork、哪些能力需要自研。 ### 阶段 1:工程骨架 目标:让前端、NestJS API、Go Gateway 和基础设施能稳定启动。 任务: 1. 建立 monorepo 或多目录结构: - `web`:React + TypeScript + Vite - `api`:NestJS + TypeScript - `gateway`:Go CMPP Gateway - `docs`:需求、设计、接口、部署文档 2. 配置 PostgreSQL、Redis、MinIO、Docker Compose。 3. 配置 Prisma、migration、seed。 4. 配置 OpenAPI/Swagger。 5. 配置日志、环境变量、健康检查。 6. 配置基础 CI 命令:lint、test、build。 验收标准: - `web`、`api`、`gateway` 均可本地启动。 - Docker Compose 可启动 PostgreSQL、Redis、MinIO。 - NestJS 可连接 PostgreSQL 和 Redis。 - Go Gateway 可读取配置并启动健康检查接口。 ### 阶段 2:基础后台 目标:完成业务底座。 任务: 1. 登录认证。 2. 企业/租户。 3. 用户、角色、权限。 4. 操作日志。 5. 文件上传。 6. 系统字典。 7. 手机号段库。 8. 敏感词。 9. 黑名单。 10. 引流字段库。 验收标准: - 所有业务数据具备租户隔离。 - 关键操作写入操作日志。 - 文件上传可用于签名资料和认证资料。 ### 阶段 3:短信配置 目标:客户和运营能配置发送短信所需资源。 任务: 1. 短信应用。 2. 应用密钥和 IP 白名单。 3. 应用发送限额。 4. 应用级“不符合模板短信”处理策略。 5. 短信签名。 6. 签名资料。 7. 短信模板。 8. 模板变量。 9. 平台审核记录。 验收标准: - 客户端可创建应用、签名、模板。 - 运营端可审核签名和模板。 - 发送时只能使用审核通过的签名和模板。 - 客户端不能修改已审核模板主体,只能填写变量。 ### 阶段 4:通道与报备 目标:运营端能配置通道、通道组和签名报备流程。 任务: 1. 短信通道管理。 2. 通道组管理。 3. 路由规则。 4. 通道签名报备字段配置。 5. 签名报备资料。 6. 报备任务生成。 7. 报备资料导出。 8. 回执导入。 9. 报备记录。 10. 签名报备状态同步。 11. 引流信息按通道报备任务、报备记录和回执。 12. 企业签名、通道报备详情、报备任务三个入口共用同一报备状态事实源。 验收标准: - 签名审核状态和通道报备状态分层。 - 运营端提供真实签名审核列表、资料详情和通过/驳回操作;运营端直接新建的签名自动审核通过并写审核记录。 - 引流信息按“报备字段库 → 通道配置引流字段 → 企业签名提交资料 → 通道报备任务”流转,不使用 JSON 中的静态三网状态代替任务状态。 - 发送前必须校验签名在目标通道报备通过。 - 报备任务导出、导入具备幂等和历史记录。 ### 阶段 5:账户计费 目标:发送前能判断是否可发,发送后能对账。 任务: 1. 企业现金账户。 2. 人工充值记录。 3. 现金余额。 4. 发送费用预估。 5. 充值记录。 6. 账单流水。 7. 发送预估费用。 8. 冻结、扣费、退费、解冻。 9. 短信记录与账务流水关联。 验收标准: - 客户端账户余额和充值记录进入第一版。 - 发送前校验企业现金余额与授信额度之和必须大于 0。 - 每条短信记录可追溯到账务流水。 ### 阶段 6:风控与审核 目标:发送前有规则、有原因、有闭环。 任务: 1. 风控规则配置。 2. 默认阈值。 3. 单任务最大号码数。 4. 重复号码比例。 5. 非法号码比例。 6. 黑名单命中比例。 7. 非工作时间大批量营销发送。 8. 短时间任务创建频控。 9. 模板变量异常。 10. 风控命中记录。 11. 短信审核。 12. 审核原因展示。 13. 拒绝原因返回客户端。 验收标准: - 命中风控规则时记录规则编号、规则名称、阈值、实际值、处理动作。 - 进入审核的任务必须展示审核原因。 - 直接拒绝的任务必须向客户端返回可读原因。 ### 阶段 7:发送链路 目标:真实跑通短信发送。 任务: 1. 客户端批量任务创建。 2. 号码导入和拆分。 3. 发送任务入队。 4. Send Worker。 5. 通道路由。 6. Redis 限速。 7. 上游通道 CMPP connect/login、ActiveTest、断线重连和连接状态回写。 8. Gateway 消费 SubmitCommand 并真实 submit 到上游通道。 9. Submit Resp 解析、sequence/messageId 映射和提交状态回传。 10. Deliver Receipt 解析、重复/迟到回执幂等回传。 11. 普通 Deliver 上行解析和上行入库。 12. 下游客户 CMPP Server 监听 `17890`。 13. 下游客户 connect/login 鉴权、IP 白名单、应用状态和连接数校验。 14. 下游客户 CMPP Submit 转平台发送请求,source=cmpp,不创建客户端批量任务。 15. 平台最终回执和上行向下游客户连接投递。 16. 72 小时未知转超时。 17. 任务进度统计。 验收标准: - 平台创建的批量任务只记录客户端任务。 - 平台任务、API 调用、CMPP 对接发送全部按手机号维度进入短信记录。 - 支持 submit resp、deliver 回执、上行短信、超时补偿。 - Gateway `/health` 只表示进程存活,不等于 CMPP 发送链路验收通过。 - `17890` 必须是真实 CMPP Server 监听,能完成客户 connect/login 鉴权、IP 白名单校验和 submit 接入。 - Gateway 必须真实消费 SubmitCommand 并向上游通道 submit,submit resp、receipt、uplink 事件必须进入 NestJS 后端闭环。 - 客户 CMPP 接入发送不创建客户端批量任务,但所有号码必须进入短信记录并可追踪客户侧 sequence/msgId 与平台 messageId。 ### 阶段 8:查询、统计、验收 目标:系统可运营、可排查、可上线。 任务: 1. 客户端批量任务。 2. 客户端发送详情。 3. 客户端上行短信。 4. 运营端发送监控。 5. 运营端任务进度。 6. 运营端短信记录。 7. 运营端上行记录。 8. 运营看板。 9. 数据统计。 10. 操作日志审计。 11. 性能压测。 12. 500 条/秒报告。 13. Linux 上线部署文档。 14. 回滚方案。 验收标准: - 可按企业、应用、通道、任务、手机号追踪发送链路。 - 可按账务流水和短信记录对账。 - 通道列表“今日发送质量”必须按北京时间当天的真实通道提交和回执,展示总数、成功、未知、失败及对应比例,不得使用前端固定零值。 - 运营看板不重复展示通道连接或通道运行数据;按设计基线恢复当天签名发送统计,并按是否关联引流信息分组展示签名、企业、发送总数、成功、未知、失败、成功率和平均到达时长。 - 数据统计的“通道占比”默认统计北京时间当天,可选择单个历史日期重新查询;图表使用真实通道名称和该日期的通道提交量。 - 输出 500 条/秒压测报告。 - 输出 Linux 部署方案,至少覆盖 Docker Compose 或 systemd 部署、环境变量、数据库迁移、日志目录、备份恢复、服务健康检查和回滚步骤。 ## 16. 新 Codex 会话提示词 ## 17. 第一版需求增补记录 ### 2026-07-01 账户与日志展示调整 1. 运营端增加企业人工充值入口。 - 运营端可针对指定企业录入人工充值金额、操作人和备注。 - 人工充值金额支持负数,用于余额冲正或调减;0 金额不得提交。 - 人工充值必须写入充值订单和账务流水。 - 运营端充值记录均为人工充值或负数冲正。 2. 客户端概览指标调整。 - 原“剩余条数”改为“剩余余额”。 - 原“近24小时成功率”改为“今日发送条数和今日成功率”。 - 新增“今日消费金额”和“今日返还金额”展示。 - “今日返还金额”展示当天真实返还到可用余额的金额,包含消息级冻结释放和扣费后退款,排除任务冻结转扣费产生的内部释放流水,金额统一保留三位小数。 3. 运营端增加系统日志。 - 运营端可查询全平台系统日志。 - 日志需支持按企业、模块、级别、操作人、资源 ID 或详情定位。 - 人工充值、审核、风控、发送链路、系统管理等关键动作需要可追溯。 建议新开 Codex 会话后直接发送以下提示词: ```text 当前项目是 CMPP 短信平台第一版开发,工作区内已有前端设计原型和需求文档。 请先完整阅读: 1. docs/first-version-development-requirements.md 2. docs/ui-design-guidelines.md 3. package.json 4. src/routes/AppRoutes.tsx 5. src/layouts/ClientLayout.tsx 6. src/layouts/AdminLayout.tsx 重要前提: - 第一版保留短信业务,排除彩信功能。 - 现金余额计费、人工充值、账单流水进入第一版。 - 前端使用 React + TypeScript + Vite。 - 后端 API 使用 NestJS + TypeScript。 - DB 使用 PostgreSQL。 - ORM 使用 Prisma。 - 队列使用 Redis + BullMQ。 - 缓存/限速使用 Redis。 - 文件使用 MinIO。 - 第一版必须实现真实 Go CMPP Gateway。 - Go Gateway 协议层优先评估 gocmpp,参考 JoeCao/cmpp-gateway 的连接、重连、SEQID/MSGID 追踪、模拟器设计。 - 不从零手写整个 CMPP 协议栈,也不直接照搬完整开源网关;协议层可复用,服务层按本项目自研。 - NestJS 负责业务审核、风控、计费、报备、路由和发送编排。 - Go Gateway 只负责 CMPP 连接、协议提交、submit resp、回执、上行事件回传。 - 当前项目已进入真实开发阶段;前端 mock、localStorage 或静态数据不得作为功能完成标准,也不得作为验收兜底通过路径。涉及审核、通道、连接、日志、安全控制、用户、账务等闭环时,必须接入真实 API、数据库或 Gateway 回写,并补真实后端 smoke 或 E2E 测试。 请从 docs/first-version-development-requirements.md 的“阶段 0:技术 Spike”开始执行。 ## 追加需求:登录与用户管理闭环 ### 1. 登录入口 - 客户端和运营端必须使用独立登录页面:客户端 `/client/login`,运营端 `/admin/login`。 - 两端登录均输入用户名、邮箱或手机号,外加密码和图形验证码。 - 登录接口必须调用真实 NestJS API,不允许前端静态用户、localStorage mock 或纯前端验证码作为验收依据。 - 运营端登录仅允许平台管理员;客户端登录仅允许已关联企业的企业管理员。 - 浏览器认证凭据必须改为服务端随机生成的不可预测会话标识,通过 `HttpOnly`、`SameSite=Lax` Cookie 传输;前端 `localStorage` 只可保留非敏感展示信息,不得保存访问令牌。生产 Cookie 必须启用 `Secure`,因此正式部署本功能前必须先为页面和 API 配置 HTTPS。 - 会话真实状态保存在 Redis,至少包含用户、入口、`sessionVersion`、创建时间、最后有效操作时间、最近密码认证时间、锁定时间和绝对到期时间。Redis 不可用或记录不存在时必须失败关闭,不得退回纯前端会话。 - 运营端连续 60 分钟、客户端连续 120 分钟无用户操作后进入安全锁定,提前 5 分钟提醒。锁定后只需验证当前密码即可生成新会话标识并继续使用,不重复输入账号和图形验证码;锁定超过 4 小时必须完整登录。 - 自动轮询、Dashboard 定时刷新、健康检查和后台标签页请求不得延长无操作期限。前端计时器只负责提醒和锁屏,NestJS/Redis 必须在每次受保护请求前独立执行空闲、锁定和绝对期限判断。 - 单次登录绝对时长为 12 小时,无论是否持续操作均不得自动续期;到期必须使用账号、密码和图形验证码完整登录。修改密码、禁用/删除用户、角色变化和管理员强制下线必须通过 `sessionVersion` 和 Redis 会话立即撤销现有会话。 - 用户、权限、企业状态、应用密钥、通道配置、路由、报备状态和资金调整等敏感操作要求最近 30 分钟内验证过当前密码。超时后由后端返回 `RECENT_AUTHENTICATION_REQUIRED`,前端验证当前密码后自动重试原操作;不能只依赖前端弹窗判断。 - 主动退出、空闲锁定、密码解锁、敏感操作再认证和会话创建均需写真实系统日志;多标签页使用浏览器消息同步锁定、解锁和退出。普通网络错误、400、403 业务拒绝或 5xx 不得被误判为自动退出。 - 在同一浏览器已有运营端或客户端会话时,另一个入口的账号、密码、角色或验证码登录失败只属于本次登录尝试,不得清理、广播退出或跳转已有会话;只有现有会话自身的 401 失效响应才触发退出处理。 ### 2. 用户类型和企业关联 - 运营端用户管理支持创建两类用户:平台管理员、企业管理员。 - 平台管理员不得关联企业,只能登录运营端。 - 企业管理员必须关联一个企业,只能登录客户端。 - 企业管理员登录客户端后,所有客户端 API 必须继承当前企业租户范围,用户、Dashboard、账务、日志等数据不得越权访问其他企业。 ### 3. 用户管理功能 - 运营端和客户端用户管理均需接入真实 API,支持添加、编辑、启用/禁用、删除、修改密码。 - 启用/禁用、删除必须弹窗确认。 - 删除采用软删除,历史系统日志、审核记录和业务记录仍可追溯;删除后用户不可登录且列表默认不展示。 - 客户端用户字段必须包含邮箱和手机号,并支持用户名、邮箱或手机号登录。 - 所有创建、编辑、启用、禁用、删除、修改密码动作必须写系统日志。 ### 4. fail2ban - 用户连续登录失败 5 次后,账号锁定 24 小时。 - 锁定状态必须写入后端持久化字段,后续登录直接拒绝。 - 登录成功后清空失败次数和锁定状态。 ### 2026-07-02 纯 mock 菜单真实化补充 除文档或菜单明确标注为待开发的彩信能力外,第一版所有可进入菜单不得以 mock/static/localStorage 作为系统功能完成标准: 1. 客户端账户余额、充值记录、批量任务、短信发送、短信签名、短信模板必须调用真实 API;签名/模板新增后进入真实审核状态,发送任务调用真实发送链路。 2. 运营端数据统计、账务账户、发送监控、短信审核、短信记录、安全控制、手机号段库、报备字段库、通道组、通道报备字段、报备任务和报备记录必须调用真实 API。 3. 运营端企业管理使用真实租户、账户、应用、签名、模板接口;企业新增、编辑、启用/禁用、删除必须写真实租户表,删除采用软删除或归档,不允许纯前端删除。 4. 企业签名和企业模板运营端列表只展示真实短信配置数据;彩信签名、彩信模板、彩信通道、彩信记录、彩信任务进度等仍归入待开发,不得用静态样例作为第一版短信验收结果。 5. 后端接口暂缺编辑/删除能力时,前端不得用本地状态模拟成功;应只开放真实能力,缺失能力记录为待补接口。 6. API 不可用、数据库不可用或依赖服务不可用时,页面展示错误态或空态;测试记录标记阻塞或失败,不能用静态兜底数据假装通过。 第一步请先不要大规模写业务代码,先输出并创建阶段 0 Spike 的最小工程计划,包括: 1. 目录结构建议。 2. NestJS 与 Go Gateway 的队列消息格式。 3. Go Gateway 最小能力清单。 4. gocmpp 与 cmpp-gateway 的技术评估任务。 5. 500 条/秒 Spike 压测指标。 6. 阶段 0 的验收标准。 然后按计划逐步实现。每完成一步都要运行构建或测试,并更新文档。 ``` ### 2026-07-15 签名与引流资料批量导入、通道映射及统一报备 1. 运营端在“报备任务”下提供“待报备资料”工作台。WPS 在线表格须先由用户另存为 `.xlsx`,系统读取真实工作簿、工作表、表头、单元格和内嵌图片;原始文件及拆出的图片写入 MinIO,导入批次、映射和业务资料写入 PostgreSQL,不支持用 CSV、前端静态数组或浏览器本地存储冒充图片导入。 2. 导入分为“解析预览”和“确认入库”两步。用户可指定企业、企业应用、资料类型、表头行数、数据起始行并复用映射方案;每个源列可映射到签名名称、用途、所属签名、站点名称、URL、备注或报备字段库中的动态字段,同时配置文本/图片/文件、必填和转换规则。源文件中的签名名称同样必须使用完整中文黑括号格式 `【签名】`,不得通过导入绕过页面/API签名校验;源文件字段名称和顺序不固定,映射方案必须可持久化复用。 3. 导入和业务页面的新建/修改只将已审核签名或引流信息标记为待报备,并递增材料版本;不得在每次导入后自动创建通道报备任务。运营人员可跨签名、跨引流信息勾选资料,一次创建统一报备批次。 4. 创建批次时按每条资料所属企业应用的当前生效路由规则展开所有通道;一个签名走多个通道时,必须为每个通道创建或重置独立报备任务并生成一份该通道的 `.xlsx`。无生效路由、通道未配置字段或缺少通道必填资料时,该资料继续保留在待报备池,任务进入“资料待补充”,不得伪装为已完成。 5. 通道“配置签名报备字段”和“配置引流信息字段”弹窗使用字段池,按资料类型分别配置。每列包含标准字段、通道导出表头、列顺序、必填、说明、列宽、文本转换、缺省值以及图片宽高;导出表头和列顺序必须严格使用通道配置,不受导入表格原始名称和顺序影响。 6. 通道导出文件必须为 WPS/Excel 可打开的 `.xlsx`,图片直接内嵌到对应单元格区域,而不是仅写 MinIO URL 或本地路径。批次保留所选材料版本快照、通道文件、行号和通道任务关联,可从最近批次直接下载每个通道文件。 7. 2026-07-28 起,导入“确认”只把每一行保存为待审核明细,不得立即创建、修改或自动审核通过签名/引流信息。签名和引流审核中心分别提供“导入批次审核”页签,可查看整批行明细,一次通过全部、通过勾选项或驳回勾选项;审核通过后才将该行应用到真实业务对象并进入正常待报备流程,行校验失败不得阻断同批其他行。 8. 导入审核必须同时支持新增和修改:页面明确展示每行操作类型、原对象、目标资料、错误原因和审核结果。驳回原因可选,不得因未填写原因阻止常用批量操作;审核人、审核时间和实际处理结果必须落 PostgreSQL。 9. “待生成报备批次”页面由“待生成资料”和“已生成批次”两个页签组成。两个页签均使用后端分页,并可按关键字和时间范围查询;待生成资料可继续按签名/引流类型筛选。 10. 已生成批次展示报备总数、成功数和成功率。报备总数以批次导出文件中的通道报备明细数为准,成功数以对应通道报备任务当前为通过的明细数为准;不展示“已导入回执”“等待回执”等当前无明确业务需求的字段。 11. 两个页签的搜索区使用统一查询操作按钮尺寸。待生成资料应缩短资料类型及“企业/应用/签名/站点”关键字输入宽度并增加“资料变更时间”宽度;已生成批次的“批次生成时间”保持紧凑,不占满剩余空间。查询和重置按钮统一使用全局查询操作区样式。 12. 报备明细是一条签名或引流信息在一个具体通道上的当前报备状态。常用操作为逐条人工修改状态,弹窗只要求选择目标状态,修改原因可选;详情展示所属企业/应用、来源批次、导出文件行号和时间顺序的状态轨迹。 13. 本阶段不提供新的回执导入入口,不实现批量回执文件规范、解析或自动状态覆盖。已有历史数据和后端兼容代码保留以便追溯,后续只有在回执文件格式、匹配键、批量结果语义和异常处理规则明确后再立项。 ## HTTP 客户接口第一版 ### 管理端企业应用配置 - CMPP 与 HTTP 是两套可独立开通的接入能力,不再把 HTTP 作为 `interfaceType` 的互斥选项。运营端在企业应用“接口配置”中维护 HTTP 总开关,以及单条发送、短信状态查询、回执 Webhook、上行 Webhook、上行查询、客户端凭据自助管理等子能力。 - 企业应用新增/编辑页必须将 CMPP 与 HTTP 配置拆成两个视觉和语义独立的区域:CMPP 区只放协议、账号、扩展码、客户接入号、接口密码、连接数、CMPP 白名单及下游重试;HTTP 区只放 HTTP 子能力、HTTP 白名单、QPS、凭据限制、投递模式和 Webhook 策略。协议关闭时收起该协议参数,只保留独立开关和关闭说明,不得再把两套字段混排在同一表单网格中。 - HTTP 配置独立维护 IP/CIDR 白名单、应用级 QPS、签名时间容差、最多有效凭据数、上行保留/查询范围/分页上限、Webhook 超时和最多尝试次数、生产 HTTPS 约束、客户手工重投权限。 - 回执和上行分别配置 `cmpp/http/both/none` 投递模式。Gateway 产生的回执或上行必须先写入现有真实短信记录,再按模式投递;HTTP 回调不得取代或伪造 Gateway、回执匹配和上行认领链路。 - HTTP 访问密钥和 Webhook 签名密钥使用 `HTTP_API_MASTER_KEY` 派生的 AES-256-GCM 密钥加密保存。Secret 只在创建或轮换当次返回,后续运营端和客户端仅显示末四位;允许同时保留多个有效凭据以完成无停机轮换。 ### 客户接口与安全约束 - 第一版提供 `POST /api/openapi/v1/sms/messages` 单条发送、`GET /api/openapi/v1/sms/messages/{messageId}` 状态查询、`GET /api/openapi/v1/sms/uplinks` 上行游标查询和 `GET /api/openapi/v1/sms/uplinks/{uplinkId}` 上行详情。 - 身份只由 `X-App-Key` 对应凭据确定,不接受请求体中的企业或应用身份。签名原文为 `METHOD + "\n" + PATH + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + SHA256(rawBody)`,使用 Secret 执行 HMAC-SHA256。 - 时间戳默认允许正负 5 分钟;签名成功后使用 Redis `SET NX EX` 防 nonce 重放,并按应用在 Redis 执行秒级 QPS 限制。IP 白名单与 CMPP 白名单相互独立。 - 单发必须提供 8~128 位 `Idempotency-Key`。PostgreSQL 对 `(applicationId, idempotencyKey)` 建唯一约束并保存请求体哈希和响应快照:相同内容重放原响应,不同内容返回 409;`clientMessageId` 在应用内唯一。 - 单发复用现有 `SendChainService`,必须经过真实企业/应用、签名、模板、风控、余额、计费、通道路由和 Redis 队列链路;接收成功返回 202,不代表运营商提交或终端到达成功。 - 上行查询只返回已匹配或人工认领到当前应用的记录,默认最近 24 小时,单次范围和分页上限由应用配置控制;未匹配和歧义上行不得泄露给任一客户。 - 客户错误使用 `application/problem+json` 和稳定业务码。客户 Swagger 只包含四个 `/openapi/v1` 接口,不得包含 admin、client 管理或 gateway 内部接口。 - 客户 HTTP API 使用独立公网源地址配置;管理页面、参数复制和 Swagger 链接必须从真实后端返回的 `HTTP_API_PUBLIC_ORIGIN` 生成,不得沿用管理页面 `window.location.origin`。预生产固定为 `https://api.lisglo.com`,该灰云域名只暴露 `/api/openapi/v1/*`、客户 Swagger 和健康检查,不得暴露 admin/client 管理接口或前端页面。 ### HTTP Webhook 与客户端页面 - 状态回执和上行回调分别配置 HTTPS URL 与独立事件类型,共享该端点的签名密钥;每个事件生成唯一 `eventId`。回调请求用 `TIMESTAMP + "\n" + rawBody` 执行 HMAC-SHA256,客户必须按 `eventId` 幂等。 - Webhook 禁止重定向,并在保存和每次投递前解析域名,拒绝环回、私网、链路本地、共享地址和元数据地址。2xx 成功;网络错误、408、429、5xx 可按立即、1 分钟、5 分钟、15 分钟、1 小时、6 小时、24 小时重试;其他 4xx 直接终结。 - PostgreSQL 分别保存 Webhook 事件、投递状态和每次尝试摘要;客户和运营人员可查询,授权后可手工重投。首次投递与重试均由 BullMQ 执行,不得使用浏览器定时器或 localStorage 冒充。 - 客户端“短信基础配置”新增“接口对接”,包含接口概览、访问凭据、回调配置、接口文档、调用与回调记录五个页签;企业应用卡片显示 HTTP 开通状态并跳转。客户端上行列表改为真实服务端条件查询,不再先拉全量数据后仅在浏览器过滤。 ## 2026-07-16 运营端与客户端移动端适配要求 1. 运营端和客户端在宽度不大于 780px 的小屏设备上统一使用顶部栏加左侧抽屉导航。抽屉默认关闭,由顶部菜单按钮打开,支持遮罩、关闭按钮、Esc 和选择菜单后关闭;菜单内容在抽屉内部独立滚动,业务内容不得被完整侧栏挤到页面下方。 2. 320px、360px、375px、390px 和 768px 常见视口不得出现页面级横向滚动。登录面板、筛选条件、表单、统计卡、操作区和弹窗必须限制在可用宽度内,桌面端既有可折叠侧栏行为保持不变。 3. 通用数据表格在小屏下改为带字段名称的纵向记录卡片,操作按钮允许换行;不得要求用户横向滚动才能看到状态、失败原因或操作。业务专用的签名、引流、通道报备列表也必须按同一原则重排。 4. 多列查询条件和报表筛选在小屏下收敛为单列;相关查询、重置和导出按钮保持可见并可换行。通道组配置、手机号段库、HTTP 接口凭据、企业签名报备目标等固定宽度区域必须取消页面级最小宽度。 5. 移动端顶部栏至少保留导航入口、平台标识、通知和用户菜单;交互控件应具备可读的无障碍名称,抽屉打开状态使用 `aria-expanded` 表达,并尊重系统“减少动态效果”设置。 6. 客户端彩信签名、彩信模板、彩信发送、彩信任务、彩信详情和上行彩信均未完成真实后端闭环,在功能完成前不得展示“彩信服务”菜单或其子菜单;保留内部路由不代表可向客户开放。 7. 运营端手机号段库使用平台通用 Breadcrumb、Button、Input、Tabs、Table、Tag、Pagination 和 Modal 实现。Tab 位于标题下方和筛选条件上方;当前 Tab 仅显示自身的真实总数。统计使用紧凑信息带,手机号段突出显示、运营商使用语义标签、删除使用克制的危险操作样式,不得另造一套组件或用大面积统计卡挤压表格。 ## 2026-07-16 全平台金额精度要求 1. 企业应用客户单价、通道成本单价、账户余额、授信额度、充值、消费、返还、短信计费金额以及对账和利润报表中的全部金额,统一精确到人民币小数点后 4 位;输入最多允许 4 位小数,页面及导出文件统一展示 4 位小数。 2. 数据库和计费链路继续使用整数运算,最小金额单位统一为 `0.0001 元`,即 `1 元 = 10000 金额单位`。历史字段名中的 `Cents` 为兼容既有 API 暂不改名,但其数值语义同步调整为金额单位,不再表示人民币“分”。 3. PostgreSQL 金额列统一升级为 `BIGINT`。上线迁移时既有按分保存的数据乘以 100,应用换算除数由 100 改为 10000,确保迁移前后实际人民币金额完全一致。 4. 企业应用单价修改必须写入真实 `SmsApplication.customerUnitPrice`,例如 `0.0325 元/条` 保存为 `325`;后续预估、冻结、扣费、返还和利润统计均使用该整数值,不得在前端或后端再次四舍五入到分。 5. API 返回 `BIGINT` 金额时仅在 JavaScript 安全整数范围内转换为 JSON number;超过安全整数范围必须显式报错,避免静默丢失金额精度。 ## 2026-07-16 企业应用接口参数复制与下游接入约束 1. 运营端企业应用列表同时提供 CMPP 参数和 HTTP 参数复制;客户端应用列表提供 CMPP 参数复制,客户端“接口对接”页提供 HTTP 参数复制。复制内容必须来自真实应用和 HTTP 配置 API,不得用静态数组、localStorage 或页面默认值冒充。 2. 客户端仅在应用已开通对应协议时允许复制参数。未开通 CMPP 时按钮不可操作,且客户端直接请求 CMPP 参数 API 必须返回 403;未开通 HTTP 时同样不得复制 HTTP 参数。 3. 客户侧 CMPP 网关地址和端口是平台对外公布的下游接入地址,分别由 `CMPP_PUBLIC_HOST`、`CMPP_PUBLIC_PORT` 配置,不得读取任一上游短信通道的网关地址。当前预发布环境默认值为 `8.160.169.106:17890`;正式生产必须使用独立正式地址和配置。 4. 参数复制必须兼容平台当前 HTTP 页面:优先使用 Clipboard API;浏览器因非安全上下文或权限拒绝时,使用受控 textarea 复制降级,并向用户明确反馈成功或失败,不得无提示失败。 5. `cmppMaxConnections` 必须在 Gateway 登录时按应用和活动 TCP 会话真实计数并限制,同时由 API 的连接事件校验兜底。连接关闭或异常断开后必须及时释放连接名额并回写断开事件。 6. CMPP IP/CIDR 白名单必须在登录和连接事件中校验;运营端修改白名单、关闭接口、停用应用或降低最大连接数后,Gateway 应在下一次心跳校验时关闭不再符合条件的存量连接,不能只限制后续 Submit。 ## 2026-07-18 手工验收瑕疵收口要求 1. 图片上传单文件不得超过 2MB,其他文件不得超过 10MB;前端选择文件时即时提示,NestJS 接口与 MinIO 入库前必须再次校验,不得只依赖页面限制。 2. 企业统一社会信用代码仅允许英文字母和数字,前后端同时校验;上传文件名需换行,营业执照预览和下载使用一致的操作样式。 3. HTTP 由未开通切换为开通时,默认开启发送、状态查询、回执 Webhook、上行查询、上行 Webhook 和客户自助凭据全部能力,回执与上行默认使用 HTTP Webhook;参数复制页须展示业务化投递方式,不直接暴露 `cmpp/http/both/none` 原始值。 4. 企业应用、短信记录的“查询”和“重置”即使条件未变也必须重新请求真实 API;CMPP 连接详情不得依赖水平滚动,长 AppID/连接 ID 必须可换行。 5. 短信审核列表时间统一为 `YYYY-MM-DD HH:mm:ss`;审核人和审核时间由当前 HttpOnly 会话在服务端写入,列表用“更多信息”展示;操作成功后待审角标立即重新请求真实仪表盘 API。 6. 短信详情同时展示客户提交时收到的接入号和平台送往上游的接入号;运营商区分规则显示中文名称并支持真实 DELETE API。 7. 所有登录用户发起且成功的 POST/PUT/PATCH/DELETE 操作必须写入 PostgreSQL `OperationLog`,至少保存操作人、方法、路径、资源 ID、状态码、IP 和 User-Agent;不得记录密码、密钥或请求体。 ## 2026-07-20 回执、报表、安全与公开 HTTP 接口收口要求 1. 供应商回执优先按内部消息 ID,或 `channelId + gatewayMessageId + DestTerminalId` 对提交记录/长短信分片审计作唯一匹配。若同一供应商账号配置了多个物理通道连接,回执可能从非原提交连接返回,此时仅允许在“账号、Gateway 主机、端口、协议及 CMPP 版本全部一致,且 `gatewayMessageId + DestTerminalId` 只有一个候选提交/分片”时跨连接认领,并仍归属原提交的逻辑通道;任一字段不同或候选不唯一必须拒绝自动匹配。每个回执事件必须以逻辑通道生成数据库唯一键,并发或重复事件不得重复生成记录、退款、计费或企业应用投递。 2. 成功回执统一更新短信主记录状态、回执状态、到达时间、真实通道、通道消息号、原始回执码和文本;失败、超时和未知回执保留可解释状态,最终失败只退款一次。 3. 对账、利润和质量报表统一以短信记录计费条数为发送量,以唯一主记录终态统计成功/失败;收入取扣费流水,返还取退款流水,成本取真实提交通道单价,利润等于收入减成本。到达时长按提交至成功回执计算,延迟回执由 T+1 和 T-4 至 T-1 重算覆盖;查询、页面和 CSV 共用同一聚合表。 4. 运营端与客户端用户接口使用各自安全 DTO,禁止输出密码散列、会话版本、登录失败内部计数和密钥字段。后端禁止自删除/自停用、禁止删除或降权最后一个平台管理员;企业管理员允许删除或停用至零人;所有操作强制校验跨租户范围,唯一冲突返回 HTTP 409 和明确字段。 5. HTTP 单发公开契约使用 `mobile`、`content` 和可选 `clientMessageId`,不要求内部签名或模板 ID;服务端按 CMPP 同一规则识别已审核签名、模板及变量,复用风控、余额、计费、路由和队列。成功返回可查询 messageId,业务拒绝返回对应 4xx,不得在已创建记录后返回“批次不存在”。 6. 客户发送候选只返回 approved 签名和模板,管理视图可查看历史状态。模板变量必须拒绝空变量、未闭合、中文或非法名称、重复名称及超长名称;客户端签名视图仅返回必要报备汇总状态。 7. 报备资料提供官方 XLSX 模板和按当前筛选导出;导入拒绝空文件、错误扩展名、超限文件以及公式/脚本单元格。分析、提交和导出日志记录操作人、文件名、筛选条件、成功数、失败数和 IP,不保存密钥或完整请求体。 8. 下游客户连接超时清理必须同时覆盖“最后心跳早于阈值”和“最后心跳为空但连接建立时间早于阈值”;刚建立且尚未超过阈值的空心跳连接不得误删。历史共享供应商账号导致的错归回执,只能在同一主记录、同一通道消息号、同一目的号码且唯一提交记录可证明时改绑并聚合;存在歧义时必须保留原数据供人工核查。 ## 2026-07-20 客户端用户管理移动操作可达性要求 1. 客户端用户管理在`≤780px`使用键值卡片时,编辑、改密、禁用/启用、删除四项操作不得继续沿用不可换行的桌面横排;采用2×2网格或可访问的“更多操作”菜单,任何允许动作都不得因容器裁切而消失。 2. 390×844和375×667下四项操作必须全部可见、可聚焦、可命中,触控热区高度至少44px;操作组应具有包含目标用户名称的可访问名称。 3. 删除仍必须走真实客户端用户API与确认流程,不得通过前端隐藏或静态数据冒充;页面验收只打开并取消确认时,不得产生DELETE请求或数据库状态变化。 4. 1440×900、1366×768和768×1024必须同步回归。1366桌面宽表若仍需内部横向滚动,滚动条必须可发现且操作可到达;固定操作列和邮箱列宽另按全站Table整改治理。 ## 2026-07-21 双门户会话隔离与深链恢复要求 1. 运营端和客户端必须分别使用独立的浏览器存储键、跨标签广播频道和 HttpOnly Cookie;后端只允许目标门户的 Cookie 认证对应路由,不能依赖前端隐藏或跳转实现隔离。 2. 受保护路由必须先向真实会话接口完成初始化,再决定渲染或跳转;刷新和直接打开深链时不得先跳登录页,失效后重新登录必须回到同站点、同门户白名单内的原目标。 3. 同一浏览器可以同时保持运营端和客户端登录。锁定、解锁、会话过期和主动退出事件仅作用于当前门户;退出一端不得删除、广播或撤销另一端会话。 4. 会话锁定必须保留目标页面,显示锁定原因、恢复说明和剩余策略;锁定状态下暂停首次业务路由请求,解锁后在原 URL 重新挂载并从真实 API 加载数据。当前标签页内已加载的非敏感草稿不得因跨门户事件被清除。 5. 旧共享 Cookie 和旧共享 localStorage 只允许做一次同门户迁移或清理,不得继续作为双门户认证来源。生产发布会使旧共享 Cookie 失效时,必须在发布说明中明确需要重新登录。 ## 2026-07-21 UI/UX A2安全上传与日志导出补充 - 客户端文件上传和下载必须使用`/api/client/files`专用接口;后端从当前会话用户反查企业,不接受请求头指定文件归属。客户端仅允许企业认证、签名报备、引流报备三类用途及对应安全目录,跨企业文件统一不可见。 - 运营端和客户端系统日志导出必须读取PostgreSQL真实筛选结果,具备提交中防重复、结构化成功结果、操作单号、文件下载、失败原地重试和会话恢复后的筛选保留。客户端导出不得包含详情JSON、IP、User-Agent等内部字段。 - CSV导出最多10000条并明确截断状态;对`= + - @`开头单元格做公式注入防护。瞬时恢复状态可使用按门户隔离的`sessionStorage`,不得作为业务数据源。 ## 2026-07-21 UI/UX A3审核风险治理补充 - 运营端签名、模板“通过”必须先调用后端资格预检,确认层展示对象名称、唯一ID、企业、应用、资料完整度、阻断原因和后续影响;阻断项存在时后端和前端均不得批准。 - 审核决定必须使用当前登录会话审核人、客户端生成并重试复用的幂等键,以及对象`updatedAt`状态版本。后端仅允许`pending`对象在Serializable事务中原子变更,版本不一致返回409且不得覆盖其他审核员结果。 - 审核成功必须返回持久化`AuditRecord.id`作为操作单号;同对象同幂等键重试返回原结果且不得重复更新或重复审计。旧的签名/模板批准接口也必须进入同一治理服务,不得保留绕过入口。 ## 2026-07-21 UI/UX A4报备生成风险治理补充 - 待报备资料必须由后端预检应用启用状态、资料审核与待报备状态、资料版本、启用路由/通道、通道字段配置和必填资料;不合格资料在列表中不可选择,并返回可执行的阻断原因。 - 生成接口提交时必须重新预检,不能信任列表时的前端状态。零个可生成通道组合必须返回业务4xx,不得先创建空批次或显示成功。 - 业务去重键必须覆盖资料类型、资料ID、资料版本、应用、通道及适用运营商;历史已生成组合返回跳过及既有批次,不得无提示覆盖旧任务或重复导出。 - 每次生成要求8至128位客户端幂等键。后端通过PostgreSQL事务锁认领操作,相同键同一请求返回原操作单及`replayed=true`,相同键用于不同范围返回409;生成结果按成功、跳过、失败分项返回并提供操作单号。 ## 2026-07-21 UI/UX A5删除治理补充 - 通道、签名和模板删除前必须由后端返回对象身份、活动依赖数量与对象摘要、影响范围、`allowedActions`、`blockedReasons`、状态版本和可恢复说明;前端不得自行推断或只显示通用风险文案。 - 删除提交必须包含预检版本和8位以上幂等键,删除原因选填。后端在Serializable事务中重新以`updatedAt`和未删除状态做条件更新,并写入包含原因、依赖、影响及幂等键的`OperationLog`,返回操作单号和重放标识。 - 客户端只能预检和删除当前会话企业的签名/模板,不得删除运营通道;通道的活动通道组/路由/连接依赖以及签名级联安全规则继续按专项口径处理。单独删除模板不得因已创建发送或批量任务而阻断。 - 删除采用逻辑删除,历史发送、回执、计费、审核和审计数据继续保留;恢复需有审计依据。所有旧删除入口必须委托同一治理服务,禁止保留绕过路径。 ## 2026-07-21 UI/UX A6人工充值治理补充 - 充值记录页和企业管理页必须共用同一人工充值组件。取消、右上角关闭或完成后必须销毁未提交金额、备注、预检结果和幂等键;重新打开必须是新草稿,不得自动恢复资金操作输入。 - 最终入账前必须调用真实后端预检并重复展示企业名称、编码、唯一ID、操作方向、当前现金余额、本次变动、预计现金余额和备注;余额以PostgreSQL账户读取结果为准,不能用前端静态计算冒充资格检查。 - 人工充值请求必须使用当前会话操作者、8至128位幂等键和账户`updatedAt`版本。相同幂等键同一请求返回原订单/操作单和`replayed=true`;不同范围复用键或账户版本变化必须返回冲突并要求重新核对。 - RechargeOrder、TenantAccount余额增量、AccountTransaction和OperationLog必须在同一Serializable事务内原子完成;审计记录需包含前余额、变动金额、后余额、订单号、原因和幂等键。正数为充值,负数为冲正,金额精确到小数点后4位且不得为0。 - 运营端充值记录必须提供可截图的账户充值回执。回执只能使用真实充值订单、企业和关联账务流水数据,展示系统真实Logo、入账状态、企业名称与编码、订单号、入账时间、前后余额、入账方式和备注;不得使用前端临时数据补齐缺失字段。 - 回执的“本次充值金额”按实际精度显示:整数金额不显示小数部分,存在小数时仅保留有效小数位;前后余额继续遵循平台统一的四位金额精度。 ## 2026-07-22 UI/UX A7公共Dialog契约 - 所有公共Dialog打开后必须把焦点送入弹窗,并将Tab/Shift+Tab约束在当前顶层弹窗;背景内容必须同时不可聚焦、不可被辅助技术读取,页面滚动必须锁定。 - Dialog必须通过`aria-labelledby`关联可见标题;遮罩不得伪装成可聚焦关闭按钮。右上角、取消、Escape和遮罩关闭必须使用同一关闭协议,关闭后焦点返回触发控件。 - 可编辑弹窗必须声明dirty状态。存在未保存内容时,任何关闭入口都必须先显示具名确认层;继续编辑保留草稿并恢复原焦点,只有明确放弃后才能销毁草稿。 - 确认层作为顶层`alertdialog`管理焦点并隔离父弹窗;手机端按钮应安全堆叠,320—1440px内不得产生页面级横向溢出。 ## 2026-07-22 UI/UX A2/A3收口补充 - 客户端上传必须由可见页面操作触发客户端专用接口;租户只能从当前会话用户解析,上传用途、对象目录和租户内下载均由后端校验,真实对象必须写入配置的MinIO并可回读。 - 企业认证上传区在手机端必须完整显示长文件名;步骤条允许安全横向浏览且默认展示第一步,省市选择不得因固定宽度被裁切。 - 客户端日志导出必须显示提交中、完成数量、操作单号、下载和失败重试;客户端CSV仅允许时间、级别、模块、操作人、动作、资源ID六列,不得包含详情、IP或嵌套内部字段。 - 签名和模板审核通过/驳回必须共用资格预检、状态版本、幂等键、事务审计和结构化结果协议。页面必须在最终决定前显示对象唯一标识、资格和影响范围;取消确认不得改变状态或写审计。 ## 2026-07-22 应用日发送上限与HTTP参数默认值补充 - 每个短信应用的日发送上限默认为100000条,按北京时间自然日和去重后目标号码数计数。客户端、公开HTTP和下游CMPP入站必须共用PostgreSQL原子配额计数,多API实例并发不得突破上限。 - 客户端/HTTP整批超限时不创建任务、短信记录或账务冻结,HTTP返回429及`DAILY_SEND_LIMIT_EXCEEDED`。CMPP合法Submit整包超限时返回唯一一个非0 `SUBMIT_RESP`(日限额映射`result=8`),每个目的号码仍保留`rejected/DAILY_LIMIT`审计主记录;不冻结、不扣费、不占用额度,也不再生成或投递异步`DELIVER`回执。 - 日额度在任务正式受理时按北京时间占用;待审核和定时任务占用受理日额度,后续审核拒绝、取消或发送失败均不返还。发送校验固定遵循身份/应用权限、请求结构和全部号码基础格式、签名/模板/引流、禁发时段及风控、路由通道、余额原子校验与冻结、日额度原子占用、任务落库入队的业务优先级。余额早期读取只能用于提示,最终资格判断与冻结必须紧邻任务受理执行。 - 号码基础校验只判断空值、字符、长度、数量上限、重复及多号码完整性;号段识别用于运营商和地区快照及路由提示,未知号段不得据此拒绝,必须继续按全国或三网兼容通道处理。 - 预发布历史数据库仍为`SQL_ASCII`时,数据迁移不得使用可能把多字节UTF-8字符拆成单字节处理的正则字符类。中文签名修复必须同时校验主键和原始字节序列,并从迁移前备份恢复完整UTF-8;长期生产数据库必须规划迁移到`UTF8`编码。 - 首次开通HTTP接口时,后端默认开启单条发送、状态查询、回执回调、上行查询、上行回调和客户端自助密钥六项能力,回执/上行投递默认为HTTP Webhook。参数复制必须包含应用名称、AppID、六项能力、基础地址、文档、QPS、白名单和真实投递方式。 ## 2026-07-23 供应商连接主动心跳与自动重连要求 1. 供应商出站CMPP通道只要状态为`active`,首次连接失败、运行中断链、心跳超时或Gateway重启后都必须持续自动恢复到`desiredConnections`;不得依赖下一条短信触发惰性重连。 2. Gateway必须主动向供应商发送`CMPP_ACTIVE_TEST`并按`Sequence_Id`确认`CMPP_ACTIVE_TEST_RESP`。默认30秒一次,连续3次未响应判定断链;间隔和阈值允许按通道配置。短信提交、回执响应和心跳写包必须串行,断链后旧读写循环必须退出。 3. 网络类失败按5秒、15秒、30秒、60秒、2分钟、5分钟逐级退避并增加抖动,达到上限后持续重试;鉴权或凭据类失败仍持续重试,但默认降为5分钟一次。重连成功后清零失败次数和下次重连时间。 4. API每30秒协调数据库期望状态与Gateway真实状态,以Redis租约避免多实例重复下发;连接数不足、心跳陈旧、失败且到期或状态缺失的活动通道需要重新下发连接意图。 5. 手动停用或逻辑删除通道必须向Gateway发送`DisconnectChannel`,关闭连接池、主动心跳和重连监督器;协调任务还必须清理数据库状态与通道状态不一致的存量连接。重新启用或修改地址、端口、账号、密码、版本、窗口、连接数、心跳配置时立即使用新配置连接。 6. `CmppConnectionState`必须记录重连次数、最近重连尝试、下次重连时间、错误分类和最近心跳。运营端允许配置心跳参数并展示重连次数、下次时间和最近错误。 7. 多API实例并发恢复同一供应商通道时,数据库必须以`channelId + connectionId`的部分唯一索引约束`applicationId IS NULL`状态行;唯一冲突复用并更新既有状态,不得生成重复连接状态。 ## 2026-07-23 通道、应用、详单与报表口径补充 - 新建上游短信通道默认端口为 `7890`;当前仅支持 CMPP,管理端和 API 均不得接受 HTTP/SGIP 作为通道协议。 - 通道业务代码写入真实通道运行配置 `config.serviceId`,默认 `SMS`,限制为 1~10 个 ASCII 字符,并用于 Gateway CMPP `Service_Id`。 - 新建企业应用的“每任务最大号码数”默认 `10000`,超过上限仍由后端整任务拒绝。 - 安全控制的企业黑名单、全局黑名单和敏感词接口不得返回逻辑删除记录。 - 所有日报表按 `SmsMessageRecord.billingUnits` 统计长短信分片条数,输出提交、发送、未知、成功、失败五项;平台拦截(`status=rejected`)计入提交但不计入发送,并保证 `发送=未知+成功+失败`。 - 通道维度只存在已经路由到通道的记录,因此该维度的提交数等于发送数;应用、签名、引流信息和对账维度的提交数包含平台拦截。 - 二级添加、编辑页面必须继续高亮其所属侧边菜单。 ## 2026-07-24 CMPP/HTTP 通讯交互日志要求 1. 运营端“系统日志”必须将人员操作审计与协议通讯日志分成两个独立页签。通讯日志至少支持协议、交互方向、事件类型、结果、关键字和时间范围过滤,并展示平台消息号、上游消息号或HTTP请求号、完整手机号或账号、结果码、耗时和安全详情。 2. CMPP应覆盖客户登录/Submit、供应商SubmitResp、状态报告Deliver、上行Deliver及平台下游投递;HTTP应覆盖客户发送请求和平台回执/上行Webhook。数据库中一条记录必须对应一个真实业务报文,不得把同一报文的“入口收到”和“处理成功”拆成两条记录;处理结果、结果码和耗时写在该报文同一条记录中,失败、重试等后续真实交互另行记录。 3. Gateway收到状态报告或上行后,必须对解包/解码失败及转发NestJS失败输出结构化安全日志;NestJS入口把业务处理结果合并回同一报文记录,以便区分“上游未发”“Gateway未收到”“Gateway转发失败”和“API落库失败”。一条正常短短信的供应商侧完整成功闭环应依次展示四个真实报文:平台→通道 `CMPP_SUBMIT`、通道→平台 `CMPP_SUBMIT_RESP`、通道→平台 `CMPP_DELIVER`、平台→通道 `CMPP_DELIVER_RESP`;箭头只表达报文实际传输方向。 4. 通讯日志不得保存短信正文、密码、密钥、Token、签名鉴权值或完整HTTP请求体;手机号按完整明文保存、展示并支持关键字查询,不做脱敏。CMPP心跳不得逐包写入数据库,连接健康仍使用连接状态和聚合指标。 5. 通讯日志写入不能阻塞短信主链路,默认批量异步写入,缓冲区应有上限和溢出告警;热数据默认保留30天,保留期允许通过环境变量配置。 6. 通讯日志方向固定使用“企业应用 → 平台、平台 → 供应商通道、供应商通道 → 平台、平台 → 企业应用”。供应商长短信每个真实 `SUBMIT` 和 `SUBMIT_RESP` 分片各记一条,企业应用每个真实 `SUBMIT_RESP` 也必须记录;内部 `submit-result` 聚合回调不是协议报文,不得重复生成通讯日志。 7. 供应商长短信回执必须先写入对应 `SmsMessageSegmentAudit`。仅当同一提交尝试的全部分片均为 `delivered` 时,主记录才转 `delivered`;任一分片明确失败可进入最终失败/补发状态,分片尚未齐全时主记录保持 `submitted`,不得由首片成功提前聚合。内部业务终态只聚合一次,但对企业应用的CMPP状态报告必须按其原始Submit分片逐片投递,并分别使用平台当初为该分片返回的`CMPP_SUBMIT_RESP.Msg_Id`;HTTP Webhook仍按原HTTP消息投递一个最终事件。 8. 长短信任一分片返回非成功终态时,系统必须通过该分片审计关联的提交记录识别当前发送尝试,不得仅以主记录保存的首片上游消息号判断;确认属于当前尝试后,整条短信立即进入失败/补发或退款终态,无需等待其余分片回执。 9. 回执和上行投递方式不得由运营人员选择。企业应用开通CMPP接口即按CMPP投递,开通HTTP接口且对应Webhook地址非空即按HTTP投递,两者同时满足时双投;任一地址为空时只跳过该类HTTP事件。运营端企业应用HTTP参数页必须始终可编辑回执和上行Webhook地址,不因HTTP接口开关关闭而隐藏。 10. Gateway向企业应用发送真实 `CMPP_DELIVER` 以及收到企业应用真实 `CMPP_DELIVER_RESP` 时,都必须各写一条通讯交互日志,分别使用“平台→企业应用”和“企业应用→平台”方向;下游投递记录继续承担排队、重试和ACK业务状态,不得以通讯日志替代。 ## 2026-07-26 依赖安全治理补充 - 依赖安全整改不得直接执行 `npm audit fix --force`;必须逐条核对公告的真实利用条件、当前代码调用路径、目标版本兼容性和生产依赖/构建工具边界,并通过干净安装、全量测试和生产构建。 - 前端不得引入 React Router 实验性 RSC 服务端 API;在官方发布兼容修复版前,必须由自动门禁扫描源码并固定已验证的客户端 SPA 版本。仅因审计建议降级到包含已知 XSS、RCE 或 DoS 公告的旧版本属于禁止操作。 - PostCSS 必须固定到 `8.5.18` 或更高修复版本。平台不得接受用户 CSS 后交由构建链处理;如未来新增此能力,必须显式禁用不可信 previous source map 自动加载并重新开展威胁建模。 - ExcelJS 的旧版 `minimatch` 调用接口与安全修复版 `brace-expansion` 5.x 不兼容时,允许使用受测试的本地 CommonJS 兼容适配层;适配层只能转发到官方有长度上限的实现,必须同时验证旧版 minimatch 花括号匹配、Excel 读写和干净 `npm ci`。 - Prisma CLI 只用于生成、迁移和构建,不属于 API 请求运行路径。其无上游修复版本的中危工具链公告需记录接受条件并持续跟踪,不得为审计数字清零而把 Prisma 7 数据访问栈盲目降级到 6.x。 ## 2026-07-26 用户登录标识复用、组合查询与管理员保护提示 1. 用户逻辑删除后,原用户记录、用户主键及历史审计关联必须继续保留;用户名、邮箱和手机号仅在未删除用户范围内唯一。新建用户可以复用逻辑删除记录曾使用的登录标识,但必须生成新的用户主键,不得继承旧用户的角色、企业归属、密码、会话或权限。 2. 用户登录和按用户名查找必须显式排除逻辑删除记录。活动用户之间的登录标识并发冲突继续由PostgreSQL唯一索引保证,并返回HTTP 409、冲突字段及明确中文提示。 3. 运营端用户管理将用户姓名、登录账号、所属企业、用户角色和状态拆分为独立条件;客户端将用户姓名、登录账号和状态拆分。点击查询或重置时调用真实后端组合查询,条件之间使用AND,登录账号内部对用户名、邮箱和手机号使用OR;不得加载全量数据后仅在浏览器过滤。 4. 运营端用户管理的五组查询条件与查询/重置操作必须使用共享查询控件宽度并允许按完整控件自然换行;不得通过缩窄字段把所有条件强塞在一行。“新增用户”作为业务操作与查询操作保持独立,在窄屏下不得挤入查询字段之间。 5. 删除、禁用或降权最后一个平台管理员时,后端实时拦截继续作为权威判断;企业管理员不设“最后一名”保护,可删除或停用至零人。前端必须在当前确认弹窗内以`role=alert`显示平台管理员保护失败原因和处理建议,保持弹窗打开,禁止只向浏览器控制台输出未处理Promise;请求期间确认按钮必须禁用。 ## 2026-07-26 企业应用停用与回执清算补充要求 - 删除企业前必须检查其企业应用;只要存在`active`或`disabling`应用就阻止删除,并提示先完成应用停用。 - 删除企业前还必须在企业账户事务锁内检查真实余额;余额大于或小于 0 均禁止删除,并提示“完成余额清算后方可删除,请给企业充值到金额为0”。只有余额恰好为 0 且不存在启用或停用中的企业应用时才允许逻辑删除,避免删除检查与并发充值、扣费或退款竞争。 - 点击停用应用时,系统必须统计尚未收到供应商回执的短信、待发送回执、等待`CMPP_DELIVER_RESP`的回执、仍可重试的失败投递及待投递上行。 - 无待清算数据时,应用直接转为`disabled`并断开该账号的全部下游CMPP连接;存在待清算数据时,运营可选择“等待回执后停用”或“强制停用并断开连接”。 - “等待回执后停用”将应用转为`disabling`:立即拒绝新短信Submit,但保留或允许下游账号重新连接以接收历史回执;待清算数据归零后自动停用。 - `disabling`状态必须展示进入原因、各类待清算数量、当前连接数及自动停用时间;运营点击启用可清除停用计时并恢复为`active`。 - “停用中”最长保留72小时,从`disablingAt`起算。到期仍未清算时自动转为`disabled`,剩余下游投递标记`abandoned`并停止重试,然后断开全部下游连接。 - 应用停用后才到达的供应商回执仍更新短信主记录并保存原始证据,但下游投递记录直接标记`abandoned`,不得继续推送或形成重试告警。 - Gateway读取已形成的待投递回执不能依赖企业或应用当前是否启用,避免“已生成回执但账户停用导致永远无法读取”的投递死锁。 ## 2026-07-26 风控规则、逐号码拦截与短信人工审核补充要求 1. 风控规则只保留全局默认和企业应用级覆盖两层;应用级同编码规则优先于全局规则。企业级覆盖不再存在,企业应用表单和数据模型中的`maxPhonesPerTask`删除,单任务号码上限完全由`MAX_PHONES_PER_TASK`规则控制。本条取代2026-07-23“应用每任务最大号码数”旧要求,测试环境既有应用值无需迁移。 2. 可配置规则仅包括单任务最大号码数、非工作时间大批量营销发送和10分钟客户端任务创建频控。重复号码比例、非法号码比例、黑名单命中比例、模板变量异常不再作为可配置风控规则;历史规则与命中记录保留审计但不再生效或展示。 3. 10分钟任务频控只按同一企业应用、`SmsBatchTask.sourceType=client`统计真实客户端任务;CMPP、公开HTTP、运营通道测试和风控预检不得计入。配置上限N时,第N+1个客户端任务进入规则指定动作。 4. 非工作时间规则支持`HH:mm`开始、结束时间并允许跨日,时区固定使用`Asia/Shanghai`;全局和企业应用级覆盖均可分别配置。 5. 手机号码基础合法性只判断“1开头、总计11位、全部为数字”,不得依赖可能滞后的手机号段表。非法号码为确定性拦截,不进入人工审核;客户端/HTTP短信记录标记`submit_failed + submitStatus=rejected + INVALID_PHONE`,CMPP已受理的多号码提交对非法目的号码生成平台失败回执,其他合法号码继续处理。 6. 黑名单按单号码确定性拦截,不再按命中比例拒绝整批。命中平台或企业应用黑名单的客户端/HTTP记录标记提交失败、金额为0且不提交通道;CMPP已受理记录生成`REJECTD`失败回执。混合批次中的非黑名单号码继续发送。 7. 模板变量异常指本次发送缺少模板必填变量或传入模板未定义变量。该校验保留为不可配置的确定性拒绝,返回明确的缺失/多传变量原因,不进入人工审核;模板创建时的人工审核不能替代每次发送的变量完整性校验。 8. 短信审核页面只展示待人工审核和人工审核记录;自动放行和自动拒绝不得混入“人工通过/人工驳回”。号码数量可点击查看真实号码明细,字段仅为手机号码、号码归属地、运营商和短信记录状态,并提供服务端搜索与分页。 9. 创建待审核批次时,每条待审`SmsMessageRecord.reviewTaskId`必须同步保存。人工通过或驳回应同时兼容短信直连审核任务和`SmsBatchTask.riskTaskId`关联路径,保证审核任务、批次、短信状态及入队/拒绝动作一致;本次不修复或补发升级前历史异常数据。 ## 2026-07-26 编号名称统一补充要求 - `SmsBatchTask.taskNo`在运营端短信任务进度、客户端批量任务、客户端首页及发送成功提示中统一显示为“发送批次号”,不得再显示为笼统的“任务编号”或“批次编号”。 - `SmsSendTask.taskNo`在短信审核列表、筛选、详情及号码明细标题中统一显示为“审核任务号”。 - 报备任务及其状态记录中的任务标识统一显示为“报备任务号”。 - 数据库内部主键、发送批次号、审核任务号、报备任务号和协议`Msg_Id`保持原有数据结构与编号格式,本次只统一用户可见名称,不做字段迁移。 ## 2026-07-26 企业删除拦截提示补充要求 - 删除企业或变更企业状态被后端业务规则拦截时,错误必须显示在当前确认弹窗内,弹窗保持打开;不得只写入被遮挡的页面级错误区域。 - 请求处理中禁用确认、取消及弹窗关闭操作,避免重复提交;仅在操作成功后关闭弹窗并刷新企业列表。 - 企业仍有启用或停用中的应用时,必须展示后端返回的应用数量和“先完成应用停用”提示。 ## 2026-07-26 运营页面细节与通道重连补充要求 1. 企业签名和引流信息报备状态中,通道运营商必须显示为移动、联通、电信或全网等中文名称;目标通道使用真实通道名称展示,不得用内部通道编号替代。 2. 短信上行列表为上行内容保留足够列宽,列表可展示最多三行并在表格容器内横向滚动;完整内容继续以详情为准。 3. 编辑启用中的通道时,仅当网关地址、端口、账号、密码、CMPP版本、连接数、窗口或心跳参数的实际值发生变化才请求重连。名称、运营商、地区、单价、服务号、扩展位、企业代码及TPS限速等业务参数不得触发重连;启用和停用状态变更仍按原规则连接或断开。 4. 运营端短信记录首次进入及点击重置后,默认查询北京时间昨天和今天两天,仍允许用户选择其他日期。 5. 下游投递详情按时间线卡片展示每次投递,分别呈现中文状态、发送/ACK/截止时间、连接ID、Sequence_Id、Msg_Id、ACK Result和错误,不使用需要横向滚动的宽表。 6. “网关异常”的“提交异常”Tab列表标题区域必须与容器边框、表格留出清晰间距,并展示当前结果总数;分页区域具有独立分隔。 7. 原提议的报表“T-4未知转失败”本轮明确取消,不改变既有日报未知状态、重算逻辑或历史数据。 ## 2026-07-26 通道补发归因与发送详情补充要求 1. 模板短信和直接签名短信必须使用短信记录保存的真实`signatureId`进行通道路由及失败补发,不得要求直接签名短信必须存在模板;无法选出备用通道时必须写结构化原因、已尝试通道和通道组信息,禁止静默吞掉异常。 2. Gateway返回的每条聚合和逐分片提交结果必须携带连接命令原始`submitId`。API必须按`submitId`精确更新一次`SmsSubmitRecord`;滚动升级期间收到不含`submitId`的旧结果时,只能在短信记录、通道和提交尝试构成唯一候选时兼容,零个或多个候选必须拒绝并记录日志,禁止批量覆盖历史尝试。 3. 迟到的旧尝试结果只允许更新其对应提交尝试和分片审计,不得覆盖短信主记录当前尝试的通道、上游消息号或终态。 4. 发送详情中的“通道发送与回执”必须优先按`SmsMessageSegmentAudit.submitId`重建逐次尝试,显示每次真实通道、发送时间、提交结果及各分片回执;不得用短信主记录最终通道回填所有历史尝试。 5. 通道组成员顺序、优先级、权重或主备关系变更必须写操作审计,保存修改前后有序成员及真实通道名称,便于解释某条短信发送当时使用的路由配置。 ## 2026-07-26 长短信失败并发补发与账务幂等补充要求 1. 同一`SmsSubmitRecord`无论收到多少个分片失败回执、重复聚合结果或并发工作线程,只允许创建一个下一跳补发记录。数据库必须以来源提交记录建立唯一补发关系,不能只依靠进程内锁、先查后建或短信主记录当前状态判断。 2. 任一分片明确失败仍可及时判定本次长短信尝试失败,不要求等待其余失败回执;后续分片回执继续完整落库和写通讯日志,但只能复用已取得的补发决定,不得再次向Gateway发布提交命令。 3. 短信扣费、冻结释放和最终失败退款必须使用稳定业务幂等键。企业账户余额变更在数据库事务内按企业串行并使用原子增量,禁止读取旧余额后覆盖写入;相同幂等键重复调用必须返回原交易,不得再次改变余额。 4. 同一短信记录只允许形成一个内部最终业务结论、一笔最终退款和一个HTTP最终回执事件。CMPP下游必须按原始客户Submit分片分别生成最终状态报告,使用“短信记录+原始客户分片序号”数据库唯一键;同一分片重复终态处理返回原投递,不得再次发送。HTTP Webhook继续使用短信记录级稳定事件号和事件/端点唯一关系。 5. 补发抢占成功、并发复用及下游投递去重必须写结构化日志,至少包含平台消息号、短信记录、来源提交记录、下一跳`submitId`、通道和复用的投递记录。 6. 历史重复提交、通讯报文、回执、退款及客户ACK属于事故审计证据,不得在功能migration中删除或覆盖。历史余额修正必须先完成账户与流水专项对账,再通过可审计冲正处理。 ## 2026-07-27 签名审核与运营端数据展示补充要求 1. 签名审核资格必须读取签名提交时保存的`reportRequirementSnapshot.fields`和`signatureReportValues`,只校验`reportTypes`包含`signature/both`的必填字段;不得再硬编码公司名称、信用代码、法人、责任人和资质文件。仅用于引流报备的字段不能阻断签名审核。 2. 审核中心内“风控规则”固定放在最后一项,面包屑归属审核中心。 3. 运营看板今日签名发送统计必须单列展示提交失败;送达失败不得混入提交拒绝或提交超时。今日企业消费排行必须按北京时间当天真实`SmsBillingRecord.billingStatus=charged`金额聚合,不得使用充值记录或仅取最近若干企业拼装。 4. 每次真实上游提交必须保存当时选中的通道组,短信发送详情展示通道组和通道;历史数据允许在migration时按仍可确认的应用、运营商和通道成员关系回填,后续路由变更不得改写已持久化归因。 5. 通道测试短信处于回执等待状态时与普通短信使用相同状态样式,不得因“测试短信”说明显示红色失败;发送测试弹窗的接入号和网关密码必须使用独立表单名称及自动填充语义,避免密码管理器串填。 6. 企业应用的通道组选择控件不得突破卡片宽度;通用输入控件在有无提示文案时控制区顶部对齐。 7. 分片补偿审计按`createdAt`从早到晚展示,并显示审计时间;同一时刻按分片序号和主键稳定排序。 ## 2026-07-28 运营端列表与审核详情补充要求 1. 报备字段库的“被通道引用”只统计仍未删除的通道,并按不同通道去重;已删除通道的历史映射不再阻止字段删除。 2. 通道报备详情只展示仍未删除的企业签名;已删除签名的历史报备记录继续保留在数据库审计链路中,但不进入当前业务列表。 3. 运营看板“今日企业消费”只排行仍未删除的企业;消费金额继续按北京时间当天真实已计费短信聚合。 4. 短信审核列表展示发送企业和企业应用,不展示审核任务号和审核原因;任务号、原因及号码明细保留在详情和查询能力中。 5. 短信任务进度的号码数量提供“查看列表”入口,号码弹窗必须从真实短信记录服务端分页和搜索,并展示手机号、归属地、运营商和短信状态。 6. 运营端企业签名的引流资料只保留“引流url或号码”业务字段,不再要求填写独立“引流信息”;新增和编辑均以该字段作为真实保存与检索值。 7. 审核中心各审核页面的查看入口统一命名为“详情”;详情必须展示审核时间和审核人员用户名。运营自动通过显示“系统自动”,无法追溯审核人的历史记录显示“-”,不得伪造人员。 8. 企业管理列表不展示实现来源类注释,不向运营人员暴露“数据来自真实接口”等研发说明。 ## 数据统计:签名在通道与运营商维度的发送质量 - 数据统计页默认查询北京时间当天,并允许选择任意一个不晚于今天的自然日。 - 仅统计短信记录已关联平台签名的记录;正文中出现但企业未在平台登记、未形成`signatureId`关联的签名不纳入本统计。 - 签名总览按业务短信记录统计发送量、送达成功、送达失败、提交失败、成功率和平均到达时间;同一业务短信在总览中只计算一次。 - 签名通道明细按真实`SmsSubmitRecord`提交尝试统计。短信发生切换通道补发时,各次提交分别归入实际通道,因此通道提交次数允许大于业务短信数。 - 运营商取短信发送时识别并持久化的真实号码运营商;通道与运营商是实际发送组合,不假设一个通道只支持一个运营商,也不把通道永久挂在某一个运营商下。 - 查看签名明细时,“运营商概览”的数量按真实`SmsMessageRecord`业务短信去重统计,同一短信发生通道切换或补发仍只计一条;成功率为最终送达成功业务短信数除以该运营商业务短信总数,展示名称统一为“最终成功率”。 - 查看签名明细时使用“通道行 × 运营商列”矩阵,每个有数据的单元格展示提交次数、成功率、平均到达时间及提交失败数量;所选日期无真实提交显示`—`,不据此推断通道不支持该运营商。 - 平均到达时间只统计成功送达的提交尝试,从该通道提交受理时间开始,到该通道成功回执完成为止;长短信以全部成功分片完成时间为准。 - 签名列表提供签名、企业或应用关键字查询和后端分页;查看详情不应依赖前端模拟数据或浏览器本地存储。 # 列表查询性能与分页约束(2026-07-28) - 除通道组外,运营端和客户端业务列表必须使用真实后端、数据库分页;浏览器不得先拉取完整结果再切片分页。 - 本轮覆盖短信记录、短信任务进度、报备任务、报备记录、企业应用、企业签名及引流、企业模板、短信通道、上行短信、充值记录,以及对应客户端页面。 - 列表筛选条件必须传入后端并参与总数统计;分页响应统一包含 `items`、`total`、`page`、`pageSize`。页面翻页只读取目标页,筛选和重置回到第一页。 - 短信记录列表只返回当前页面渲染及详情所需字段;CSV 导出使用独立后端导出接口,不通过浏览器当前页或全量列表拼接。 - 企业应用和签名等下拉框使用轻量选项接口,不得为了选择器加载连接、资料、报备任务等完整关联对象。 - 分页排序字段应有数据库索引;Nginx 对 JSON、JavaScript、CSS、CSV、文本和 SVG 响应启用 gzip,降低传输与解析等待。 - 通道组本轮按用户明确要求排除,保留现状,后续如数据规模扩大再单独改造。 ## 企业应用列表字段安全约束(2026-07-29) - 企业应用列表及分页接口只能在 Prisma 查询中引用 `SmsApplication` 模型真实存在的字段;接口入参或响应别名(例如 `passwordCipher`)不得作为数据库 `select`、`omit` 或筛选字段。 - 企业应用列表必须排除真实敏感字段 `secretHash`,不得通过列表接口返回应用接入密码;密码只允许在既有受控的专用参数接口中按权限读取。 - 企业应用黑名单等页面加载筛选项时应使用轻量企业应用选项接口,避免依赖包含连接状态和统计信息的完整企业应用列表。 ## 短信号码路由识别性能约束(2026-07-29) - 短信发送仍以启用的 `PhoneCarrierRule` 作为运营商路由判定依据,手机号段表继续用于省份识别;不得直接用 `PhoneSegment.carrier` 替代运营商业务规则。 - 启用的运营商正则规则应在 API 进程内编译并短时缓存,默认有效期 30 秒且允许通过 `PHONE_CARRIER_RULE_CACHE_TTL_MS` 调整;并发首次加载必须合并为一次数据库查询,规则新增或删除成功后必须立即使当前进程缓存失效。 - 缓存失效期间正在执行的旧规则查询不得覆盖新一代缓存;多实例部署时允许其他实例最多保留一个 TTL 周期的旧缓存,后续扩容到多 API 实例时应升级为跨实例版本通知。 - 省份识别必须把 7 位至 3 位候选前缀放入一次真实 PostgreSQL 查询,并按前缀从长到短选择首个有省份的结果;未知号码也不得逐级产生 5 次数据库往返。 - `SmsMessageRecord` 已持久化 `carrier` 后,补发和再次选路必须同时复用该记录的 `carrier/province`,包括已确认省份未知而保存为 `null` 的情况,不得重复加载运营商规则或号码段。 ## 运营看板与短信记录运营商筛选(2026-07-29) - 运营看板“今日发送趋势”按 `Asia/Shanghai` 自然日固定返回 00:00 至 23:00 共 24 个小时桶,以折线图同时展示每小时业务短信提交总条数和最终状态为 `delivered` 的成功条数;无数据小时必须补零,不使用前端静态数据。 - `SmsMessageRecord.queuedAt` 为 PostgreSQL `timestamp without time zone` 且保存 UTC 时间,小时分桶必须先按 UTC 解释该字段,再转换为 `Asia/Shanghai` 提取小时;结果不得依赖 PostgreSQL 会话时区,不得把北京时间 09:00 的记录显示在 01:00。 - 运营看板“审核处理趋势”更名为“审核处理速度”。按企业认证、短信审核、模板、签名、引流信息五类展示北京时间当天已完成审核数量及平均处理时长;处理时长为同一审核事项本轮审核完成时间减本轮提交时间,无有效提交时间或出现负时长的记录不参与平均值。 - 签名通道发送质量明细的运营商概览固定按移动、联通、电信排列;未识别运营商如有数据排在三大运营商之后,数据库聚合返回顺序不得直接作为展示顺序。 - 运营端短信记录新增运营商筛选,选项为全部、移动、联通、电信、未识别。筛选必须由真实后端和 PostgreSQL 执行,并同时作用于分页总数、当前页和 CSV 导出;“未识别”包含空运营商及不属于三大运营商标准/兼容值的历史记录。 ## 短信审核列表信息密度调整(2026-07-29) - 短信审核列表将“发送企业 / 企业应用”、“提交时间 / 审核来源”和“号码数量 / 状态”分别合并为三个上下分层的信息格,相关信息不得删除或改为仅在详情中展示。 - 短信内容列应设置为列表主要宽列,桌面端目标宽度不小于 440px;当可用宽度不足时由表格容器横向滚动,不得通过压缩内容列造成短信正文难以阅读。 - 号码数量继续使用真实审核任务关联短信记录数量,并保留打开真实号码分页列表的交互;本次仅调整展示布局,不改变审核查询、批量选择、通过或驳回流程。 ## 号码发送频次风控(2026-07-30) - 系统内置并启用两条全局兜底规则:同一企业应用、同一手机号码在北京时间自然日内最多提交 10 条业务短信;在按北京时间时钟对齐的固定 5 分钟周期内最多提交 5 条业务短信。达到阈值的短信仍允许提交,下一条开始直接拒绝。 - 两条规则独立计数、独立命中;任一规则命中即直接拒绝该号码。首版处理动作固定为直接拒绝,不进入人工审核,也不提供放行后继续发送的自动动作。 - 隔离键固定为`applicationId + phoneNumber + ruleCode`。同一号码在不同企业应用下互不影响;同一企业的不同应用也分别计数。 - 计数单位为业务短信号码记录:一次任务向同一号码提交一条短信计 1 条,长短信分片和通道失败补发不重复计数。格式非法、黑名单命中或整个任务已被前置风控拒绝的号码不进入号码频次计数。 - 24 小时规则使用`Asia/Shanghai`自然日 00:00:00 至次日 00:00:00;5 分钟规则使用 00、05、10 等整 5 分钟对齐周期。进入下一周期时该周期计数自动从 0 开始,不采用命中时刻向后滚动的滑动窗口。 - 运营可分别为企业应用新增两条规则的个性化阈值;应用规则启用时覆盖同码全局兜底规则,未配置或停用时继续使用全局兜底。号码频控阈值只能是大于 0 的整数,周期、时区、对齐方式及直接拒绝动作首版不可修改。 - 命中时必须持久化企业、应用、号码、规则、阈值、触发值、周期起止、来源和触发时间。命中后的同周期提交继续拒绝,但不得重复生成相同的活跃触发记录。 - 风控规则页提供真实后端分页的号码频次触发记录,支持按当前应用范围、号码和拦截中/周期已到期/已人工解除状态查询。 - 运营可对单条触发记录执行“解除并清零”,必须填写原因并记录操作人、时间和审计日志。解除只清零该应用、号码、规则的当前计数,不删除历史记录,也不影响另一条频控规则;同周期再次超过阈值时生成新一代触发记录。 - 计数占用、首次命中记录及活跃命中关联必须由 PostgreSQL 原子完成,并能承受同一应用、同一号码的并发提交;不得使用进程内 Map、Redis 临时键、浏览器 localStorage、静态数据或 Mock 作为正式实现。 ## 平台级号码频控白名单(2026-07-30) - 运营端在风控规则页维护平台级号码频控白名单。处于启用状态的号码,在全平台所有企业及所有应用下均豁免`PHONE_FREQUENCY_24H`和`PHONE_FREQUENCY_5M`两类号码频控,包括企业应用个性化覆盖规则。 - 白名单不豁免号码格式校验、企业/应用黑名单、敏感词和内容审核、模板与签名审核、余额、应用日限额、路由、报备及其他风控规则;首版不提供企业级或应用级白名单。 - 白名单提供真实后端和数据库分页的增删改查,号码在平台内唯一,支持启用、停用、号码筛选和已删除历史查询。用途说明必填,备注选填;新增、修改、启停和删除均记录操作人、时间、修改前后内容及操作审计。 - 新增或恢复启用白名单时,清零该号码在所有企业应用及两类规则下的当前计数,并解除尚未到期的活跃命中;修改号码时同时清零旧号码和新号码;启停切换或删除时同样清零,确保号码退出白名单后从零开始重新计数。历史命中和白名单记录不得物理删除。 - 白名单有效期间的提交不写入号码频控状态、不增加计数、不产生号码频控命中。白名单写操作事务返回后发起的新请求必须遵循新状态;已经在写操作前进入频控事务的并发请求允许按其已读取的事务快照完成。 - 批量发送时白名单必须按号码集合分块查询,不得逐号码访问数据库;正式实现不得使用 Mock、静态数组或浏览器 localStorage。 ## 客户端工作台与短信发送体验完善(2026-07-30) - 客户端短信服务工作台的账户状态必须基于当前企业真实认证记录展示“已认证”或“未认证”,不得使用账户启停状态代替认证状态;企业主体展示真实企业名称。签名数量统计当前企业未删除、未禁用的真实签名。 - 模板状态和签名状态分别展示当前企业真实待审核数量并可进入对应菜单;“服务提醒”改为“批量任务”,展示当前企业来源为客户端且状态为`pending_review`的真实批量任务数,并可进入批量任务菜单。 - 客户端“今日发送趋势”复用真实 Dashboard 24 个北京时间小时桶,同时展示提交量和成功量;UTC 存储时间必须先按 UTC 解释再转换为`Asia/Shanghai`,不得依赖数据库或浏览器本地时区。 - 签名与引流信息页点击“重置”时,即使筛选条件已经为空,也必须回到第一页并重新请求真实后端,以获取最新签名审核、报备和引流信息状态。 - 运营端企业认证审核和短信模板审核首次打开及点击重置后,默认查询待审核状态。 - 短信发送页的字数和预计条数必须随编辑后的实际短信内容即时重新计算;单条短信不超过70个Unicode字符计1条,长短信按每67个字符计费,预计条数为单号码计费条数乘有效号码数。单价单位显示为“元/条”。 - 提交发送任务失败时使用页面中央弹窗展示真实后端错误;定时发送控件提供按北京时间计算的“今天”按钮,并以浅色背景标出北京时间当天。提交给后端的定时时间必须显式携带`+08:00`时区。 - 提交任务成功后弹窗展示真实任务编号和发送号码数。“继续发送短信”清空当前发送表单和导入内容;“查看任务进度”进入批量任务页面并按任务编号定位本次任务。 ## 工作台金额与审核查询控件统一(2026-08-03) - 客户端短信服务工作台在“今日返还金额”前展示“今日消费金额”,金额取当前企业 Dashboard API 按北京时间当天汇总的真实消费值,与运营端账务口径一致,不在浏览器重新估算。 - 审核中心的企业认证、短信、短信模板、短信签名和引流信息审核页面均提供“提交时间”日期区间筛选;短信签名和引流信息的“导入批次审核”页签也按导入批次提交时间筛选。 - 提交时间筛选必须传入真实后端并由 PostgreSQL 执行。日期边界按 `Asia/Shanghai` 自然日解释,开始日包含 `00:00:00`,结束日包含 `23:59:59.999`;格式非法、日历日期不存在或开始日晚于结束日时,API 返回受控参数错误。 - 运营端查询区建立全局统一尺寸:普通输入框/下拉框使用统一标准宽度,日期区间控件使用更宽的统一宽度,查询/重置按钮使用统一固定宽度;窄屏下控件和按钮自适应整行,不产生页面级横向溢出。 - 风控规则页的规则范围、平台白名单和号码频次触发记录三组查询条件均使用上述统一布局。白名单和触发记录提供查询、重置,重置后以明确的默认条件重新请求真实后端。 ## 短信内容引流信息识别与统计(2026-08-03) - 本期只识别、记录、查询和统计短信内容是否含引流信息。引流资料是否已报备、审核状态及报备进度均不得拦截或转人工审核;所有发送入口移除`DRAINAGE_NOT_APPROVED`决策,既有模板、签名、余额、黑名单和其他风控规则保持不变。 - 运营端“系统管理”新增“引流识别规则”页面,规则存储在真实数据库,支持 URL、手机号码、固定电话三类表达式的新增、编辑、启停、优先级和测试。变更保留版本并写操作日志,发送入口按当前启用规则生成识别快照和规则版本。 - URL 识别覆盖带协议链接、无`http://`的裸域名、短链接、IP 地址及端口/路径,并支持中文标点相邻和中文句号替代域名点号;URL遇到空格、制表符、换行或其他空白字符时必须立即结束,空白后的字符不得拼接进前一个链接,空白拆分的域名也不得恢复成一个URL。手机号码仍支持`+86`、空格、短横线和中文标点拆分;固定电话仍支持区号括号、分隔符和分机号。邮箱地址不属于引流信息。 - 识别规范化只作用于检测副本,不得修改真实短信发送内容。消息记录持久化是否含引流、命中类型、原文位置、规则版本和检测时间;历史未检测数据保留为“未检测”,不得伪造为不含引流。 - 运营端短信记录提供“是否含引流信息”筛选,支持含引流、不含引流和未检测;含引流记录使用提示色底色并高亮原文命中片段,CSV 同步导出该维度。 - 数据统计的签名发送质量明细保留原“通道 × 运营商”整体矩阵,并提供按含引流、不含引流、未检测切分的矩阵视图;整体统计必须直接反映全部提交,不得用分组平均值替代。 - 运营看板“今日签名发送统计”按签名统计全部短信,不区分是否含引流;“今日签名发送统计 - 含引流”只统计内容识别结果为含引流的消息。 - 未来可在识别结果之上增加发送、拒绝或人工审核决策,但必须作为独立迭代启用,不能在本期以隐式状态或既有报备审核逻辑提前拦截。 ## CMPP逐分片最终回执与72小时超时回执(2026-08-03) - CMPP协议状态值中的`ACCEPTD`、`UNKNOWN`不得在产品和代码中扩展解释为协议规定的“临时状态生命周期”;平台只依据明确业务规则判断是否继续等待,不能臆造协议阶段。 - Gateway接收每个客户`CMPP_SUBMIT`时必须保存该包的`Registered_Delivery`和`Sequence_Id`。长短信每个原始分片的身份独立保存,不得只保留第一片;历史未保存`Registered_Delivery`的CMPP记录按原有“请求回执”行为兼容。 - 内部仍以一条`SmsMessageRecord`聚合长短信业务终态、重投、计费和退款;对外CMPP状态报告按原始客户分片逐片建立。每片使用`SubmitGroupMessageId + 原Sequence_Id`重建平台当初返回的`SUBMIT_RESP.Msg_Id`,并以“短信记录+客户分片序号”独立幂等。 - 已取得真实分片回执时,下游对应分片必须使用该片自己的状态、原始状态码、错误码和到达时间,不得把另一片的结果复制过来;整条短信明确最终失败而部分片仍无结果时,缺失片使用整条短信的明确最终失败状态,已成功片不得改写为失败。 - `Registered_Delivery=0`的客户分片不生成CMPP状态报告;同一HTTP提交仍只生成一个消息级最终Webhook,不因供应商内部计费分片数而重复回调。 - 提交或未知状态满72小时仍无明确最终回执时,主记录转为`timeout`并写入`undelivered/EXPIRED/RECEIPT_TIMEOUT`。CMPP对每个请求状态报告的原始分片建立失败回执,HTTP建立一个明确失败Webhook;退款保持消息级一次。 - 超时状态变更与下游回执建单之间必须可恢复:仅在HTTP事件及全部应建CMPP分片投递均成功持久化后写`timeoutReceiptQueuedAt`;中途失败保留空标记,由后续定时扫描按稳定幂等键补齐,禁止出现“已转超时但永久没有下游回执”。 ## 供应商长短信整条级成功回执与网关异常中心(2026-08-06) - 通道配置增加“长短信成功回执口径”,默认值为`per_segment`(逐分片)。只有供应商明确约定长短信成功时仅返回一条、且该条代表整条短信全部分片成功,才允许人工配置为`message_level`(整条级);修改该业务口径不触发通道重连。 - `per_segment`保持既有严格规则:只收到一个成功分片时,其他未回执分片继续等待,主记录不得提前成功。`message_level`收到当前提交尝试的一条明确成功回执时,可将同次提交中尚无回执的分片标记为推断成功,并以`compensationType=supplier_message_level_receipt`保留推断依据;真实`SmsReceiptRecord`仍只保存供应商实际返回的一条回执,不伪造多条原始回执。 - 整条级成功推断只作用于当前通道、当前提交尝试和真实存在的多分片审计;已有明确失败的分片不得被成功推断覆盖。业务终态、退款、补发及对客户的CMPP/HTTP最终回执继续沿用既有幂等规则。 - 已按整条级成功形成`delivered`终态后,如果同一提交尝试又收到明确失败回执,平台保留原始回执和分片审计,但不得自动把已送达终态改成失败、重复退款或向客户推送互相矛盾的失败结果;系统按稳定异常键写入`SmsReceiptAnomaly`,重复矛盾回执累加发生次数。 - 运营菜单“Gateway提交异常”更名为“网关异常”,原路由保持兼容。页面使用“提交异常”和“回执异常”两个Tab,均查询真实后端和PostgreSQL;每个Tab必须在标题区说明其数据来源、业务含义、不能代表的结论及人工处理注意事项,避免运营人员间隔较久后误判。 - “提交异常”展示Gateway消费提交命令连续失败且没有明确供应商提交结果的死信,可在严格确认未被供应商接收后重新入队;“回执异常”展示供应商回执与平台既有终态冲突的结构化异常判定。回执异常详情必须指明真实回执保存在回执记录、原始CMPP报文在通讯交互日志,不得用异常摘要替代原始证据。 ## 运营端休眠唤醒与会话锁定恢复(2026-08-09) - 同一单页应用内完成重新登录时,前端必须把本次成功登录视为新的用户活动起点,不得沿用上一会话或电脑休眠前的内存活动时间,避免新会话登录后被立即误锁。 - 服务端返回`401/SESSION_LOCKED`或前端空闲计时触发锁定后,运营端必须同步暂停当前业务路由和全局待审核角标轮询;锁定期间不得继续请求短信记录、筛选项或`pending-audits`等受保护接口。 - 全局角标轮询的启停必须跟随当前实时锁定状态,不得只读取布局首次挂载时的`session.locked`快照。浏览器重新获得焦点时,仅在会话处于解锁状态后才允许刷新待审核数量。 - 密码解锁成功后恢复原路由并重新挂载页面,由真实后端重新读取短信记录和筛选项;不得用锁定前缓存、静态数据或localStorage伪造恢复后的业务数据。 - 锁定、解锁和跨标签会话事件必须同时更新锁屏界面、业务路由暂停状态和全局轮询状态;重复事件应保持幂等,不得形成额外登录、短信发送或其他业务副作用。 ## 签名删除预检与多通道报备汇总修正(2026-08-09) - 签名删除预检中的“未结束报备任务”只统计仍需处理的过程态任务;`approved`、`completed`、`failed`、`cancelled`、`rejected`、`abandoned`、`partial`和`partial_success`均属于已结束历史,不得作为活动关联项。 - 签名及运营商报备汇总不得因单个目标通道失败就直接变为整体“报备失败”。全部当前目标通道通过时为“报备成功”;至少一个通过但尚未全部通过时为“部分成功”;没有通过且仍有其他目标待处理时为“报备中”;只有全部当前目标通道均为`failed/rejected`时才为整体“报备失败”。 - 每个通道的失败事实、失败原因和历史报备记录必须继续保留并展示;汇总状态修正只改变整体归因,不得覆盖或删除通道级失败证据。 ## 通道组删除风险展示与历史保留(2026-08-09) - 运营端删除通道组前,必须通过真实后端和数据库统计并展示:关联正常企业应用数、组内通道数、等待供应商提交结果数。企业应用按不同`applicationId`去重;状态为`deleted`或应用记录已不存在的残留关联可继续计入后台审计快照,但不得在删除弹窗展示。 - “等待供应商提交结果”固定为该通道组下`SmsSubmitRecord.submitStatus = queued`的记录数,表示平台已选定该组但尚未收到供应商提交结果;该状态不按三个工作日自动完成,不能与最终回执超时口径混用。 - 正常应用关联、组内通道和等待提交记录均只作风险展示,不得禁用或阻止“确认删除”;弹窗不要求输入通道组名称,不要求填写删除原因,由运营查看真实影响后确认。 - 删除采用逻辑删除,将通道组状态置为`deleted`并从通道组列表及新短信选路中排除;不得删除组内通道配置、企业应用关联、发送记录、回执或审计数据,确保历史查询、回执处理及上行接入号匹配仍可追溯。 - 弹窗标题为“删除通道组:{通道组名称}”,正文依次展示上述三项真实数量,并明确:“删除后该通道组不再参与新短信发送,历史配置、发送、回执和审计数据继续保留。”操作仅保留“取消”和“确认删除”。 ## 通道、签名与模板级联删除确认(2026-08-09) - 通道、签名和模板删除原因统一为选填;未填写时仍允许删除,后端必须继续记录操作人、对象版本、幂等键、真实依赖快照和级联结果。删除仍采用逻辑删除,不物理清除历史发送、计费、审核、报备或审计数据。 - 删除签名时,若存在未删除短信模板、未删除引流信息或未结束报备任务,弹窗必须分别提供“同时删除关联的模板”“同时删除引流信息”“同时结束关联的报备任务”勾选项。发现的勾选项必须全部勾选后才允许确认;后端必须再次校验并在同一个`Serializable`事务中将关联模板和引流信息逻辑删除、将未结束报备任务置为`abandoned`,最后逻辑删除签名。 - 删除通道时,若存在未结束报备任务,必须提供“同时结束关联的报备任务”勾选项;勾选后在同一事务中将任务置为`abandoned`并逻辑删除通道。已有活动通道组引用、直接路由规则或活动网关连接仍属于不能由该勾选项解决的硬依赖,必须先处理后再删除。 - 运营端可以看到未结束报备任务的真实ID和状态;客户端也允许勾选“同时结束关联的报备任务”,但客户端专用预检响应和页面不得展示任务ID、状态、通道或其他内部详情,只展示统一说明:“发现关联的未结束报备任务。勾选后将全部置为‘放弃报备’,历史任务和报备记录继续保留。” - 每一条被放弃的报备任务必须写`ChannelSignatureReportRecord`,保留变更前状态、`abandoned`变更后状态、操作人、原因和`deletion_governance`来源;级联删除的模板和引流信息必须留下子对象审计记录。删除通道并结束任务后,受影响的未删除签名必须在同一事务内按剩余有效通道重算报备汇总。 - 删除签名时,关联模板若仍存在真实未结束发送或批量任务,不得仅靠“同时删除关联的模板”绕过签名和报备安全约束;该签名级联阻断与单独删除模板的规则分开。 - 单独删除模板只阻止之后新建发送任务,不阻断也不改写已创建的`SmsSendTask`、`SmsBatchTask`和`SmsMessageRecord`。模板保持逻辑删除,历史`templateId`关联、短信内容、分类、计费与审核快照继续保留。 - 已接受的定时任务到点时必须使用创建时持久化的内容快照继续处理,不得因模板后续逻辑删除而失败;但仍必须重新校验企业、应用及签名当前可用性,防止绕过停用和签名安全控制。 ## 发送质量矩阵与成功率色阶统一(2026-08-09) - 数据统计“签名通道发送质量”的明细抽屉中,“按引流切分”固定按每个通道三行展示,顺序为“含引流、 不含引流、未检测”;运营商固定为三列,顺序为“移动、联通、电信”。即使某个组合没有真实提交,也必须保留该行列并明确显示`0`,不得省略、错位或用空白代替。 - 上述固定行列只改变真实统计结果的展示,不改变后端签名、通道、运营商、引流状态、提交量、送达结果和到达时间口径;整体统计页签继续展示全部真实提交。 - 签名发送质量列表、明细抽屉、短信通道管理列表和通道报备详情中的成功率数字统一使用六档颜色:`0`为红色,`>0且<=25`为橙色,`>25且<=50`为黄色,`>50且<=75`为蓝色,`>75且<96`为绿色,`>=96`为深绿色。小数成功率必须按该连续边界归档。 - 短信通道管理列表及通道报备详情中的提交失败、回执未知和送达失败比例与数量统一使用黑灰色,不因数值高低显示为红、橙或其他告警色;该展示规则不改变真实失败状态及统计值。 ## 报表筛选结果全量汇总(2026-08-09) - 对账单、利润报表和发送质量报表在每次搜索后都必须展示当前筛选条件匹配的全部结果汇总,不得只对当前分页明细在前端求和。汇总、总数、分页和CSV导出必须复用同一套日期、企业、应用、通道及统计维度筛选口径。 - 三类报表均汇总提交、发送、未知、成功和失败条数;利润报表另汇总净消费、返还、成本和利润金额。综合成功率必须按合计成功量/合计发送量重算,综合利润率必须按合计利润/合计净消费重算,不得对每行百分比求和或简单平均;分母为0时显示0%。 - 平均到达时长不属于可加总数据,本汇总区不对各日、各维度均值再求和;明细表仍保留每组的真实P95截尾平均到达时长。 ## 新建企业省份与地市字典(2026-08-09) - 运营端新建和编辑企业的省份、地市必须使用真实后端字典及级联关系,不得在页面写死少量省市选项。本版字典从PostgreSQL `PhoneSegment.province/city`中查询去重后的真实归属关系,与平台手机号段库保持一致。 - 新增`GET /api/admin/dictionaries/administrative-regions`返回省份及其地市数组;前端选中省份后只展示该省真实地市,切换省份必须清空原地市。字典请求失败时必须明确报错,不得回退到Mock或静态列表。 - 编辑历史企业时,若原省市值与当前号段字典格式不同或暂无对应项,页面仍必须保留并显示原值,不得因加载字典而静默清空已存档案。 ## 运营端菜单与查询控件细节修正(2026-08-09) - “风控规则”菜单归入“安全控制”业务域,并同步更新页面面包屑;路由、真实规则接口和数据库数据不变。 - 发送监控页面的通道运营商必须显示中文名称,至少统一映射移动、联通、电信、三网和未识别;无法识别的新增值保留后端原值,避免隐藏真实数据。 - “待生成报备批次”的两个页签标题固定为“待生成资料”和“已生成批次”,不在标题后展示括号及总数;真实后端分页总数仍用于分页,不改变待生成池和批次查询。 - 短信记录的通道筛选使用平台通用可搜索下拉控件,选项来自真实通道接口,显示通道名称及已有通道编码,并向短信记录及导出接口传递精确`channelId`;不得使用静态列表、Mock或浏览器本地数据替代。 ## CMPP客户连接请求诊断日志(2026-08-09) - 每一次客户向平台发起的真实CMPP CONNECT尝试,无论账号是否存在、认证是否成功、IP白名单是否命中或应用是否启用,都必须同步写入`OperationLog`,动作使用`cmpp_connection.connect_requested`,并将TCP真实远端IP写入`ipAddress`;未知或恶意账号也必须以请求账号作为资源标识保留,不得因无法关联企业而丢弃。 - 连接请求详情保存客户实际发送或由Gateway从报文解析的诊断参数,包括远端IP、`Source_Addr`账号、`AuthenticatorSource`、时间戳、协议版本及原始版本值,并保存认证结果、应用ID和失败原因。标准CMPP CONNECT不传输明文密码,页面必须明确说明这一事实,不得把平台配置的密码或密钥伪造成客户请求密码;仅兼容调用真实携带`password`字段时原样保存和展示该字段。 - 上述连接请求日志必须在认证响应返回前持久化,日志写入失败时不得把未经审计的连接当作认证成功。系统与操作日志列表继续直接显示`ipAddress`,并为`cmpp_connection.connect_requested`提供“查看详情”按钮,展示上述结构化参数。 - 供应商通道的既有连接操作日志仍保留;客户入站连接使用`cmpp_downstream_connection`资源区分方向。本功能不改变CMPP认证算法、IP白名单、最大连接数或客户连接状态。 ## 暂缓需求:通道支持运营商多选(2026-08-09) - 本需求当前只记录、不实施,不改变现行`SmsChannel.carrier`单值模型、通道创建/编辑交互、通道组校验、发送选路、报备或生产数据;后续重新启动时必须另行完成影响评估、生产数据迁移方案和兼容发布计划。 - 目标交互为取消“移动、联通、电信、三网”四选一,将通道能力改为“移动、联通、电信”三个复选项,至少选择一个;同时勾选三个运营商等价于现行“三网”,允许只勾选其中两个运营商。 - 该变化只作用于通道本体的运营商能力集合。`SmsChannelGroup.carrier`、`SmsChannelGroupItem.carrier`、`ChannelRouteRule.carrier`及短信号码实际运营商仍保持移动/联通/电信单值;一个通道只有在能力集合包含对应运营商时,才允许加入该运营商通道组并参与选路。 - 同一通道勾选多个运营商时继续共用一个通道单价,不增加分运营商单价;如未来出现分运营商计价需求,必须另立需求并升级为通道运营商明细模型,不能在本需求中隐式扩展。 - 完整、理想的报备模型为“签名 × 通道 × 运营商”,用于分别记录同一通道在不同运营商下的报备状态;该模型本期明确暂不设计、不实施,现有“签名 × 通道”报备任务及历史记录保持不变。未来启动运营商多选实施前,必须再次确认是否同步升级报备粒度,不得把一个运营商的报备结果无依据地复制为其他运营商结果。 - 未来生产数据迁移原则为:`mobile→[mobile]`、`unicom→[unicom]`、`telecom→[telecom]`、`all→[mobile,unicom,telecom]`,已删除通道也要保留并迁移历史能力;不得根据当前通道组关联、通道名称或近期流量自动缩减旧`all`通道的能力范围。 - 未来取消某个已勾选运营商时,如果该通道仍被对应运营商的活动通道组引用,后端必须返回真实影响并阻止保存,不得自动删除通道组成员、路由、报备任务或历史发送记录;新增运营商能力也不得自动加入通道组或自动视为报备通过。 - 未来迁移必须采用向前兼容的分阶段发布:先增加新能力集合、回填并让后端兼容读取,再开放多选写入。出现两个运营商组合后,旧单值代码无法无损解释该数据,回滚下限必须是已经支持新集合的兼容版本,不能直接回滚到仅识别`mobile/unicom/telecom/all`的旧版本。 ## 通道组按通道筛选(2026-08-09) - 运营端“短信通道组管理”在通道组名称条件之外增加“通道”筛选。选定一个通道后,只展示成员配置中真实包含该`channelId`的未删除通道组;与通道组名称同时输入时按两个条件取交集。 - 通道选项必须来自真实通道API,不使用静态列表、Mock或localStorage。页面首次加载时通道组与通道两个独立请求并行执行;选项同时展示通道名称和编码,已删除通道明确标记“已删除”。未加入任何通道组的真实通道仍可选择,选中后结果为零而不得隐藏该选项。 - 通道选择控件必须为通用下拉可搜索控件,支持按通道名称或编码搜索;“全部通道”表示不按通道限制,点击“重置”必须同时清空通道组名称和通道条件并回到第一页。