hl-api-changelog/changelogs/2026-05/11_feat_admin_itinerary-mileage-anomaly-warnings.md

6.2 KiB

admin/product-v2: 行程编辑详情新增里程异常 warnings 字段 + 建议前端 POI 搜索控件改进

服务: hl-product-service-v2 (端口 8083) PR: #1938 Issue: #1937 日期: 2026-05-11 影响范围: 管理端产品编辑(step2 行程编排)详情响应; POI 搜索控件(可选改进)


⚠️ 关键变化

后端在产品编辑详情响应里新增了一个 mileageWarnings: List<String> 字段(按天), 用于在运营选错 POI 时弹红旗提示。前端可在编辑页该天显示警告徽章。

同时强烈建议(可选):POI 搜索控件 UX 优化, 防止运营选错同名异地 POI。已发生事故见下文背景。


一、背景

2026-05-11 发现产品《一次走透呼伦贝尔的旷野心脏》DAY 3 daily_mileage = 475.2 km, 客户用高德地图手测同样 5 点路线 = 275.0 公里, 差 +200 km(+73%)

根因(实证)

运营在产品 step2 编辑器搜"恩和", 选了高德返回的 POI poiId=B0LALCK6P1, 该 POI 名字叫"恩和"但实际坐标在陈巴尔虎旗宝日希勒镇 (lat=49.279092, lng=119.616988), 跟真正"额尔古纳市恩和俄罗斯民族乡" (lat≈50.83) 偏南 167 km。后端调高德 API 算驾车里程, 从骑马场(50.83°) → 错位"恩和"(49.28°)单段就多算 200 km。

平台 F 维度扫描

正式服全平台扫描坐标落在 (lat 49.27-49.30, lng 119.60-119.65) 范围 → 共 6 条记录, 涉及 4 个产品:

产品 状态 命中
2053003990289129474 一次走透呼伦贝尔的旷野心脏 PUBLISHED DAY3 dismissal + DAY4 gather
2052985893312008193 呼伦贝尔5天4晚自由行 COMPLETED DAY2 dismissal + DAY3 gather
2052926308213891073 燕6天5晚私人订制 COMPLETED DAY3 gather
2052984406884225025 草原慢旅行 DRAFT DAY3 dismissal + DAY4 gather

已通过 SQL JSON_SET 修正全部 6 条 location 到 (50.828414, 119.912452), 修复后 F 扫描 = 0 行。

数据库 schema 差异

  • 正式服旧 schema: product_itinerary_day.gather_place / dismissal_place 是 JSON, 含 location.lat/lng
  • 测试服新 schema: 同字段已升级为行政区粒度(name / level / adcode / fullPath / levelName), 不嵌套坐标; 途经点搬到新表 product_route_point
  • 测试服 F 维度扫描 0 命中, 新 schema 已天然阻断此 bug 模式

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 管理端获取产品详情(含 step2 行程) GET /admin/product/item/{id} 响应新增字段 每个 days[] 元素新增 mileageWarnings: List<String>, 默认空数组

三、接口详情

1. 管理端获取产品详情 GET /admin/product/item/{id}

VO: ProductDetailRespVO.days[] -> ItineraryDayRespVO

出参新增字段

字段 类型 说明 默认
days[i].mileageWarnings List<String> 该天里程异常 warning 列表, 空数组表示正常。每条 warning 是给运营看的中文短句, 建议在编辑页该天显示红色感叹号 / 黄色徽章, 鼠标悬停展示具体内容 []

三条 warning 规则(后端自动检测)

规则 触发条件 warning 文案样例
单段直线超长 同一天内相邻两节点直线距离 > 100km DAY3 第4段 直线距离 173.6km, 跨城市级跳跃, 可能 POI 坐标错位
驾车/直线比异常 某段 driving / linear > 3.5 (正常 1.4~2.5) DAY3 第4段 驾车/直线比 4.2, 正常 1.4~2.5, 强烈疑似 POI 错位或绕远
全天里程过大 dailyMileage > 600km DAY3 全天里程 720km 异常大

不阻塞保存, 不持久化, 每次 GET 实时计算。资源服务异常时静默退化为空数组。

响应示例

{
  "code": 0,
  "data": {
    "id": "2053003990289129474",
    "name": "一次走透呼伦贝尔的旷野心脏",
    "days": [
      {
        "dayNumber": 3,
        "dailyMileage": 275.0,
        "dailyDuration": 210,
        "mileageWarnings": []
      },
      {
        "dayNumber": 4,
        "dailyMileage": null,
        "dailyDuration": null,
        "mileageWarnings": [
          "DAY4 第2段 直线距离 145.2km, 跨城市级跳跃, 可能 POI 坐标错位"
        ]
      }
    ]
  }
}

四、前端建议改造(强烈推荐, 防再发)

A. step2 编辑页展示 mileageWarnings

每天的里程展示位旁加一个图标:

  • mileageWarnings.length === 0 → 不显示 / 显示绿色 ✓
  • mileageWarnings.length > 0 → 显示红色 ⚠️, 鼠标 hover 展开 warning 列表

B. POI 搜索控件 UX 改进(根因防御)

当前组件:运营搜"恩和", 高德 API 返回多个同名 POI, 用户只看到 name 选错。

建议改造:

  1. 搜索结果项显示完整地址:name | district + township, 例如:
    • 恩和 | 呼伦贝尔市 / 陈巴尔虎旗 / 宝日希勒镇 ← 错的恩和
    • 恩和 | 呼伦贝尔市 / 额尔古纳市 / 恩和俄罗斯族民族乡 ← 对的恩和
  2. 按产品 cityCode 限定搜索范围:产品基础信息已存 city/adcode, POI 搜索时 region 参数限定本市, 同名异地 POI 自然过滤
  3. 选完 POI 后展示地图小预览:<map markers={[poi.location]}>, 让运营肉眼校核位置对不对

不强求一次做完, B.1 最便宜也最有效。


五、不需要前端做的事

  • 后端已修 6 条已发现错坐标记录(SQL JSON_SET), 前端不需要清缓存或重建索引
  • daily_mileage NULL 的天 = 等运营在后台对该产品 step2 点一次「保存」让 ItineraryAutoCalculator 走高德重算, 前端展示 NULL 即可

六、回归风险

  • ProductDetailAggregator.fillItinerary 多一次 Feign 调用资源服务取节点坐标, 已 try/catch 退化, 资源服务挂掉不影响详情主流程
  • 阈值 100km 对跨地级市出发产品会触发 warning(例如"鄂尔多斯出发去敦煌"), 这是 feature 不是 bug, 让运营核对一下确认 POI 没选错就好

七、相关 PR / Issue

  • PR #1938 后端实现
  • Issue #1937 工单
  • 上下文相关 PR: 6 条错坐标 SQL 修复在工单 #1937 评论里记录