feat(product): 行程集合地/解散地从行政区改高德 POI 精准定位 (#1570)

@mmg 前端 picker 切换 + 落库 JSON schema 升级。后端 PR #1570 已合 dev + 部署测试服 round-trip 完成 (#1563)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot 2026-04-30 16:11:27 +08:00
父节点 4c376c73dc
当前提交 85f2f9e946

查看文件

@ -0,0 +1,144 @@
# 行程编排集合地/解散地:行政区 → 高德 POI 精准定位
**类型**: 后端 FEAT(字段扩展+读路径增强) + 前端 picker 切换
**关联**: 工单 #1563 / PR #1570
**日期**: 2026-04-30
**前端处理者**: mmg
**影响范围**: 管理后台产品编辑页 Step 2「行程编排」→ DAY X 的「开始 / 结束」字段(即 `gatherPlace` / `dismissalPlace`
---
## 业务目的
集合地 / 解散地从「行政区」(如「海拉尔区」)升级为「精准 POI」(如「海拉尔东山国际机场 / 海拉尔火车站」)。前端 `gatherPlace/dismissalPlace` 选择器从行政区下拉换成**高德 POI Autocomplete**。
途经路线 (`routePoints`) 不在本次范围内(已是 lng/lat 结构)。
---
## JSON Schema 升级
### 新格式POI 精准定位,2026-04-30 起新数据按这个落库)
```jsonc
{
"name": "海拉尔东山国际机场", // POI 名称
"address": "内蒙古呼伦贝尔市海拉尔区机场路", // 详细地址
"location": { "lng": 119.825, "lat": 49.205 },// 嵌套对象,POI 精确坐标
"poiId": "B0FFGTEST1", // 高德 POI ID
"adcode": "150702", // POI 解析所属行政区编码
"cityName": "呼伦贝尔市", // POI 解析所属城市
"districtName": "海拉尔区" // POI 解析所属行政区
}
```
### 老格式行政区,2026-04-30 之前已落库的存量数据,零迁移继续工作)
```jsonc
{
"name": "海拉尔区",
"level": "district",
"adcode": "150702",
"fullPath": "内蒙古自治区/呼伦贝尔市/海拉尔区",
"levelName": "区/县"
}
```
后端**同时兼容三种格式**
- 新 POI 精准定位(含 `location`
- 老行政区无 `center`(依赖 `adcode` 反查)
- 更早期行政区带 `center: 'lng,lat'`(直接 split
---
## mmg 前端要做的事hl-ui 仓库)
### 1. 替换 picker 组件
`src/views/product/edit/Step2*.vue` 行程编排 DAY X 块里,「开始」「结束」两个 `n-select` / 行政区 picker,**替换成高德 POI Autocomplete 输入框**。
参考高德 JS API[`AMap.Autocomplete` + `AMap.PlaceSearch`](https://lbs.amap.com/api/javascript-api-v2/documentation#autocomplete);输入关键字 → 用户选择 POI → 把 POI 数据组装成上面的新 JSON 结构。
### 2. 落库 JSON 字段映射(高德 POI → 后端字段)
```js
// 高德 PlaceSearch 单个 POI 结果 → 后端 gatherPlace/dismissalPlace
function poiToPlace(poi) {
return {
name: poi.name, // POI 名称
address: poi.address || '', // 详细地址
location: {
lng: Number(poi.location.lng), // 高德 LngLat 对象
lat: Number(poi.location.lat)
},
poiId: poi.id || '',
adcode: poi.adcode || '', // POI 自带 adcode
cityName: poi.cityname || '', // 高德 cityname 全小写
districtName: poi.adname || '' // 高德 adname
};
}
```
### 3. PUT 行程接口
接口路径不变:`PUT /admin/product/item/{productId}/itinerary``days[].gatherPlace` / `days[].dismissalPlace` 用上面的新 JSON 结构。后端字段全保留(向后兼容)。
### 4. 详情回显
`GET /admin/product/item/{productId}``data.itinerary[i].gatherPlace` / `dismissalPlace`
- **新数据**:包含 `name / address / location / poiId / adcode / cityName / districtName`7 字段全在)
- **老数据**:包含 `name / level / adcode / fullPath / levelName`5 字段)
- 前端组件渲染时按字段存在性切换:有 `location.lng` 就用 POI 模式回显;否则按老行政区回显(兼容期)
---
## 后端测试服 round-trip已完成
测试服部署commit `982558f9` 已部署 hl-product-service-v2 + hl-order-service-v2 双实例。
```bash
# 1) 老格式产品 GET 详情 → PlaceItem 老字段完整回显(零回归)
GET /admin/product/item/2047268852147875842
→ day1.gatherPlace = {"name":"海拉尔区","level":"district","adcode":"150702","fullPath":"内蒙古...","levelName":"区/县"} ✅
# 2) PUT 新 POI 格式 → GET 详情回显 7 字段全在
PUT /admin/product/item/{draftId}/itinerary
body: { days:[{ gatherPlace:{name,address,location:{lng,lat},poiId,adcode,cityName,districtName} }] }
→ HTTP 200 「行程保存成功」
GET → day1.gatherPlace 7 字段完整含 location.lng=119.825/lat=49.205 ✅
# 3) 同 day 混合格式 (gather=POI, dismiss=老) 兼容
→ 两边各自字段完整回显,互不干扰 ✅
```
---
## 后端改动(参考)
13 个文件 +846 / -48,PR #1570
- `InternalProductDetailVO.PlaceItem` + `ProductDetailVO.PlaceItem` (Feign 镜像) **扩展 7 个 POI 字段 + 嵌套 Location 静态类**,旧字段保留兼容
- `MpProductRouteMapService.parsePlaceVO`:路径优先级 `location → center → adcode 反查`
- `AmapDrivingService.parseCenterToLngLat`:同上优先级
- `TeamReportService.parseCityFromSnapshot`:取城市优先 `cityName/districtName`,回退 `fullPath`
- VO 注释批量升级三种格式说明
- 单测 +13含 Hutool BeanUtil 嵌套 Map → Location 运行时验证)
- DB schema 不变(仍是 JSON 列)
- 老数据零迁移
---
## 风险 / 兼容性
- **零回归承诺**:老行政区数据继续按原行为工作(路线图 origin/destination / 每日里程 / 团报城市 全部正常)
- **新旧混合期**:同一产品 itinerary 不同 day 可以混存新 POI / 老行政区结构,互不影响
- **降级**POI 缺 `location` 时自动回退到 `adcode` 反查中心点(高德 API,与现行 `MpProductRouteMapService` 老路径一致
---
## 联调建议
mmg 前端改完后:
1. 本地起 mock 服务返回新 POI 结构,验证组件回显
2. 接到测试服 `api.test.1814.love:9443` 后端,PUT → GET round-trip 验证字段对齐
3. 看路线图 / 团报 / 每日里程,确认坐标用的是 POI 精确坐标(比之前 adcode 中心点更精准)