docs(changelog): 行程精准定位 schema 追加镇级字段 (#1574/#1575)

PlaceItem 加 provinceName/township/towncode 三段镇级行政区字段。
@mmg 选 POI 后多调一次高德 regeo 拿 addressComponent.township/towncode + 拼 4 段 fullPath。

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

查看文件

@ -1,11 +1,13 @@
# 行程编排集合地/解散地:行政区 → 高德 POI 精准定位
**类型**: 后端 FEAT(字段扩展+读路径增强) + 前端 picker 切换
**关联**: 工单 #1563 / PR #1570
**类型**: 后端 FEAT(字段扩展+读路径增强) + 前端 picker 切换 + regeo 调用
**关联**: 工单 #1563 / PR #1570 + follow-up 工单 #1574 / PR #1575(镇级行政区字段)
**日期**: 2026-04-30
**前端处理者**: mmg
**影响范围**: 管理后台产品编辑页 Step 2「行程编排」→ DAY X 的「开始 / 结束」字段(即 `gatherPlace` / `dismissalPlace`
> ⚠️ **2026-04-30 16:25 updatePR #1575**:行政区精度要求**到镇/街道级**。POI 自身只到区县,前端选 POI 后**必须额外调高德 regeo (逆地理编码)** 拿镇级数据。详见下方 schema 和 mmg 前端 mapping。
---
## 业务目的
@ -18,20 +20,26 @@
## JSON Schema 升级
### 新格式POI 精准定位,2026-04-30 起新数据按这个落库)
### 新格式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 解析所属行政区
"name": "海拉尔东山国际机场", // POI 名称
"address": "内蒙古呼伦贝尔市海拉尔区机场路", // 详细地址
"location": { "lng": 119.825, "lat": 49.205 }, // POI 精确坐标
"poiId": "B0FFGTEST1", // 高德 POI ID
"adcode": "150702", // 区县编码POI 自带)
"provinceName": "内蒙古自治区", // 省regeo addressComponent.province
"cityName": "呼伦贝尔市", // 市regeo addressComponent.city
"districtName": "海拉尔区", // 区regeo addressComponent.district
"township": "东山街道", // 镇/街道regeo addressComponent.township
"towncode": "150702002000", // 镇编码regeo addressComponent.towncode
"fullPath": "内蒙古自治区/呼伦贝尔市/海拉尔区/东山街道" // 4 段(前端拼)
}
```
**关键点**POI 本身(高德 PlaceSearch 返回)只到区县级 `adname`,**到镇级必须前端额外调高德 regeo (`AMap.Geocoder.getAddress`)** 拿 `addressComponent.{township, towncode}` 后 merge 到 gatherPlace JSON。
### 老格式行政区,2026-04-30 之前已落库的存量数据,零迁移继续工作)
```jsonc
@ -62,20 +70,39 @@
### 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
};
// 选 POI 后,先用 location 调 regeo 拿镇级行政区,再 merge
function poiToPlace(poi, callback) {
const geocoder = new AMap.Geocoder();
geocoder.getAddress([poi.location.lng, poi.location.lat], (status, result) => {
if (status !== 'complete') {
// 降级regeo 失败时退回到 POI 自带数据(无 township/towncode
callback({
name: poi.name,
address: poi.address || '',
location: { lng: Number(poi.location.lng), lat: Number(poi.location.lat) },
poiId: poi.id || '',
adcode: poi.adcode || '',
cityName: poi.cityname || '',
districtName: poi.adname || ''
// 此时 provinceName/township/towncode/fullPath 留空,后端兼容
});
return;
}
const ac = result.regeocode.addressComponent;
callback({
name: poi.name,
address: poi.address || result.regeocode.formattedAddress,
location: { lng: Number(poi.location.lng), lat: Number(poi.location.lat) },
poiId: poi.id || '',
adcode: poi.adcode || ac.adcode,
provinceName: ac.province || '',
cityName: (ac.city && ac.city.length) ? ac.city : '', // 直辖市 city 是空数组
districtName: ac.district || '',
township: ac.township || '',
towncode: ac.towncode || '',
fullPath: [ac.province, ac.city, ac.district, ac.township].filter(s => s && s.length).join('/')
});
});
}
```