|
|
@@ -0,0 +1,157 @@
|
|
|
+# Feature Specification: 自部署地图集成(台湾外卖/闪送)
|
|
|
+
|
|
|
+**Feature Branch**: N/A(按用户要求本次不建分支,文档随当前分支走)
|
|
|
+
|
|
|
+**Created**: 2026-09-24
|
|
|
+
|
|
|
+**Status**: Draft
|
|
|
+
|
|
|
+**Input**: User description: "自部署地图集成:台湾外卖/闪送切换到自部署地图服务(Foodie_HCM_Map,PostGIS 搜索 + OSRM 算路),外卖/闪送运费按平台设置计价(pos_freight / flash_delivery_pricing),所有地图功能逐步切换,已有完整差距分析结论。"
|
|
|
+
|
|
|
+## User Scenarios & Testing *(mandatory)*
|
|
|
+
|
|
|
+### User Story 1 - 闪送报价距离切换到自部署地图(Priority: P1)
|
|
|
+
|
|
|
+运营在平台配置中把路线距离来源从 Google 切到自部署地图后,用户发起闪送报价时,距离与费用按自部署地图的**道路距离**计算;当地图服务不可达、路线不可达或坐标吸附失败时,沿用现有直线距离兜底保证报价可用,且每单记录实际使用的距离来源,运营可事后核对。
|
|
|
+
|
|
|
+**Why this priority**: 闪送线已有路线距离抽象与"路线失败降级直线 + 距离来源落库"机制,切换成本最小;此切片独立可用,是整个切换的 MVP 与灰度入口。
|
|
|
+
|
|
|
+**Independent Test**: 字典开关切到自部署后发起闪送报价,返回的距离来自地图服务;人为停掉地图服务再报价,仍能出价且距离来源标记为直线。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 平台距离来源配置为自部署地图,**When** 用户提交闪送报价(两点或多站点),**Then** 报价使用地图道路距离,订单落库距离来源为"地图路线"。
|
|
|
+2. **Given** 地图服务超时或返回不可达/吸附失败,**When** 用户提交闪送报价,**Then** 报价仍成功(直线距离兜底),订单落库距离来源为"直线"。
|
|
|
+3. **Given** 距离来源配置切回 Google,**When** 再次报价,**Then** 全链路回到原 Google 路径,无需发版。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 2 - 外卖运费改为道路距离 + 后端权威计价(Priority: P1)
|
|
|
+
|
|
|
+用户下单时,外卖运费不再信任前端传入值:后端按订单的实际取送坐标用地图道路距离、结合平台运费规则(pos_freight 时段起步价与跳表)计算运费;用户确认订单页看到的费用即后端计算结果,前端传入的运费仅用于一致性校验。
|
|
|
+
|
|
|
+**Why this priority**: 外卖运费目前用直线距离且由前端定价,既不符合"道路距离计价"目标,也存在改价风险;平台规则载体(pos_freight)已存在,补上后端权威计价即完成核心商业闭环。
|
|
|
+
|
|
|
+**Independent Test**: 用一对坐标下单,比对订单落库运费与后端按 pos_freight 规则 + 地图距离手算结果一致;篡改前端传入运费不影响最终订单金额。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 用户下单且地址、门店坐标有效,**When** 订单创建,**Then** 订单运费 = 平台 pos_freight 当前时段规则 × 地图道路距离(保留现有跳表规则:超起步距离 <0.5km 不计、0.5~1km 按 1km)。
|
|
|
+2. **Given** 前端传入的运费与后端重算不一致,**When** 订单创建,**Then** 以后端计算为准落库并记录差异,不阻断下单。
|
|
|
+3. **Given** 地图服务失败,**When** 用户下单,**Then** 运费按直线距离兜底计算,订单仍可创建,距离来源落库。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 3 - 选址、逆地理与地址搜索切自部署地图(Priority: P2)
|
|
|
+
|
|
|
+用户在 App 选择收货/取件地址时,地址关键词搜索、地图点选反查、附近候选列表来自自部署地图;App 不直连地图服务,统一经 foodie 后端聚合接口访问(地图服务读接口无鉴权,仅限内网调用)。
|
|
|
+
|
|
|
+**Why this priority**: 选址是 App 侧高频功能,依赖 App 前端改造与联调,排在服务端能力切换之后;完成此切片后,用户侧不再产生 Google/百度地址类调用。
|
|
|
+
|
|
|
+**Independent Test**: 在 App 地址选择页搜索中文地址、点选地图取点,返回结果来自自部署地图;断开地图服务时地址选择页给出明确错误提示。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 用户在地址页输入中文关键词,**When** 发起搜索,**Then** 返回自部署地图的候选列表(名称/地址/坐标)。
|
|
|
+2. **Given** 用户在地图上点选一个点,**When** 触发反查,**Then** 返回最近候选与精度标识,坐标不被候选覆盖,需用户确认。
|
|
|
+3. **Given** 地图服务不可用,**When** 用户搜索地址,**Then** 收到友好错误提示,App 其它功能不受影响。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 4 - 闪送多站点订单路线支持(Priority: P2)
|
|
|
+
|
|
|
+闪送多站点单(用户按顺序添加多个站点)在自部署地图下同样按站点序列计算道路距离;在地图服务扩展多站点能力上线前,多站点单维持原 Google 路径或直线兜底,不因切换而坏单。
|
|
|
+
|
|
|
+**Why this priority**: 自部署地图当前仅支持两点路线,多站点是包装层待扩展项;属跨仓库依赖,可与 foodie 侧切换并行推进。
|
|
|
+
|
|
|
+**Independent Test**: 提交 3 站点闪送报价,返回的距离为按站点顺序的道路总距离;地图多点能力未上线期间,该单按既有路径正常出价。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 地图服务已支持多站点,**When** 用户提交 3 站点报价,**Then** 距离按传入顺序途经各站点的道路总距离计算。
|
|
|
+2. **Given** 地图多站点能力未上线,**When** 用户提交多站点报价,**Then** 报价仍成功(Google 或直线兜底),不出现报错坏单。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 5 - 商家可被地图搜索发现(Priority: P3)
|
|
|
+
|
|
|
+平台的台湾商家(名称/地址/坐标)同步进自部署地图的热门地点库,用户在地址选择时能直接搜到商家并定位,减少手输地址成本。
|
|
|
+
|
|
|
+**Why this priority**: 提升选址体验的增量能力,依赖前四个切片稳定后才有意义。
|
|
|
+
|
|
|
+**Independent Test**: 新建/修改商家后(或定时任务执行后),在地址搜索框输入商家名能搜到该商家。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 商家已在平台建档且坐标有效,**When** 同步任务执行,**Then** 地图搜索该商家名可返回其名称、地址与坐标。
|
|
|
+2. **Given** 商家改名或搬迁,**When** 同步再次执行,**Then** 地图侧信息更新,旧信息不残留。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 6 - 预计送达时间与旧外部地图服务退场(Priority: P3)
|
|
|
+
|
|
|
+预计送达时间不直接使用地图的汽车时长,而按平台配置的车型/时段折算系数估算;切换稳定后,旧的 Google/Mapbox/百度地图调用端点停止对外服务,已泄漏在代码仓库中的第三方地图密钥全部作废。
|
|
|
+
|
|
|
+**Why this priority**: 收尾性质——ETA 折算需要运营数据支撑系数,旧端点下线需等全部调用方迁移完成。
|
|
|
+
|
|
|
+**Independent Test**: 修改平台折算系数后,订单预计送达时间按新系数变化;访问旧外部地图端点返回已停用提示;泄漏密钥在第三方控制台已失效。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 平台配置了机车折算系数,**When** 产生闪送/外卖订单,**Then** 预计送达时间 = 地图汽车时长 × 系数。
|
|
|
+2. **Given** 全部调用方已迁移,**When** 访问旧地图端点,**Then** 返回已停用(410/明确提示),不再产生第三方调用费用。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Edge Cases
|
|
|
+
|
|
|
+- 路线不可达(NO_ROUTE,如离岛/无路网区域):报价与下单按直线兜底并标记来源,不阻断交易。
|
|
|
+- 坐标吸附超限(SNAP_TOO_FAR,默认 250 米):老地址库坐标可能大面积触发——切换前必须用存量地址批量试算体检,按结果决定调整吸附上限或引导重选地址。
|
|
|
+- 坐标越界(OUTSIDE_REGION):拦截并提示地址无效,不降级。
|
|
|
+- 地图服务整体宕机:闪送/外卖计价全部走直线兜底,选址接口明确报错;派单与附近骑手不受影响(数据库半径计算,不依赖地图)。
|
|
|
+- 多站点单在地图多点能力空窗期:维持 Google 或直线兜底,不坏单。
|
|
|
+- 外卖订单坐标取自门店坐标快照:门店坐标缺失/非法的商家,下单时按直线兜底并提示运营补录。
|
|
|
+
|
|
|
+## Requirements *(mandatory)*
|
|
|
+
|
|
|
+### Functional Requirements
|
|
|
+
|
|
|
+- **FR-001**: 平台 MUST 提供距离来源开关(第三方路线服务 / 自部署地图),可运行时切换与回滚,不依赖发版。
|
|
|
+- **FR-002**: 闪送报价距离 MUST 优先取自配置的路线来源,失败时按直线距离兜底,且每笔报价/订单 MUST 落库实际距离来源。
|
|
|
+- **FR-003**: 外卖运费 MUST 由后端在下单时按订单坐标 + 平台 pos_freight 规则重算并落库;前端传入运费仅作一致性比对,不一致时以后端为准并记录差异。
|
|
|
+- **FR-004**: 外卖运费报价接口 MUST 返回道路距离、费用与距离来源三项信息,供前端展示与后端校验共用。
|
|
|
+- **FR-005**: 地址搜索、逆地理、附近候选能力 MUST 经 foodie 后端聚合提供,App MUST NOT 直连地图服务(地图服务无读鉴权,仅限内网)。
|
|
|
+- **FR-006**: 地图服务 MUST 支持多站点顺序路线(跨仓库扩展项);能力上线前 foodie MUST 保证多站点单可用既有路径完成报价。
|
|
|
+- **FR-007**: 商家信息(名称/地址/坐标)MUST 可同步至地图搜索库,支持新增与更新。
|
|
|
+- **FR-008**: 预计送达时间 MUST 按平台可配置的车型折算系数计算,MUST NOT 直接使用地图汽车时长作为送达时间展示。
|
|
|
+- **FR-009**: 切换稳定后,旧第三方地图端点 MUST 停止服务并返回明确停用标识;代码仓库中已泄漏的第三方地图密钥 MUST 作废。
|
|
|
+- **FR-010**: 切换前 MUST 提供存量收货地址的批量路线试算体检能力,输出吸附失败比例与清单供运营决策。
|
|
|
+- **FR-011**: 派单、附近骑手、门店距离排序 MUST 维持现有数据库半径计算,不依赖地图服务可用性。
|
|
|
+
|
|
|
+### Key Entities *(include if feature involves data)*
|
|
|
+
|
|
|
+- **距离来源(distanceSource)**: 每笔闪送报价/外卖订单记录的距离计算方式(地图路线/直线),用于对账、监控与切换验证。
|
|
|
+- **平台外卖运费规则(pos_freight,既有)**: 按时段的起步价、起步距离、跳表单价;计价输入为道路距离,输出为运费。
|
|
|
+- **闪送定价规则(flash_delivery_pricing,既有)**: 按车型/时段的起步、步长单价、加急费率;计价输入为道路距离。
|
|
|
+- **地图服务配置(平台字典)**: 距离来源开关、地图服务地址、失败策略、机型折算系数等运营参数。
|
|
|
+
|
|
|
+## Success Criteria *(mandatory)*
|
|
|
+
|
|
|
+### Measurable Outcomes
|
|
|
+
|
|
|
+- **SC-001**: 切换自部署地图后,闪送报价成功率不低于切换前基线的 99%(兜底机制保证)。
|
|
|
+- **SC-002**: 外卖订单运费 100% 由后端计算落库,抽样核对与平台规则手算结果一致。
|
|
|
+- **SC-003**: 切换后选址搜索与路线计算的 95% 请求在 2 秒内返回。
|
|
|
+- **SC-004**: 除明确保留项(如临时 ETA)外,平台对第三方地图服务的调用量降为 0,月度地图 API 费用清零。
|
|
|
+- **SC-005**: 存量地址体检完成并输出吸附失败清单后,方可全量切换;全量切换后因地图错误导致的下单失败率 < 0.1%。
|
|
|
+
|
|
|
+## Assumptions
|
|
|
+
|
|
|
+- 本 spec 覆盖 foodie 后端与运营配置;App 前端(uni-app,不在本仓库)的选址页、费用展示改造由前端团队按本 spec 接口对接。
|
|
|
+- `PosOrderController` 的通用管理接口 `POST /system/order`(`add(...)`)和 `PUT /system/order`(`edit(...)`)已废弃,不纳入本特性的订单创建、修改入口;它们与仍在使用的 `/system/order/addorder` 不同。
|
|
|
+- 地图服务(Foodie_HCM_Map)部署在与 foodie 服务器同内网或经私网打通;其多站点路线扩展、读接口防护属地图仓库工作,作为跨仓库依赖跟踪。
|
|
|
+- 地图仅提供 car 档道路距离,不提供机车档与实时交通;距离用于计价可信,时长仅作折算参考(见 FR-008)。
|
|
|
+- 现有运费规则载体(pos_freight、flash_delivery_pricing)与管理端 CRUD 继续沿用,本特性不改动计价规则本身。
|
|
|
+- 派单/附近骑手/门店排序维持数据库半径计算,不纳入切换范围。
|
|
|
+- 外卖前端运费与后端不一致时,默认以后端为准落库并记日志,不阻断下单;若运营要求阻断式校验,在实现阶段调整。
|
|
|
+- 泄漏的 Google/百度密钥作废动作需运维在第三方控制台执行,spec 只约束代码侧停止引用并留痕。
|