---
schema: "hl-changelog/v2"
ticket: "5689"
title: "派单通知模板加载失败修复（短链业务不匹配降级 + 全程行结束日口径统一 + 拆箱 NPE）"
consumer: "admin"
author: "wx"
change_type: "修复"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "后端完成：PR #5692+#5694 已合并 dev-v3（089d1a7e0）并部署 TEST（hl-fleet-service 双实例 UP）。根因两层：①冻结链/预览链对全程行（service_date NULL）结束日口径不一致（startDate vs endDate）→ 短链 tripEndDate 漂移 → matchesBusiness 不匹配抛 605308 冒泡；②确认执行中间态（ASSIGNED+dispatchPlan 未冻结）ItineraryTokenService 三元 long?Long 拆箱 NPE 500。修复：预览链 605308/身份未就绪统一降级「行程链接暂不可用，请联系车务确认」不再 500 阻塞整条模板渲染；SnapshotFactory endDateOf 口径统一；三元装箱 null 安全。网关验证 26-8778（实证单）6/6：render 200 正文完整、render-batch item success=true。"
updated_at: "2026-08-08"
base: "dev-v3"
generated: "2026-08-08T12:40:00+08:00"
---

# 派单通知模板加载失败修复（#5689）

## 背景

派单页消息预览区「通知模板加载失败，请重试」+「派车前先选择并成功渲染通知模板」（实证 26-8778）。

## 根因（网关 + 库表 + 日志定位）

1. **短链业务不匹配 605308**：`MessageTemplateRenderService`（预览链）对全程行（service_date NULL）结束日取 `endDate`（#5562 口径），而 `AssignmentHoldNotificationSnapshotFactory`（冻结链）取 `startDate`——同一派车组两条链算出不同 tripEndDate → `createOrGet` 命中已存在短链但 `matchesBusiness(tripEndDate)` 不匹配 → `requireSameBusiness` 抛 605308 冒泡 → 渲染接口失败。
2. **拆箱 NPE 500**：确认执行后派车行处于 ASSIGNED + dispatchPlan 未冻结（finalized=0、generation=null）中间态，`ItineraryTokenService` 三元 `holdPendingIdentity ? long常量 : Long字段` 被强制按 long 求值 → false 分支对 null 拆箱 → NPE 500（在既有 fail-closed 检查之前）。
3. render-batch 单项失败被容错吞掉（success=false）→ 前端只看到「加载失败」更隐蔽。

## 修复（PR #5692 + #5694）

1. **预览容错**：`resolveItineraryShortLink` 捕获 BusinessException 605308 + IllegalStateException 身份未就绪（#5675 白名单）→ 统一降级为「行程链接暂不可用，请联系车务确认」，不再 500 阻塞整条模板渲染；短链配置/落库等非业务异常仍 fail-fast（#5461 不变）。通知发送链（SnapshotFactory/EffectService）不走本方法，保持 fail-closed。
2. **口径统一**：`AssignmentHoldNotificationSnapshotFactory` 新增 `endDateOf`（serviceDate NULL → endDate），冻结链与预览链 tripEndDate 一致，根除短链日期漂移。
3. **NPE 修复**：`ItineraryTokenService` 三元改 `Long.valueOf(PENDING_DISPATCH_PLAN_GENERATION)`，null 不拆箱 → 正常走「派车组身份不完整或已失效」fail-closed → 预览降级。

## 行为变化

- 模板渲染接口不再因短链中间态/业务不匹配而 500；预览区模板正常加载渲染，行程链接不可用时显示人工介入文案（而非加载失败）。
- 派车组确认执行后（dispatchPlan 未冻结窗口）预览行程链接显示「行程链接暂不可用，请联系车务确认」，dispatchPlan 冻结后自动恢复可点链接。
- 错误码 605308/600801/600800 口径不变；非业务异常（如短链配置缺失）仍按原样上抛。

## 前端动作

- 预览区「通知模板加载失败，请重试」为后端 500 的兜底文案；本次修复后正常场景不再出现。若仍出现，请按响应 code/message 反馈后端（600800 模板变量缺失 / 600801 模板不存在 / 605308 短链业务不匹配）。
- 无需字段适配；渲染正文中的行程链接可能为「行程链接暂不可用，请联系车务确认」占位文案（属正常业务态）。

## 验证证据

- 定向 64/64（render 605308 降级、非业务异常上抛、全程行冻结 endDate 口径、ASSIGNED 中间态不 NPE、预览降级）；spotless:check 通过。
- 全量 verify 3287+（另一会话并行占用 3306 测试容器导致 MySQL8033 固定端口类竞争失败——基线脆弱项，主工作区干净基线同失败已证；错峰重跑补录）。
- 网关实测（TEST，26-8778 实证单）6/6：单渲染 200 + 正文完整（呼伦旅行—订车单/请确认以下订车信息）+ itinerary.url 降级文案 + 有效期正常计算；render-batch 200 + item success=true（修复前 NPE 500 / success=false）。
- 部署：hl-fleet-service dev-v3=089d1a7e0 双实例 UP（PR #5692+#5694）。
