verify / booking-verify (push) Has been cancelled
补齐C1正文资料交付和C2配额分类规则,完善Proposal人工任务、固定图片过滤及邮件附件展示。 修正失败终态与恢复上限,增加关闭状态的通知组件,永久保留处理历史并完善筛选分页。 同步相关页面修复、迁移、测试和项目记录。 验证:后台复用同源码clean verify结果1155通过/10条件跳过;前端262项及生产构建通过;敏感资料和提交路径检查通过。
69 lines
5.9 KiB
Markdown
69 lines
5.9 KiB
Markdown
# 失败提醒能力 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`;使用平台短独立事务保存领取、发送开始及完成。状态迁移为:
|
||
|
||
```text
|
||
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及历史保留共同加载当前本地后台;发送关闭,未接自动失败发现/登记,未发送或补发。当前业务数据保留。见[实际运行证据](../../../.project-docs/50-evidence/topics/2026-09-10-unified-runtime-load.md)。
|