Files
lislgosms/docs/channel-sensitive-words-plan-20260910.md
T
hectorzhao d13ca0713a
CSS quality / css-quality (push) Has been cancelled
docs: record channel sensitive words test deployment
2026-09-10 16:11:13 +08:00

97 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 通道敏感词需求评估与实施方案
日期:2026-09-10。状态:已实现、本地提交并部署测试环境;应用8e4bc5a,验收与未执行项见测试进度。用户要求在敏感词页面新增“通道敏感词”Tab,按通道配置,短信命中时不走该通道。本方案补充[风控设计](phase-6-risk-review-plan.md)和[发送链路设计](phase-4-send-pipeline-redesign.md),不替代平台全局敏感词或[引流门禁](drainage-send-gating-plan-20260910.md)。
用户已明确基于性能考虑,第一版不做入队后的通道敏感词复核。以下方案已按“仅在选路时过滤”修订,替代初稿的逐片复核、发送授权锁及配置变化触发重选设计;既有引流门禁保持原有行为。
## 1. 结论与当前证据
可实现,属于中等规模的跨前后端、数据库和发送链路改造,不能仅增加页面筛选。核心是通道候选排除规则,不是新增全局拒绝词库。
- 当前main为8694782,应用实现提交0c3f820;实际远端main为6d63eb5452ffc7c802960d044bf598cc8646564d。已有metrics、发布工具、治理/网络/HTTP评估文档修改须保护。
- `src/apps/admin/AdminSensitiveWordsPage.tsx`仅有单列表和新增/删除,通过`/api/admin/dictionaries/sensitive-words`访问真实后端;尚无通道字段或Tab。当前页面无编辑/启停按钮,不能把后端状态接口当作已有完整页面能力。
- Prisma `SensitiveWord`仅有word、level、status等字段,word全局唯一。`risk-review.service.ts/evaluateContent`对active词使用原文`content.includes(word)`;命中统一block,当前不是按低/中/高等级选择不同动作。此事实不代表原级别设计已经正确落地,本轮不顺带改动其语义。
- `send-gateway-submit.service.ts`分别实现微批和普通选路,均先满足企业应用通道组、运营商、签名及引流资格,再调用`selectChannelCandidate`;普通路径已有排除通道集合。必须覆盖两条路径及其降级/重选调用者。
- Gateway `internal/upstream/submit.go``drainage_guard.go`已有引流资格复核,API入口为`drainage-submit-guard.controller.ts`。本需求不扩展该复核,不新增通道词Gateway请求或拒绝码;通道词排除在创建提交意图之前完成。
- 以上是本轮源码核验;未连接目标环境API/数据库验证通道敏感词功能,未启动发送、创建规则或发送短信。历史测试部署不能证明新需求已经实现。
## 2. 业务规则(建议第一版)
1. 现有列表放在“平台敏感词”Tab,保留原搜索、级别、状态和操作语义;新增“通道敏感词”Tab。两份词库独立,禁止把通道词写入全局SensitiveWord,否则会误拦其他通道。
2. 一条通道词绑定一个真实通道。A配置“贷款”、B未配置时,包含“贷款”的短信排除A,B仍需满足原有全部发送资格;剩余候选继续按原优先级、地域、权重等规则选取。不是无条件改走B,也不能越过应用绑定的通道组找其他通道。
3. 同一词可在多个通道独立配置;单通道命中任意一个启用词即排除该通道。三网通道按整个channelId生效,对移动/联通/电信都适用;本需求未要求运营商细分,不增加该配置维度。
4. 平台敏感词仍优先执行原有全局拦截。通道词命中不进入人工审核,不影响其他消息、企业配置、余额规则和报备状态;人工审核通过不豁免通道词过滤。
5. 第一版建议沿用现有敏感词的原文连续包含匹配,区分英文大小写,不支持正则、通配符、分词或自动删除间隔字符。输入词去首尾空白,禁止空词;中间字符原样保留。NFKC、忽略大小写或抗干扰匹配属于可选增强,不能直接沿用引流清洗规则而扩大拦截范围。用户已授权执行本修订方案,第一版按此规则实施。
6. 判断完整最终短信内容(含签名、变量替换后的文本、长短信重组内容),不按单片分别匹配,避免词跨片绕过。不改原文、编码、分片或计费长度。
7. 无配置/全部停用的通道不受该新增规则影响。以本次选路读取的规则快照为准:配置更新影响之后开始读取规则的选路,当前微批已经读取的快照可继续使用。已完成选路并进入提交队列的消息不再复核,即使尚未实际发出,也不因后来新增/编辑/启停/删除词而取消或换通道;已发和历史终态不重新处理。仅进入入口队列、尚未选路的消息仍在选路时检查,不能把“已入队”一概当成免检查。
8. 只有候选被本规则全部排除时,明确失败原因为“可用通道均命中通道敏感词”。原先就无有效通道时仍保留原原因,不能把离线/未报备失败归为敏感词。
9. 无可用通道时不发出,按既有路由失败处理记录、任务进度和费用预留释放;CMPP沿用原请求回执语义生成适用失败回执,非CMPP不新增拒绝回执推送。建议内部原因码`CHANNEL_SENSITIVE_WORD_NO_ROUTE`,客户协议短码独立定义并验证长度,不冒充供应商回执。
## 3. 页面与API
通道Tab独立保持通道、敏感词、启用状态三个搜索条件,查询/重置及服务端分页(默认25)。切换Tab不串筛选或请求结果;通道下拉可搜索,用真实通道ID。列表列为通道名称、敏感词、状态、备注、更新时间、操作;未知/停用通道保留历史名称及状态说明。
新增/编辑表单:通道必选、敏感词必填(建议1~200字符)、备注可选(最多500字符)、启用状态;支持新增、编辑、启用/停用、删除,第一版一次配置一个通道,不扩展导入/导出或批量多通道覆盖。删除为软删除并二次确认。停用或删除后不参与后续匹配,但历史命中快照保留。
复用公共Tab/Select/Table/Pagination/Modal;创建和编辑弹窗只能显式关闭,保留dirty提示;保存失败在弹窗内显示,不能假成功关闭。覆盖加载、空数据、失败、权限不足、停用通道、刷新和跨路由;三尺寸1600×1000、1366×768、390×844。若实施涉及CSS,先完整阅读CSS规范,页面样式归所有者,不扩大全局样式。
建议新增管理API,保持旧接口兼容:
| 接口 | 作用 |
|---|---|
| GET /api/admin/dictionaries/channel-sensitive-words | channelId/keyword/status/page/pageSize查询,返回items/total |
| POST 同路径 | 创建单条规则 |
| PATCH 同路径/:id | 修改词、通道、备注或状态,带期望version |
| DELETE 同路径/:id | 软删除,带期望version |
后端必须使用可运行时验证的DTO,不只依赖TypeScript接口。校验通道存在、状态枚举、非空词、长度、重复和版本冲突;分页上限100。沿用运营端敏感词管理入口权限,并核对全局认证/角色/权限链,客户端和无管理权限管理员不得写入,不能只隐藏按钮。配置属于平台通道,不接受客户端自报tenantId扩大访问;消息处理中的企业/应用/通道资格仍从真实记录取得。操作人从会话取,创建/修改/启停/删除记录变更前后及审计ID,不信任body中的operatorId。
## 4. 数据模型与兼容
建议新增`ChannelSensitiveWord`id、channelId外键、word、status(active/inactive/deleted)、remark、version、createdAt/updatedAt、createdBy/updatedBy。使用(channelId,word)唯一约束;删除记录重加通过受审计的恢复/更新实现,不因软删除绕过唯一约束生成含糊重复。加(channelId,status)索引;通道删除须遵守既有生命周期,不级联抹掉审计快照。
新增追加式`SmsChannelSensitiveDecision`保存messageRecordId、routeAttemptId、contentHash、使用的规则ID/version快照、候选/排除通道及命中词、判定时间、选中通道或失败原因;只记录选路阶段,不建final阶段记录。微批批量落库,同一次选路用唯一routeAttemptId保证重复持久化幂等。短信详情仅增加运营端“通道筛选原因”;成功走B也能看出A为何被排除,不依赖原规则仍存在。分页/详情限制快照规模:保存命中总数和有上限的样例,完整排除channelId集合参与算法不得截断。客户端不得泄露供应商通道词库及路由细节,仅给适用的业务失败原因。
配置变更与审计同事务,version乐观锁防止相互覆盖;数据库异常返回失败。新增表初始为空,不迁移全局敏感词、不改旧规则、不回填历史消息、不复制词到所有通道。应用回退会失去新排除规则,需评估是否允许继续发送;回退不删除决策或自动重投短信。
## 5. 仅选路过滤与性能边界
流程:原有全局风控 → 企业应用/运营商/地域/在线状态/签名/引流资格候选 → 按本次规则快照排除命中通道 → 原选路算法 → 创建提交意图 → 沿用既有发送流程。本需求不在Gateway或实际写供应商前增加通道词检查。
- 建立统一通道词评估服务,普通选路、微批及原有重选/全国通道降级共用;排除集合与原excludeChannelIds取并集。不能只检查最终选中的一个通道后直接终止,也不能仅在客户端入口检查。
- 微批按涉及channelId集合一次取词,作为本批选路快照;同内容和同规则快照复用匹配结果,决策批量持久化,不能每个号码×每个词单独查库。普通选路一次读取其候选通道词。不增加逐片数据库查询、Gateway HTTP请求或通道配置与物理发送之间的锁。第一版不引入跨批长期缓存,不承诺任意词库规模下固定TPS;按实际批大小和词库规模验证SQL次数与吞吐,必要时再优化匹配算法。
- 配置写入仍保留管理端乐观锁和审计事务,防止多人编辑相互覆盖;它不参与发送授权,也不阻塞已取得规则快照的选路。无需新增按channelId的发送共享/独占锁或配置变更触发器。
- 已选A后新增A敏感词:消息继续按原选路结果处理,不触发取消、扫描队列、逐片检查或自动重选。当前批处理期间发生变更,也不重跑本批匹配。这是用户接受的第一版生效边界,不列作绕过缺陷。
- 若现有业务因原有原因进入新的选路尝试(例如原有降级/允许的重试),该次选路使用新读取的快照并执行过滤;如果只是消费同一已确定通道的提交意图,不重复检查。保持原重试资格、次数和部分已发保护,不新增因配置变化产生的发送/补发/重新入队。
- 原有绕过业务选路的直连诊断路径不新增通道词Gateway门禁;实施时列明此覆盖边界,不能宣称所有物理提交都受新规则复核。
- 选路读取规则失败或超时:不得当空词库放行,也不能伪装业务命中;走现有技术异常有限重试/死信与告警。选路命中导致排除不创建对应通道的发送尝试,不计为供应商发送失败。
- 费用幂等释放、终态/任务进度、CMPP适用失败回执和投递意图必须可恢复;当前drainageReceiptPending仅服务引流拒绝,不可不加审查复用于所有原因。实施需为新原因扩展通用恢复标记或独立持久标记、补已有回执缺失投递的恢复测试。终态回执只生成一次,中途排除A后B成功不得提前给客户失败回执。
## 6. 范围、验证成本与实施顺序
范围为一页及其API/types、词库/选路决策迁移、统一评估服务、普通/微批选路及原有重选调用者、无路由失败恢复、运营端详情和相关测试文档。移除通道词最终Gateway门禁、逐片复核、配置与发送并发锁,以及由最终拒绝触发的结果协议/重选改造。没有新增报备、审核、客户自助词库、批量文件导入、通道组级词、运营商细分或正则编辑器;这些须另提需求。
建议先实现配置/迁移及隔离数据库用例,再实现选路过滤/无路由失败处理,最后接UI和真实页面回归。用户已选择仅选路检查,按该边界即可作为第一版完整交付。验证重点为普通/微批选路一致性、规则快照生效边界、批量性能、决策落库和全候选排除时的回执/费用幂等;未做环境及数据量基准,不给固定工时/TPS承诺。
实施验收按[系统用例](system-functional-test-cases.md)TC-CHANNEL-WORD-0112的修订版本执行:API/前端定向和相关全量、类型、production构建及质量门禁;真实PostgreSQL/Redis及生产构建浏览器验收。本方案无Gateway代码变更,若实际改动范围不涉及Go,不为本需求新增Go改造或强制重跑Go门禁。mock仅隔离测试,不能证明供应商零Submit或客户收到回执。实际发送、客户配置写入、提交/推送/两环境部署均另按明确授权,方案和用例存在不构成执行授权。
## 7. 实施前确认建议
已明确需求是独立Tab、按通道配置、命中排除通道,并且第一版仅选路检查,不做入队后复核。建议第一版采用原文连续包含匹配、单channelId覆盖三网、全部候选排除时失败而非待人工审核。若用户要求抗干扰匹配、按运营商区分或无通道时等待恢复,应先修改这些规则。用户随后已授权按本方案实施、提交代码并部署测试环境;未授权推送或预生产部署。网络绕过任务的未完成状态不因本需求实施改变。
## 8. 2026-09-10 实施与验收
已实现独立ChannelSensitiveWord管理API与Tab,运行时校验、平台管理员权限复核、服务端分页、版本乐观锁、软删除/恢复及同事务审计。普通选路和微批共用ChannelWordSnapshot;每批一次读取候选通道active词、按完整原文复用命中结果、一次批量写决策,再使用原通道选择算法。未改Gateway、逐片授权或既有引流最终门禁。
实际数据字段:SmsChannelSensitiveDecision的snapshot JSON保存contentHash、readAt、候选/排除ID、命中总数及每通道最多20个词样例(ID/word/version/name),routeAttemptId唯一防止同次重复落库。运营端详情返回最近10次选路记录,客户端白名单映射不返回本字段。词库为空也记录选路快照;不承诺无限词库规模的固定TPS。
全候选排除内部码CHANNEL_SENSITIVE_WORD_NO_ROUTE,适用CMPP平台回执码CSW3字符),状态REJECTD/undelivered,沿用原Registered_Delivery、长短信和去重规则。新增channelWordFinalizationPending默认false和部分索引,在失败状态持久化时置true;复用已有10秒恢复扫描,保证费用预留释放、任务进度、CMPP适用回执/意图完成后才清除。非CMPP仅恢复释放/进度,不新增拒绝回执推送。迁移新增两表和一列,不转换既有业务配置;回退应用不删数据或重投,回退后新通道词规则不再生效。
本地证据:API全量69套745项通过,后追加非CMPP恢复测试后对应发送链138项通过;前端28套139项通过。API类型/构建、前端production构建、lint(既有28警告,无错误)、格式、CSS、依赖安全、bundle门禁通过。新增tools/testing/verify-channel-sensitive-words.mjs在隔离本地PostgreSQL克隆中验证迁移、真实管理服务并发、普通/100条微批选路、失败SQL耐久标记、原文不变和客户数据隔离;微批通道词SQL读取1次、决策写1次,样本390~441ms包含既有引流处理,不是物理发送TPS。
生产构建+真实Nest API+隔离PostgreSQL/Redis的Edge浏览器通过三尺寸新增/编辑/启停/软删除、Tab筛选保持、显式关闭、刷新/跨路由及历史选路原因;pageerror=0。浏览器鉴权使用真实Redis7隔离前缀,未启用短信Worker或Gateway传输;fixture的在线状态仅用于隔离路由验证,不作为真实供应商连接证据。实际短信发送、供应商零Submit、客户回执ACK、费用流水闭环及实际吞吐未执行,不以隔离结果替代。
发布收尾:测试应用8e4bc5a已完成标准部署和真实页面只读验收,详见[发布报告](release-20260910-test-channel-sensitive-words.md)。未推送、未部署预生产,未保存线上通道词或发送短信。