hl-api-changelog/changelogs-v2/2026-07/01_4682_打印行程单出参增强-修改接口-管理后台.md
yaosutu 7d68720e9b docs(changelog): 打印行程单出参增强 changelog(Issue #4682,PR #4683)
notices 改自描述数组(破坏性)/ breakfast·lunch·dinner 改 Boolean(破坏性)/ 新增 days[].week / nodes[].description / nodes[].contactPerson·contactPhone(仅SCENIC)
2026-07-01 09:34:52 +08:00

22 KiB

打印行程单出参增强notices 自描述 / 景点联系人 / 周几 / 餐布尔)

接口路径GET /v3/admin/order/{id}/print-itinerary 服务hl-order-service-v3 PR#4683 | Issue#4682 | 首上线 PR#4650 | 合并至dev-v3 变更类型:破坏性变更(修改接口)


1. 接口背景

本次是对 PR #4650 首上线的打印行程单接口出团行程单·Driver Copy的出参增强,对照前端原型 printItinerary.jsx 补齐 5 项字段变更。只改出参,入参不变,零 DDL。

变更核心:

  • notices07 注意事项区块)由固定对象结构改为自描述数组,前端不再需要硬编码三档配色和图标。
  • 餐食字段breakfast / lunch / dinner由枚举字符串改为布尔值,前端直接判断是否含餐。
  • days[] 每日行程增加 week 字段(周几)。
  • days[].nodes[] 每个点位增加 description点位介绍和 contactPerson / contactPhone景点联系人,仅 SCENIC 类型有值)。

2. 变更清单

类型 字段路径 变更说明
破坏性变更 notices 类型从对象 {forbidden,warning,standard} 改为 NoticeGroupVO[] 自描述数组
破坏性变更 days[].breakfast 类型从 String 枚举改为 Booleantrue=含餐,false=不含)
破坏性变更 days[].lunch 同上
破坏性变更 days[].dinner 同上
新增字段 days[].week 周几(由 dayDate 派生;dayDate 为 null 时 week=null
新增字段 days[].nodes[].description 点位介绍(景区等节点有值,可能 null
新增字段 days[].nodes[].contactPerson 景点联系人(仅 SCENIC;其余类型或未维护时为空串
新增字段 days[].nodes[].contactPhone 景点联系电话(同上规则)

入参无变化无 DDL无新依赖


3. 接口详情

说明
方法 + 路径 GET /v3/admin/order/{id}/print-itinerary
接口名 打印行程单出团行程单·Driver Copy
功能描述 聚合订单 11 个打印区块,供前端渲染 A4 行程单并打印 / 导出 PDF
认证 需携带管理后台 JWTAuthorization: Bearer
权限 已登录的管理后台用户,无额外角色限制
幂等性 只读查询,天然幂等
限流 无特殊限流(走网关通用限流)
响应格式 application/json; charset=utf-8
包装类型 Result

4. 接口入参

4.1 路径参数

参数名 类型 必填 说明
id LongJSON 序列化为 String,防 JS 精度丢失) 订单 ID雪花 ID,前端应作为字符串透传,不要转 number

4.2 请求体

本接口无请求体GET 请求)。入参与 PR #4650 首上线版本完全相同,本次未作任何修改。


5. 出参字段

响应结构Result,code=200 时 data 为完整行程单对象。以下列出本次有变动的字段和结构包含字段完整表格,未变动字段agencyName / transports / customerOverview / feeDetail / hotels / emergencyContacts / refundNotes / handoverChecklist / collectReceipt 等)与首上线 Changelogchangelogs-v2/2026-06/30_4644_打印行程单接口-新增接口-管理后台.md保持一致,此处不重复列出。

5.1 notices07 注意事项区块)—— 结构破坏性变更

旧结构PR #4650,已废弃

{
  "notices": {
    "forbidden": ["..."],
    "warning": ["...", "..."],
    "standard": ["...", "...", "..."]
  }
}

新结构(本次 PR #4683

{
  "notices": [
    {
      "level": "FORBIDDEN",
      "title": "严禁事项 · 违者扣全部车费、永不录用",
      "color": "#DC2626",
      "icon": "🚫",
      "items": ["..."]
    }
  ]
}

NoticeGroupVO 每个元素字段:

字段名 类型 说明
level String 档位标识FORBIDDEN / WARNING / STANDARD
title String 档位标题(含完整描述,可直接渲染为区块标题)
color String 档位主题色,格式 #RRGGBB
icon String 档位图标emoji
items String[] 本档说明条目列表

三档固定配置

level title color icon items 数量
FORBIDDEN 严禁事项 · 违者扣全部车费、永不录用 #DC2626 🚫 1 条
WARNING 警示事项 · 风险由领队、师傅承担 #D97706 ⚠️ 3 条
STANDARD 流程标准 · 出团必做 #16A34A 5 条

三档内容为后端常量,前端直接渲染 title / color / icon / items 即可,不需要再硬编码配色和图标。内容如需修改需后端发版。


5.2 days[]06 每日行程)—— 新增 week,餐食改布尔

days[] 数组完整字段表:

字段名 类型 变更 说明
dayNumber Integer 无变化 天序号1-based
dayDate StringLocalDate 无变化 实际日期,格式 YYYY-MM-DD
dayTitle String 无变化 当日标题
week String 或 null 本次新增 周几(周一~周日;dayDate 为 null 时为 null
description String 或 null 无变化 当日描述
breakfast Boolean 类型变更 true=含早餐(原 HOTEL/SPECIAL/CAMP,false=不含(原 NONE/SELF/null
lunch Boolean 类型变更 同上
dinner Boolean 类型变更 同上
diningRemark String 或 null 无变化 餐饮备注
gatherPlace String 或 null 无变化 集合地点POI JSON 字符串)
dismissalPlace String 或 null 无变化 解散地点POI JSON 字符串)
dailyMileage StringBigDecimal或 null 无变化 当日里程km
nodes NodeVO[] 见 5.3 当日点位列表,按 sort_order 升序

5.3 days[].nodes[]06 子项:行程点位)—— 新增 description / contactPerson / contactPhone

nodes[] 完整字段表:

字段名 类型 变更 说明
nodeName String 无变化 点位名称(优先取 node 自定义名,回退资源名)
nodeType String 无变化 节点类型枚举值(见 §6
description String 或 null 本次新增 点位介绍(固化行程 node.description;景点节点通常有值,餐厅等可能 null
startTime String 或 null 无变化 开始时间,格式 HH:mm
timePeriod String 或 null 无变化 时段(上午 / 下午 / 全天)
contactPerson String 本次新增 景点联系人(仅 nodeType=SCENIC 时通过 resourceId 调 resource-service 取;其余节点类型及未维护景点固定为空串 "",不为 null
contactPhone String 本次新增 景点联系电话(同 contactPerson 规则)
supplierPhone String 无变化 供应商电话node.supplierPhone;无则空串—— 与 contactPhone 是两个不同字段
remark String 或 null 无变化 操作备注(作司机 / 导游话术)

contactPhone vs supplierPhone 区别:

  • contactPerson / contactPhone景点资源的联系人/电话,来自 resource-service getSpotContacts 接口,仅 SCENIC 节点。
  • supplierPhone节点层面手动录入的供应商电话,所有节点类型均有此字段无值时为空串。两者独立,前端分开展示。

6. 枚举 / 数据字典

6.1 notices.level注意事项档位—— 本次新增

枚举值 中文含义 配色 图标
FORBIDDEN 严禁事项 #DC2626 🚫
WARNING 警示事项 #D97706 ⚠️
STANDARD 流程标准 #16A34A绿

6.2 nodeType行程点位类型

枚举值 说明 contactPerson/contactPhone 是否有值
SCENIC 景区 有(若 resource-service 已维护该景点联系人)
RESTAURANT 餐厅 固定空串
ACTIVITY 活动 固定空串
SERVICE 服务项 固定空串
CUSTOM 自定义 固定空串

6.3 breakfast / lunch / dinner本次改为 Boolean

旧枚举值(已废弃) 旧含义 新 Boolean 值
HOTEL 酒店早餐 true
SPECIAL 特色餐 true
CAMP 营地餐 true
NONE 不含餐 false
SELF 自理 false
null / 空 未录入 false

6.4 其余枚举(与首上线相同)

  • directionARRIVAL到达/ DEPARTURE出发
  • transportTypeFLIGHT / TRAIN / SELF_DRIVE
  • settleScopePER_PERSON / PER_TEAM / PER_VEHICLE
  • createSource以字典 order_create_source 为准OTA / CUSTOMER / B2B / REFERRAL / MINI_PROGRAM 等)

7. 错误码

与首上线版本相同,本次无新增错误码。

错误码 HTTP 状态 message 触发场景
581007 200Result 业务码) 订单不存在 路径参数 id 对应订单不存在或已软删除
401 401 Unauthorized JWT 未携带 / 已过期
403 403 Forbidden 无访问权限

resource-service getSpotContacts Feign 失败时不报错,contactPerson / contactPhone 降级为空串 ,不影响接口整体返回。


8. 示例

8.1 典型成功

请求

GET /v3/admin/order/1914050000000001/print-itinerary
Authorization: Bearer eyJhbGci...

响应(含本次全部新增字段):

{
  "code": 200,
  "message": "成功",
  "data": {
    "agencyName": "内蒙古呼籁国际旅行社有限公司",
    "printTime": "2026-07-01T10:00:00",
    "teamNo": "26-0554",
    "productName": "游牧的森林-短途版",
    "dateRange": "2026-07-01→2026-07-04",
    "paxSummary": "2大2小",
    "driverName": "扎西",
    "driverPhone": "13700137001",
    "guideName": "巴特尔",
    "guidePhone": "13700137002",
    "leaderName": "其其格",
    "leaderPhone": "13700137003",
    "consultantName": "王骁",
    "transports": [
      {
        "direction": "ARRIVAL",
        "transportType": "FLIGHT",
        "transportNo": "CA1234",
        "carrier": "中国国航",
        "departStation": "北京首都T2",
        "arriveStation": "海拉尔东山机场",
        "departTime": "2026-07-01T08:00:00",
        "arriveTime": "2026-07-01T10:30:00",
        "selfDrivePeriod": null,
        "selfDriveEta": null,
        "pickupRequired": true,
        "pickupRemark": "T2出口举牌",
        "remark": null
      }
    ],
    "customerOverview": {
      "customerName": "吕思远",
      "customerPhone": "138****0020",
      "adultCount": 2,
      "childCount": 1,
      "youngChildCount": 1,
      "babyCount": 1,
      "tripDays": 4,
      "customerType": null,
      "createSource": "CONSULTANT",
      "customerRemark": "忌海鲜"
    },
    "feeDetail": {
      "items": [
        {"name": "成人包价", "unitPrice": "3380.00", "qty": 2, "subtotal": "6760.00"},
        {"name": "儿童包价", "unitPrice": "1980.00", "qty": 1, "subtotal": "1980.00"}
      ],
      "singleRoomSurcharge": null,
      "grandTotal": "9540.00",
      "onsiteBalance": "3540.00"
    },
    "hotels": [
      {
        "dayNumber": 1,
        "stayDate": "2026-07-01",
        "hotelName": "海拉尔海棠酒店",
        "city": "呼伦贝尔市",
        "roomCategory": "标间",
        "roomCount": 4,
        "contactPerson": "海棠-李经理",
        "contactPhone": "0470-7777777",
        "settleType": "公司付款",
        "paymentMode": "月结",
        "checkInTime": "14:00",
        "checkOutTime": "12:00"
      }
    ],
    "emergencyContacts": [
      {"contactName": "admin", "contactPhone": "13000000001", "role": "房务"}
    ],
    "days": [
      {
        "dayNumber": 1,
        "dayDate": "2026-07-01",
        "dayTitle": "海拉尔接机",
        "week": "周三",
        "description": "接机入住,傍晚自由活动",
        "breakfast": true,
        "lunch": false,
        "dinner": false,
        "diningRemark": null,
        "gatherPlace": null,
        "dismissalPlace": null,
        "dailyMileage": "200.00",
        "nodes": [
          {
            "nodeName": "中俄边境公路(卡线)",
            "nodeType": "SCENIC",
            "description": "呼伦贝尔最美自驾线路,约200公里草原与边境风光",
            "startTime": null,
            "timePeriod": null,
            "contactPerson": "卡线-王警官",
            "contactPhone": "0470-8801234",
            "supplierPhone": "",
            "remark": "提前报备边防,携带身份证"
          },
          {
            "nodeName": "午餐(特色餐)",
            "nodeType": "RESTAURANT",
            "description": null,
            "startTime": "12:00",
            "timePeriod": "上午",
            "contactPerson": "",
            "contactPhone": "",
            "supplierPhone": "0470-88880099",
            "remark": null
          }
        ]
      }
    ],
    "notices": [
      {
        "level": "FORBIDDEN",
        "title": "严禁事项 · 违者扣全部车费、永不录用",
        "color": "#DC2626",
        "icon": "🚫",
        "items": ["严禁向客人推荐自费项目、带客进购物店;违者扣除本行程全部车费,且永不录用"]
      },
      {
        "level": "WARNING",
        "title": "警示事项 · 风险由领队、师傅承担",
        "color": "#D97706",
        "icon": "⚠️",
        "items": ["送站时间不得早于出发时间前 2 小时", "景区半价票差价须由领队自行承担,不得转嫁客人", "行程如有任何变更须经书面确认后方可执行"]
      },
      {
        "level": "STANDARD",
        "title": "流程标准 · 出团必做",
        "color": "#16A34A",
        "icon": "✅",
        "items": ["出发前须核实全程住宿安排", "在出行群内发出行提示及完整大交通信息", "妥善保管签单当日归还公司", "每次入住检查设施拍照留存", "司机出发主动自我介绍"]
      }
    ],
    "refundNotes": [
      {
        "sourceName": "黑山头",
        "intro": "退费说明",
        "items": [
          {
            "title": "单项未骑马",
            "amount": "100",
            "unitLabel": "/人",
            "settleScope": "PER_PERSON",
            "settleScopeLabel": "按人",
            "remark": null,
            "effectiveFrom": null,
            "effectiveTo": null
          }
        ]
      }
    ],
    "handoverChecklist": ["出行群已建立并拉入全部出行人", "签单核对完毕", "车辆及座位已确认", "特殊需求已告知", "代收款已清点"],
    "collectReceipt": {"collectAmount": "3540.00"},
    "remark": null
  },
  "success": true
}

8.2 边界情况SCENIC 联系人降级为空串 + dayDate 为 null 时 week 为 null + 三餐均 false

模拟 resource-service Feign 失败导致 contactPerson / contactPhone 降级为空串,以及 dayDate=null 时 week=null

{
  "code": 200,
  "data": {
    "days": [
      {
        "dayNumber": 1,
        "dayDate": null,
        "dayTitle": "待确认日期",
        "week": null,
        "description": null,
        "breakfast": false,
        "lunch": false,
        "dinner": false,
        "diningRemark": null,
        "gatherPlace": null,
        "dismissalPlace": null,
        "dailyMileage": null,
        "nodes": [
          {
            "nodeName": "呼伦湖景区",
            "nodeType": "SCENIC",
            "description": "呼伦湖,中国第五大湖",
            "startTime": null,
            "timePeriod": null,
            "contactPerson": "",
            "contactPhone": "",
            "supplierPhone": "",
            "remark": null
          }
        ]
      }
    ],
    "notices": [
      {"level": "FORBIDDEN", "title": "严禁事项 · 违者扣全部车费、永不录用", "color": "#DC2626", "icon": "🚫", "items": ["严禁向客人推荐自费项目、带客进购物店;违者扣除本行程全部车费,且永不录用"]},
      {"level": "WARNING", "title": "警示事项 · 风险由领队、师傅承担", "color": "#D97706", "icon": "⚠️", "items": ["送站时间不得早于出发时间前 2 小时", "景区半价票差价须由领队自行承担,不得转嫁客人", "行程如有任何变更须经书面确认后方可执行"]},
      {"level": "STANDARD", "title": "流程标准 · 出团必做", "color": "#16A34A", "icon": "✅", "items": ["出发前须核实全程住宿安排", "在出行群内发出行提示及完整大交通信息", "妥善保管签单当日归还公司", "每次入住检查设施拍照留存", "司机出发主动自我介绍"]}
    ]
  }
}

SCENIC 节点的 contactPerson / contactPhone 在 resource-service Feign 失败时降级为空串 "" 而不是 null,前端统一判断 === "" 决定是否显示联系人行。


8.3 业务失败(订单不存在)

请求

GET /v3/admin/order/9999999999999/print-itinerary
Authorization: Bearer eyJhbGci...

响应

{
  "code": 581007,
  "message": "订单不存在",
  "data": null
}

9. 业务边界

适用场景

  • 订单任意状态均可查询(只读,不限状态)
  • 出团前定制师打印行程单,交司机 / 导游 / 领队
  • 本次增强后可直接用返回的 title / color / icon 渲染注意事项区块,无需前端硬编码配色

不适用场景

  • 本接口不替代签单 PDFGET /v3/admin/order/{id}/sign-voucher
  • 本接口不是小程序端行程查看接口,只给管理后台打印用

特殊边界

情况 行为
dayDate 为 null week 也为 null,前端按空值处理
nodeType 非 SCENIC contactPerson / contactPhone 固定为空串 ""(不是 null
SCENIC 节点 resource-service Feign 失败 contactPerson / contactPhone 降级为空串 "",接口仍正常 200 返回
原 breakfast 为 null / SELF / NONE 新字段值为 false
原 breakfast 为 HOTEL / SPECIAL / CAMP 新字段值为 true
nodes[].description 为 null 该节点无介绍文字,前端按空值处理(隐藏介绍行)
feeDetail Feign 失败 feeDetail 整体为 null,前端展示暂无与首上线一致,本次无变化

10. 修改前后对比

字段级对比

字段路径 修改前PR #4650 修改后(本次 PR #4683
notices { "forbidden": [...], "warning": [...], "standard": [...] } [{ "level": "FORBIDDEN", "title": "...", "color": "#DC2626", "icon": "🚫", "items": [...] }, ...]
days[].breakfast "HOTEL" / "SPECIAL" / "NONE" / "SELF" / null true 或 false
days[].lunch 同上 同上
days[].dinner 同上 同上
days[].week 字段不存在 "周三" 或 null
days[].nodes[].description 字段不存在 "点位介绍文字" 或 null
days[].nodes[].contactPerson 字段不存在 "景点联系人" 或 ""
days[].nodes[].contactPhone 字段不存在 "0470-12345678" 或 ""

行为级对比

维度 修改前 修改后
notices 渲染 前端硬编码三档颜色(红/橙/绿)和图标 直接使用返回的 color / icon / title,前端零硬编码
餐食展示 判断字符串枚举(=== "HOTEL" 等) 直接判断布尔(=== true
景点联系人 无此数据,前端无法展示 SCENIC 节点自动回填,可展示联系人和电话
每日行程周几 前端需自行从 dayDate 计算 后端直接返回 week,前端零计算
节点介绍 无此字段 有 description 可展示点位简介

11. 影响评估 / 回滚

破坏兼容性

字段 影响 前端必改
notices 类型从对象改为数组,旧代码 notices.forbidden 将为 undefined 是:改为遍历 notices 数组,使用各元素的 level / title / color / icon / items
breakfast / lunch / dinner 类型从 String 改为 Boolean,旧代码字符串判断全部失效 是:改为布尔判断(=== true

前端需要同步上线

是,且为破坏性变更——不改前端会出现注意事项区块渲染报错以及餐食图标逻辑失效。

回滚方案

  • 无 DDL,无数据库变更
  • 后端回滚Revert PR #4683,重新部署 hl-order-service-v3,接口恢复旧结构
  • 前端兼容过渡期:可同时判断 Array.isArray(notices)新结构vs 非数组(旧结构),两套渲染保留直到后端确认全量上线

12. 注意事项

  1. notices 破坏性变更是本次最大风险:旧代码直接访问 notices.forbidden / notices.warning / notices.standard 会报 undefined。前端上线后须全面验证注意事项区块渲染正常。

  2. 餐食字段变为布尔,不再区分细类:如需展示「酒店早餐」/ 「特色餐」等细类,本次接口不支持(只返回是/否含餐)。如有此需求请反馈,另行评估。

  3. contactPerson / contactPhone 返回空串而非 null:景点联系人未维护时为 "" 而非 null,前端判断用 === "" 不要用 === null。

  4. contactPhone 与 supplierPhone 是两个独立字段,来源不同,前端按需分别展示如「景点电话」vs「供应商电话」

  5. week 字段 null 安全dayDate 有值时 week 一定有值;dayDate 为 null 时 week 一定为 null。

  6. BigDecimal 字段仍为 StringgrandTotal / unitPrice / subtotal / dailyMileage / collectAmount / amount 均序列化为 JSON String,前端直接展示,不要 parseFloat() 转换。


13. 关联 / 联系人

链接 / 信息
Issue #4682 打印行程单出参增强
PR本次 #4683 feat(order-v3): 打印行程单出参增强
首上线 PR #4650 feat(order-v3): 打印行程单 11 区块聚合接口
首上线 Changelog changelogs-v2/2026-06/30_4644_打印行程单接口-新增接口-管理后台.md
Merge Commit 602f0e68d
Feature Commit 2fcc42882
后端负责人 yaosutu腰苏图
影响服务 hl-order-service-v3端口 8086