feat: 增加模板通道拒收策略并修复运营页面

This commit is contained in:
hectorzhao
2026-09-20 15:09:57 +08:00
parent c20c2246b2
commit b24cd7c08d
38 changed files with 2485 additions and 613 deletions
@@ -2374,3 +2374,13 @@ Webhook需在当前受支持Node运行时通过真实HTTPS投递;SSRF校验后
## 2026-09-18 长短信回执终态与归属补充
最终失败(含明确回执超时)与成功必须保持消息、账务、客户通知一致。后续同次失败分片不重复选路或退款;矛盾成功/失败回执只留原始事实并生成异常,不自动改账或重发客户通知。unknown、缺分片、普通提交超时仍可接续。回执须按业务消息、手机号、逻辑通道/上游身份和唯一发送尝试共同匹配;相同Msg_Id不能跨尝试批量更新,有歧义留待匹配。设计见phase-4-send-pipeline-redesign.md第10.13节,测试见TC-RC-20260918-01~07。此次不改协议、数据库结构和线上历史数据。
## 2026-09-20 签名质量、通道能力与模板拒收指令
1. 签名质量成功率条按业务短信总提交数展示已到达、提交失败、回执失败、未收到回执四段,合计100%;灰色未知段的数量、比例仅悬停展示,零提交空轨道,不拆成多条。当前查询和新生成日报将没有明确失败回执的超时归未知;既有冻结日报保留原口径,不因查询重算。
2. 通道允许减少运营商能力;保留通道组引用,发送选路按当前能力排除不支持运营商。不自动改动客户通道组或历史报备。
3. 企业模板管理可按所属应用通道组中的通道配置固定末尾指令“拒收请回复R”的增加/删除;默认保持原文。明确模板不串用另一模板规则;无模板ID时独立匹配有效已审核模板,包括 direct_send 应用,未匹配内容不受影响。
4. “避免影响消息分片数”固定选中,接口不可关闭;增删均保持计费单位与Gateway编码分片数,否则原文发送。只处理末尾精确指令,不修改正文、标点。重试换通道从原文计算,不叠加。
5. 短信列表显示提交通道的实际内容,详情保留原始内容及改写过消息的各次提交快照;客户端保留自身原文查看能力。现有计费、回执和报表单位不变。
6. 设计及兼容边界见 [模板拒收策略方案](template-optout-policy-design-20260920.md)。本轮授权本地修改和提交,不推送或部署。
+5
View File
@@ -363,3 +363,8 @@ a350aca测试环境长短信验收发现两条消息首尝试分别仅写出2/4
- 分片写入限制messageRecordId、submitRecordId/明确submitId及channelId;不得仅凭messageRecordId+gatewayMessageId批量跨尝试更新。提交分片记录同样在明确submitId存在时优先精确匹配,避免OR条件被另一尝试同Msg_Id干扰。
- API和协议不变、鉴权/租户规则不变、无新权限。冲突/未确认回执继续走现有Inbox恢复与人工排查。发布回退只涉及应用;不自动重放已完成事件或历史退款。
- 验收:真实PG验证顺序/并发失败分片仅一次终态选路,矛盾回执账务/通知/分片均不逆转,跨通道与同通道碰撞拒绝或准确关联,同供应商跨连接、早到回执、unknown转成功、旧尝试迟到及工作故障恢复。隔离固定输入比较选路次数;不能将隔离开销降低推算为线上CPU降幅。
## 2026-09-20 模板拒收指令补充
参见 [模板拒收指令策略](template-optout-policy-design-20260920.md)。发送链在选路候选阶段按模板/通道生成内容快照,敏感词按各候选真实内容评估;每次尝试从不可变原文开始。Submit、消息内容与Outbox同事务保存,Gateway授权校验使用对应Submit的内容快照。保留既有计费单位、原收尾状态机及Outbox稳定提交身份,配置变化不改写已生成命令。未配置模板规则时正文保持原样;明确模板ID不串用其他模板规则。
+22
View File
@@ -5693,3 +5693,25 @@ TC-SQA-0114:真实隔离PG覆盖核心日期/日报/长短信/事务/分页
| TC-RC-20260918-07 | 同供应商跨连接唯一匹配、歧义拒绝、身份变更、手机号不符、跨租户关系、历史submitId、候选截断和72小时超时恢复 | receipt-attempt-resolver.spec.ts12项隔离单测;历史分片补关联并齐片完成另有真实PG用例 |
既有TC-RC-20260916收尾并发与故障用例继续执行tools/testing/verify-attempt-completion.mjs(真实PG、双OS进程、事务回滚、fence、一次补发Outbox、非零扣退费、通知持久化)。本轮不启动Gateway、Redis消费者或网络通知投递,不以数据库集成代替线上完整短信链路验收;目标环境与线上CPU改善待另行授权发布后验证。
## 2026-09-20 模板拒收指令及运营界面验收
设计:[模板拒收策略](template-optout-policy-design-20260920.md)。所有写入夹具限本地隔离数据库,不能据此向测试/预生产发送短信。
| 编号 | 场景 | 预期 |
|---|---|---|
| TC-OPT-20260920-01 | 四类混合、零提交、统计不一致 | 单条四段合计100%,未知灰色且仅悬停显示数量比例;零提交空条;不一致明确提示而不编造比例 |
| TC-OPT-20260920-02 | 超时无回执及明确失败回执 | 当前质量查询分别计入未知/回执失败;历史冻结报表不重算 |
| TC-OPT-20260920-03 | 活动通道组仍引用通道,缩减运营商 | 保存成功,组引用保留,被移除运营商不能选该通道,不触发无关重连 |
| TC-OPT-20260920-04 | 策略GET/PUT、未登录/客户端、重复/非法/外应用通道 | 鉴权拒绝非法入口,所属应用范围严格校验,有效保存留审计 |
| TC-OPT-20260920-05 | 固定/变量模板、direct_send、明确其他模板、无匹配 | 指定模板准确命中,direct_send不绕过策略,其他短信原文不变 |
| TC-OPT-20260920-06 | 69→75、71→77、删除跨70字、Unicode代理对 | 同计费单位且同Gateway分片才执行;否则跳过;保护框默认选中且不可取消 |
| TC-OPT-20260920-07 | 正文出现指令、末尾重复添加、补发换通道 | 正文不删、添加不叠加、换通道从原文重新计算 |
| TC-OPT-20260920-08 | 通道敏感词与增删策略同时存在 | 按各候选真实改写内容筛选通道,保存对应内容摘要 |
| TC-OPT-20260920-09 | 单条/微批/补发、事务中断、配置变化 | 消息/Submit/Outbox一致,失败一起回滚,旧命令快照不变化,费用及分片单位不变化 |
| TC-OPT-20260920-10 | 短信列表和详情,历史空字段 | 列表真实提交内容,详情原文及尝试快照,历史空字段正常,客户端仅返回自身消息 |
| TC-OPT-20260920-11 | API失败、重试、通道组移除后失效规则 | 无假成功,输入保留,显式删除失效规则后可保存 |
| TC-OPT-20260920-12 | 1600×1000/1366×768/390×844 | 进度条不换行;模板保存、通道缩减、原文详情、刷新跨路由正常,无控制台异常 |
代码级与真实本地API/PG验收分别见 testing-progress.md;无运营商真实发送授权,因此不将Outbox构造/回滚测试称为真实短信送达验收。
@@ -0,0 +1,28 @@
# 模板拒收指令策略与运营页面修正
日期:2026-09-20。状态:本地已实施,隔离API/PG及三尺寸浏览器验收通过;未推送、未部署、未进行运营商发送。执行证据见 testing-progress.md。本方案补充发送链路设计,不替代其事务、账务、回执和 Outbox 规则。
## 业务规则与影响
- 签名质量列表使用一个四段横向条,按总提交数计算已到达、提交失败、回执失败、未收到回执的占比;未知使用灰色,数量及比例只放悬停提示。保留筛选、分页和详情。零提交显示空轨道。
- 通道缩减运营商能力允许保存,保留已有通道组引用及历史报备;选路实时按通道能力过滤,失去可用通道时沿用既有无路由失败处理,不偷偷迁移客户配置。恢复能力后原引用可继续使用。验收发现既有窄屏查询按钮遮挡与运营商选项溢出,同页CSS增加780px以下单列/换行规则;保持桌面及筛选语义不变,所有权清单更新对应已验收摘要。
- 运营端企业模板列表增加“拒收指令”配置入口。按模板及应用通道组中的通道选择“保持原文 / 末尾增加 / 末尾删除”,固定指令为 `拒收请回复R`。只删除末尾精确匹配的指令,不删除正文相似字样,不改其他文字或标点。重复增加不叠加。
- “避免影响消息分片数”固定选中,后端也不接受关闭。增加和删除都必须同时保持原计费单位和 Gateway 实际编码分片数,否则原文发送并保留跳过原因。不能以本需求修改计费、回执或报表口径。
- 仅匹配当前企业、应用下的有效已审核模板;存在明确模板ID时仅使用该模板规则,不串用其他模板。没有模板ID时按精确正文、变量模板匹配(沿用模板匹配规则和排序)。即使 direct_send 绕过模板准入,发送前仍独立识别策略模板;完全不匹配任何模板的短信保持原文。配置不赋予未审核模板发送权限。
- 每次选路基于不可变原文产生该通道的候选内容;通道敏感词继续检查实际候选内容。换通道补发重新从原文计算,不能累计增删。已持久化的 Submit/Outbox 使用当时快照,不因配置修改而重写。
## 数据与接口
- SmsTemplate 增加 optOutRules JSON(缺省空数组),每项 channelId/action;后台验证动作、重复通道、所属应用的活动通道组成员关系。仅运营端专用 GET/PUT enterprise-templates/:id/opt-out-policy;客户端模板编辑不接受此字段。配置变更留操作审计。
- SmsMessageRecord 增加 nullable originalContent;首次改变时保留输入原文,后续永久保留。content 沿用发送内容字段,在提交事务内与 Submit 和 Outbox 一致更新。
- SmsSubmitRecord 增加 nullable sentContent 及 contentPolicy JSON,记录每次尝试内容、命中模板/动作和应用或跳过原因。迁移不重写历史短信;旧记录字段为空时维持原展示。
- 发送列表展示最近一次提交内容,详情在改写过时另列原始内容;已排队未获得供应商受理不能标称送达。尝试快照用于历史通道发送内容追溯。
- 单条、微批和补发统一使用相同改写函数,所有费用及计费单位保持不变;数据库事务失败不留下单独内容改写,Outbox 重发不重新计算策略。
## 验收与兼容
验证四类加和、零数据、悬停、通道缩减/恢复/不可路由;策略越权及非法参数、变量模板和 direct_send、无匹配不影响其他短信、69→75跳过/71→77执行、删除跨分片跳过、Unicode与编码边界、多通道补发不叠加、事务失败回滚、Outbox 快照稳定、原文详情及三尺寸页面。
使用隔离 PostgreSQL、真实 API/Redis和页面验证;不连接运营商发送,不操作线上客户或通道配置。执行定向及全量回归、类型检查、构建和质量门禁。线上验证和部署独立列为未执行。本轮只提交本轮代码与文档,不推送或部署。
质量分类补充:当前查询及今后生成的日报使用互斥四类,超时无明确失败回执归未知。已发布冻结日报不重算,其历史分类保留。进度条比例均用总提交数,原接口 successRate 字段保留兼容,不改变详情其他既有口径。未改变资金流水、客户回执数量、报表计费单位;本地验证的是生成意图及费用字段、事务一致性,并未执行运营商真实发送和资金结算。每次Submit额外保存一份内容快照用于追溯,归入原提交记录的留存治理范围。
+13
View File
@@ -5186,3 +5186,16 @@ CUA本轮可用,实际后端文档三尺寸1600×1000/1366×768/390×844无页
API全量81套/880项通过并达覆盖率门禁(语句67.73%、分支52.89%、函数68.76%、行70.60%);最终小幅兼容调整后定向2套/150项及八组真实PG、十一组并发恢复复跑通过。TypeScript生产构建通过;本轮文件ESLint零错误、30条既有any警告,Prettier及diff检查通过。新匹配器单测行覆盖100%、分支89.65%。早期旧mock未提供真实关联造成14项失败,补全关系及查询形状后通过;未降低关联约束。真实PG保留pg驱动并发query弃用警告,未出现事务失败。
可重复运行:先构建api并迁移独立本机cmpp_qa_*库,设置COMPLETION_TEST_DATABASE_URL后运行tools/testing/verify-receipt-finality.mjs和verify-attempt-completion.mjs;脚本拒绝非本机或非隔离库。原始日志保留.local-data/receipt-fix-20260918/,不进Git。此轮仅调用真实后端服务/持久层,不启动Gateway、HTTP通知投递或在线发送。前端/Go未变,未重跑其验收;目标环境、Redis/Gateway完整网络闭环、线上CPU降幅未验证。不得把本地通过视作测试/预生产已修复。本地隔离数据库进程收尾关闭,数据及日志保留。
## 2026-09-20 签名质量四段条、通道运营商缩减及模板拒收策略
- 基线:本地main c20c2246b263b22a6a84c8ac10d9f320197a8fb6,实际远端main 5e4d644788b453528e513b1c14c28b120e221b64;开始暂存区为空。保留版本3.0、metrics、发布工具/部署脚本、手机号规则迁移及其他草稿,不纳入本轮提交。
- 需求/设计:first-version-development-requirements.md“2026-09-20”节、template-optout-policy-design-20260920.md、phase-4-send-pipeline-redesign.md本轮补充;用例TC-OPT-20260920-0112。
- 根因:原质量条仅绘制成功比例;通道后端在检测到被活动通道组引用的已移除运营商时直接拒绝;原发送链没有模板/通道内容改写及尝试内容快照。本轮改为互斥四段条、仅悬停未知数据;允许能力缩减并保留原路由能力过滤;模板策略独立匹配且不得改变计费及Gateway分片数,单条/微批/补发共享,原文/Submit/Outbox同事务。Gateway授权按尝试快照核对,短信详情增加原文与尝试内容,客户端只返回自身原文。
- 迁移:新增20260920090000_template_optout_policy,旧内容不重写。初次工作区数据库包含113条迁移;随后复制原已提交迁移及本轮迁移,排除未提交20260918110000_refine_mobile_drainage_prefixes,在独立cmpp_qa_optout_commit_20260920执行112条迁移全部通过,并重新执行真实API/PG和浏览器验收。
- 自动验证:API全量85套/971例通过,覆盖率语句67.95%、分支53.55%、函数68.91%、行70.72%;前端37文件/168例通过,现有覆盖率门禁88.48%/85.09%/84%/88.19%。TypeScript、API构建、前端production构建、npm run lint、format:check、stylelint/CSS治理、bundle:verify及git diff --check通过;lint保留9条既有hooks警告,production大chunk提示但包体门禁通过。Gateway go test ./...及go vet ./...通过,队列契约未改动。
- 初次覆盖率运行并行竞争资源,前端24例触发原5000ms超时;串行安排并限制maxWorkers=2后168例全通过,没有放宽时限或断言。新增通道单测首次缺少required desiredConnections导致类型失败,补齐测试夹具后971例全通过。原失败记录保留。
- 真实验收:tools/testing/verify-template-optout.mjs使用真实NestJS、PostgreSQL16及本机Redis5.0.14.1(运行库提示建议6.2+,已记录环境差异)。验证登录/客户端入口拒绝、外应用/非法策略拒绝、固定分片保护、审计持久化、活动通道组引用下缩减保存、direct_send无templateId仍应用、单条及微批快照、换通道补发恢复原文、费用字段不变、事务回滚及旧Outbox快照不变;Outbox全部pending,未启动Gateway/SMSC或发布器,未实际发短信。
- 浏览器:当前环境无Browser插件,按前端验收技能使用独立Playwright/Edge、真实API及会话,1600×1000、1366×768、390×844分别验证策略保存、固定勾选、四段条、刷新/跨路由、原文/尝试详情、通道弹窗减少运营商后保存及PG回读,无pageerror。发现并修复既有窄屏查询遮挡/弹窗运营商溢出,仅增加同页响应式规则,保持桌面布局;已查看截图核对。
- 证据:.local-data/template-optout-20260920/ 下 api-coverage-final.log、frontend-coverage-final.log、migrations-commit.log、browser-accepted.log、lint-accepted.log、format-accepted.log、api-build-accepted.log、build-accepted.log、bundle-accepted.log、go-test.log、go-vet.log及template/quality/detail/channel三尺寸截图。验证脚本不保存会话或密码,fixture.json仅含隔离业务ID。
- 边界/未执行:未操作测试或预生产配置;未推送、未部署、未执行运营商发送/实际回执推送/资金结算;冻结历史报表不重算。测试证明本地真实持久化和页面,不能冒称线上或实际送达验收。提交仅纳入本轮源文件、迁移和本轮文档增量,提交号以本轮Git提交为准。