补齐C1正文资料交付和C2配额分类规则,完善Proposal人工任务、固定图片过滤及邮件附件展示。 修正失败终态与恢复上限,增加关闭状态的通知组件,永久保留处理历史并完善筛选分页。 同步相关页面修复、迁移、测试和项目记录。 验证:后台复用同源码clean verify结果1155通过/10条件跳过;前端262项及生产构建通过;敏感资料和提交路径检查通过。
5.9 KiB
失败提醒能力 CP1 交接
已完成的产品行为
- 内部收到明确的最终技术失败后,登记一条包含酒店、邮件标题、失败阶段、简短原因和处理记录编号的提醒。
- 同酒店、同次处理的重复失败只保留第一条提醒;后续阶段的连锁错误不会生成另一条。
- 未完成处理、正常待确认/补充/人工办理和成功结果不会登记失败提醒。
- 发送结果分别保留待发送、已受理、配置暂停、结果不确定和安全重试耗尽;不会改变或重跑原邮件。
- 请求确定没有发出时自动有限重试。已受理、发送后超时/断流、发送时进程退出等情况不能盲目重发。
- 配置修正后,内部重试入口只恢复那条提醒。已受理或结果不确定不能通过普通重试入口重发。
本次只做好独立能力。邮件流程的自动失败判断与触发尚未接线,当前服务未加载本次构建,也没有真实发送。
内部接线位置
platform.notification.service.FailureNotificationService.register是唯一内部登记入口,无HTTP Controller;调用方在原结果提交后传入FailureNotificationRequest。AutomaticProcessingOutcome.FINAL_TECHNICAL_FAILURE才入队,其余结果直接返回空。该字段是调用方提供的可信事实,本模块不从人工任务类型、页面颜色或历史错误猜测最终失败。executionIdentity必须是同一次处理的稳定身份;已有run使用其run ID。缺run的入口失败使用稳定的那次来源执行身份,后续关联补全不得换去重身份。无run到run的桥接由CP2完成。recordId使用已有source-<id>或replay-<id>。酒店名与标题由可信来源读取;原因只能选择固定安全枚举,禁止传原始异常/邮件正文/Agent回答。find(hotelId, notificationId)与retry(hotelId, notificationId)为内部查询与恢复,不新建员工页面、不开放匿名或调试写接口。- 登记最终失败和运行发送worker都拒绝原业务事务仍打开的情况,避免事务后来回滚却先发送提醒,或网络等待占用原任务事务。
CP2必须补齐原方案列明的普通工作台失败漏判、Layer6有界恢复、Booking技术失败转人工漏判,并核对已提交工作台receipt。CP1不能替代这些全链验收。
持久与并发边界
平台库新增 V39__create_failure_notification_outbox.sql,表为 platform_failure_notification_outbox;没有修改Booking PostgreSQL或既有业务表。唯一键为酒店ID与 SHA256(hotelId + 换行 + executionIdentity + 换行 + FINAL_FAILURE),不含阶段、原因或当前时间。首次内容冻结。
仓储明确注入平台 dataSource;使用平台短独立事务保存领取、发送开始及完成。状态迁移为:
READY/RETRY_WAIT → CLAIMED → SENDING → ACCEPTED
→ RETRY_WAIT / FAILED_RETRYABLE
→ PAUSED_CONFIGURATION
→ UNKNOWN
- 只有当前随机lease token且未过期的持有人能开始发送和写结果。
- CLAIMED过期恢复READY;SENDING过期转UNKNOWN,避免进程退出后重复发送。
- 累计开始发送次数永久保留。自动重试最多5次,退避从30秒开始;耗尽后内部人工恢复允许再尝试,但不重置累计次数。
- 配置暂停/安全重试耗尽可恢复同一记录;UNKNOWN/ACCEPTED不可恢复为待发送。
- 发送使用专属daemon线程,30秒轮询,不占用或替换其他模块的Spring TaskScheduler;关闭应用时中断该线程。
配置与外部协议
默认读取工作目录 .env.failure-notification;从server目录运行时可回退到父目录同名文件。其他运行目录用 notification.failure.config-file 指定绝对路径。显式路径不存在不回退到其他配置。
只解析四个已声明配置键,支持简单单/双引号,拒绝重复/未知键和命令插值,不执行shell。每次检查/发送重新读取,Secret不进入公共配置集合、toString、日志或通知表。
FAILURE_NOTIFICATION_ENABLED=false:默认关闭;无文件也关闭。WEBHOOK_EXTERNAL_TOKEN:32字符原始凭证,真实值只在私有文件中。WEBHOOK_CONFIG_ID='9998':严格使用本轮目标,不接受资料示例9999。WEBHOOK_SEND_URL:确认后的完整HTTPS URL,不接受用户名密码、query或fragment。本机HTTP只通过包内测试构造提供,无生产放宽开关。
微信端使用单次POST,JSON仅 id/content,原始Token放 x-token。连接最长5秒,总请求含响应正文最长15秒;响应最多16KiB,关闭重定向,一次性请求正文拒绝底层再次发送。只有HTTP200、整数code=0、布尔data=true才记“已受理”。200不证明微信送达;5xx、非约定响应、断流/超时一律保留不确定,不假定请求未发出。
API目前没有接收端幂等键、受理查询或回调,因此不能宣称远端恰好送达一次。真实地址、服务端部署及ID9998目标仍待真实联调确认。文档候选为 https://biz.nianxx.cn/wechat/webhookInfo/sendMessageByOut,本次未请求该地址。
验收与上线状态
已在隔离源码副本使用合成邮件标题、合成Token、H2内存数据库与本机HTTP模拟服务检查;没有复制真实私有配置、重启当前服务或运行真实邮件。
加载本次构建时会执行新增平台表迁移;当前只交付源码,当前运行数据尚未执行该迁移。配置仍关闭,CP2衔接和真实联调前不启用。关闭通知不会影响原邮件处理与人工确认。
2026-09-10 统一加载附记
CP1通知组件/V39和CP2a失败收尾/PG V21已与最新C1及历史保留共同加载当前本地后台;发送关闭,未接自动失败发现/登记,未发送或补发。当前业务数据保留。见实际运行证据。