Browse Source

文档:补充夜市自配送 App 接入说明并记录地图模型评估

qmj 1 week ago
parent
commit
92f746f5ea

+ 1 - 0
docs/map-api-usage.md

@@ -53,5 +53,6 @@
 
 ## 关联
 
+- 台湾 OSM 需求差距与后续事项(2026-10-10,先记录、暂不实施):[osm-requirements-assessment.md](../specs/032-self-hosted-map-integration/osm-requirements-assessment.md)。测试服务器已支持独立机车路网,但 foodie 自部署适配尚未传车型;现有 car-only 假设不代表该测试服务现状。
 - 切换计划:`specs/032-self-hosted-map-integration/spec.md`(自部署 Foodie_HCM_Map:PostGIS 搜索 + OSRM 算路 + OpenFreeMap 底图;plan/tasks 未生成,未开工)
 - 自部署地图仓库:`D:\QtwCode\foodie\Foodie_HCM_Map`

+ 6 - 0
specs/029-merchant-self-delivery/delivery.md

@@ -1,5 +1,11 @@
 # 029 商家自配送 — 交付说明
 
+## 商家 App 接入补充(2026-10-12)
+
+前端接入文档:[frontend-app-integration.md](frontend-app-integration.md)。包含夜市主 My 配置接口、请求与响应示例、星期多时段校验及配送操作衔接。本次仅更新文档,不修改 App 或后端业务代码。
+
+核对当前源码发现:`PosOrderShOprateController.orderList` 仍要求外送单具备骑手 ID,正常 `selfDelivery=1 && qsId=null` 的订单会被过滤,设置页可独立接入,但订单列表与完整配送闭环必须先修正后端过滤并回归。本次未修复,未进行线上/数据库联调;下文历史交付状态不能替代这一当前限制。
+
 **日期**: 2026-09-08 | **分支**: `test-202609v2`(按用户要求未单独开分支)
 
 ## 交付状态

+ 110 - 0
specs/029-merchant-self-delivery/frontend-app-integration.md

@@ -0,0 +1,110 @@
+# 夜市主自配送:商家 App 接入说明
+
+更新日期:2026-10-12。
+
+## 1. 页面入口与请求
+
+- 仅夜市主(`userType=3`)显示“自配送设置”,对旗下摊位统一生效。
+- 页面提供:开关、全天/自定义选择、按星期添加多个时段、保存。
+- 接口沿用 App 现有 baseURL;请求头携带 `token`,POST 使用 JSON。
+- 业务 `code=200` 表示成功;失败展示返回的 `msg`。
+
+## 2. 读取配置
+
+`GET /chanting/store/getMyNightMarketSelfDelivery`
+
+无参数,不传门店 ID。返回示例:
+
+```json
+{
+  "code": 200,
+  "msg": "操作成功",
+  "data": {
+    "enabled": true,
+    "mode": 2,
+    "hours": [
+      { "dayOfWeek": 1, "startTime": "17:00", "endTime": "22:00" },
+      { "dayOfWeek": 6, "startTime": "11:00", "endTime": "14:00" },
+      { "dayOfWeek": 6, "startTime": "17:00", "endTime": "23:00" }
+    ]
+  }
+}
+```
+
+按 `data` 回显表单。未设置时为 `enabled=false、mode=null、hours=[]`;表单可默认选择全天,但开关保持关闭。
+
+## 3. 保存配置
+
+`POST /chanting/store/saveMyNightMarketSelfDelivery`
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| enabled | Boolean | true 开启、false 关闭,必填 |
+| mode | Integer / null | 开启时必填:1 全天、2 自定义;关闭传 null |
+| hours | Array | 自定义时至少一段;全天/关闭传空数组 |
+| hours[].dayOfWeek | Integer | 1 周一~7 周日 |
+| hours[].startTime | String | 开始时间,HH:mm |
+| hours[].endTime | String | 结束时间,HH:mm |
+
+自定义请求:
+
+```json
+{
+  "enabled": true,
+  "mode": 2,
+  "hours": [
+    { "dayOfWeek": 1, "startTime": "17:00", "endTime": "22:00" },
+    { "dayOfWeek": 6, "startTime": "11:00", "endTime": "14:00" },
+    { "dayOfWeek": 6, "startTime": "17:00", "endTime": "23:00" }
+  ]
+}
+```
+
+全天请求:
+
+```json
+{ "enabled": true, "mode": 1, "hours": [] }
+```
+
+关闭请求:
+
+```json
+{ "enabled": false, "mode": null, "hours": [] }
+```
+
+成功返回:
+
+```json
+{ "code": 200, "msg": "操作成功" }
+```
+
+保存成功后重新读取配置。**保存整体覆盖全部时段,不是追加;不传 mdId。**
+
+## 4. 必要规则
+
+- 同一天允许多段,不允许重叠;前一段结束等于后一段开始可以。
+- 结束必须晚于开始,不支持跨午夜单段或 `24:00`。
+- 自定义模式至少有一段,不要求每天都有时段。
+- 开始时间包含、结束时间不包含:`17:00–22:00` 在 17:00 命中、22:00 不命中。
+- 关闭或切成全天会清除服务端旧自定义时段。
+- 配置只影响之后创建的外卖外送单,旧订单配送方式不变;不用于闪送。
+- 页面文字沿用 App 的四语言 i18n。
+
+## 5. 自配送订单操作
+
+订单 `selfDelivery=1` 时显示“自配送”,不显示等待骑手或联系骑手。摊位主负责接单/出餐,夜市主负责配送。
+
+| 操作 | 接口 | JSON 请求 |
+|---|---|---|
+| 开始配送(可选) | POST /system/orderShOprate/selfDeliveryStart | {"id":12345} |
+| 确认送达 | POST /system/orderShOprate/selfDeliveryComplete | {"id":12345} |
+
+`id` 为订单数据库 ID,不是订单号;确认送达可另传字符串 `qsImg` 作为送达凭证。
+
+- 两个操作都要求:有权限的外送自配送单、无骑手归属(`qsId=null`)、无售后、已出餐(`state=2`)。
+- 开始配送要求 `deliveryStatus=0`;成功后变为 2(配送中)。
+- 确认送达要求 `deliveryStatus=0/2` 且 `payStatus=1`;成功后 `state=3、deliveryStatus=3`。
+- 可跳过开始配送直接确认送达。提交中禁用按钮,成功后刷新订单。
+- 查询详情:`GET /system/orderShOprate/orderDetail?id=12345`,订单对象在业务 `data` 中。
+
+**联调提示:当前商家订单列表会过滤无骑手的自配送单,需后端修正后才能联调完整订单流程;设置页面不受此问题影响。**

+ 95 - 0
specs/032-self-hosted-map-integration/osm-requirements-assessment.md

@@ -0,0 +1,95 @@
+# 台湾 OSM 地图需求差距与后续事项
+
+记录日期:2026-10-10。
+
+来源:用户提供的《OSM地理信息服务后端需求文档.docx》及本次会话的代码核对、测试服务器接口实测。
+
+用户决定:先记录,后续再实施。本记录列出的追加工作状态为 **未开始**;不表示 032 已有能力未实现,也不表示本次已修改业务代码或完成地图验收。本次不切换分支、不部署、不修改数据库,不调整 App。
+
+## 一、结论与版本边界
+
+现有 OSM + PostGIS 搜索 + OSRM 算路 + MapLibre/OpenFreeMap 方案可继续完善,无需推倒重做,但尚未满足需求文档的全部验收标准。
+
+- 地图仓库:`D:/QtwCode/foodie/Foodie_HCM_Map`。本次本地工作区为 `master`,只配置汽车路网。
+- 地图仓库本地远端引用 `origin/taiwan-map-profiles`(本次读取提交 `8906fe1`)已有独立机车模型和双车型部署记录。该分支文件不等于当前 `master` 文件,也不能仅凭分支名称推定服务器实际发布版本。
+- 测试服务器 `http://47.74.34.111/api` 实际声明并响应 `car`、`motorcycle`。因此不能把“本地 master 只有汽车”描述为“测试服务器没有机车能力”。后续实施前应按发布清单核对线上代码、路网数据和目标分支。
+- foodie 后端 `SelfHostedRouteDistanceProvider` 当前请求 `GET /route/multi?coords=...`,没有向地图传递 `vehicleType` 对应的路线车型;不能因为地图支持机车,就认为业务已按用户车型算路。
+- 地图的 `duration_seconds` 是模型行驶时间,`traffic_aware=false`,不是包含等待接单、取件、交付的承诺送达时间。
+
+## 二、需求对照
+
+| 需求 | 当前依据与差距 | 后续处理方向 |
+|---|---|---|
+| 台湾道路、地址与 POI 数据 | 已有台湾 PBF 导入、搜索索引及路网构建;不保证 OSM 门牌齐全,未完成系统性拓扑和真实营业地址验收 | 验证数据版本、路网连通性、离岛及真实取送点覆盖,保留人工补录能力 |
+| 地图道路、巷弄、建筑与位置标记 | 验证页已有展示能力;瓦片、样式及字体来自外部 OpenFreeMap,非完全自托管 | 先明确是否必须自托管瓦片,再决定基础设施工作;App 接入仍由前端团队完成 |
+| 正向地址搜索 | 可检索繁体完整地址;本次文档中的简体地址无结果 | 补充简繁归一化、台湾地址段/巷/弄/号解析,并验证召回和排序 |
+| 逆向结构化地址 | 当前返回附近道路或地点、原始图钉坐标与确认标识,不是完整县市/区/道路/门牌结构 | 明确结构化字段和精度;缺失门牌不能伪造,保留用户确认 |
+| 模糊搜索与自动补全 | 已有包含匹配、短词前缀建议;不等于完整地址解析、错字容错或语义搜索 | 以真实台湾地址验证部分输入、简繁、别名、数字和错字;按结果确定最小改动 |
+| 骑手机车路径规划 | 已有独立机车路网;基础模型避开 motorway 及连接线,trunk/motorroad 需明确机车许可标签 | 验收真实禁行路段、单行、转向、两段式转弯;明确适用机车/电动车类别,不能宣称所有类别通行规则相同 |
+| 短距离、少红绿灯的权重策略 | 机车模型沿用汽车基础权重;未见针对文档目标的专项验收 | 先以合法通行为硬约束,再明确距离、红绿灯和转向权重,使用已知路线对照验证 |
+| 距离与预计时间 | 已返回道路距离、模型时间;无实时交通与取送件耗时 | foodie 按用户车型选择路网;行驶时间与业务预计送达时间分开定义,不靠汽车时间系数替代机车合法路线 |
+| 定期更新数据 | 有新 Compose 项目全量构建、验收切换与回退说明;本次未验证周期自动更新已运行 | 明确周期、人工地点/别名迁移、校验、切换、失败告警和回退责任 |
+| 搜索误差 20 米 | 未对真实基准坐标验证;建筑内部多个 POI 可能共享门牌,搜到建筑不代表定位到入口 | 修正验收样例,定义建筑中心或配送入口基准,再做真实地址精度验收 |
+| 算路 P99 <300ms、搜索 P99 <200ms | 没有对应并发压测证据;单次公网请求不能证明服务端 P99 | 明确测量位置、并发量、冷/热缓存、请求集与统计样本后压测 |
+
+## 三、2026-10-10 实测证据
+
+仅请求公开测试服务,未使用业务数据库、密钥或用户订单地址,未修改服务器数据。
+
+| 请求或输入 | 实际结果 |
+|---|---|
+| `GET /config` | `country=TW`、`routing_profiles=[car,motorcycle]`、`traffic_aware=false`,底图为 OpenFreeMap liberty |
+| 搜索 `台北市信义区松寿路11号` | 0 个结果 |
+| 搜索 `臺北市信義區松壽路11號` | 6 个结果,包括 iStore、GB 鮮釀餐廳、新光三越 A11 館 |
+| 搜索 `台北101` | 有结果,返回地址包含 `台北市信義區信義路五段7號` |
+| 反查 `25.0330,121.5654` | 最近道路为松智路,`precision=nearby_place`、`requires_confirmation=true`;不是完整门牌结构 |
+| 两点汽车路线:`25.0330,121.5654` → `25.0375,121.5637` | 693 米、63 秒,`profile=car` |
+| 同一组两点机车路线:`profile=motorcycle` | 693 米、87 秒,`profile=motorcycle` |
+| `GET /route/multi?coords=25.0330,121.5654;25.0375,121.5637` | HTTP 200,693 米、63 秒;未传车型时为汽车结果 |
+| `POST /route` JSON 多点请求 | 本次服务器返回 HTTP 405;不能将另一分支的 POST 契约直接当成当前部署契约 |
+
+本次少量成功请求的客户端端到端耗时约 0.8~1.2 秒,包含客户端到东京服务器网络时间,不是服务端 P99,不能作为性能达标或失败的最终依据。没有运行并发压测、20 米精度测量或全台湾禁行路段验收。
+
+需求文档把“台北市信义区松寿路11号”标为台北101,需要修正。该地址实测返回 A11 馆相关地点;台北101官方信息列出信义路五段7号、市府路45号。门牌与地标应拆成各自的正确样例,并明确配送入口基准。
+
+## 四、后续继续工作的入口
+
+以下是待办记录,不是本次已批准执行的实施计划;用户决定继续后,再补充或更新 032 的 `plan.md`、`tasks.md` 和相应地图侧规格。
+
+### 机车模型复用方向(2026-10-10 会话追加)
+
+用户要求先记录,暂不实施。后续优先沿用现有 OSRM,参考 Valhalla 的机车规则补强已有 `motorcycle.lua`,不因发现现成模型就直接替换算路引擎。
+
+- OSM 是原始地图数据;OSRM 和 Valhalla 是可使用这些数据的不同算路引擎。Valhalla 不是 OSRM 插件,不能直接把其模型文件放入 OSRM。
+- Valhalla 已有 `motorcycle` 和 `motor_scooter` 模型;前者官方文档标记为 Beta。模型名称不能直接等同于台湾机车类别,适用车型与当地规则仍需验收。
+- 官方源码入口为 `src/sif/motorcyclecost.cc`(摩托车成本模型)、`valhalla/sif/motorcyclecost.h`(接口声明)、`src/sif/motorscootercost.cc`(踏板机车/轻便摩托车成本模型)。它们依赖公共代码与路网导入结果,不是可独立复制运行的配置文件。
+- 可优先借鉴的规则包括道路通行、单行、路面与道路类型速度估算、路线偏好、转向和红绿灯成本。需要按本项目固定的 OSRM v5.27.1 能力映射,不能照搬较新版本文档中的 API。
+- OSRM Lua 主要在路网预处理时执行,Valhalla 支持请求时动态成本计算。不能承诺逐行把 C++ 转成 Lua 就能完整复刻;动态偏好、按时间变化的限制等需单独评估,必要时可能涉及引擎能力而非配置。
+- 迁移目标是选择适合台湾配送的业务规则,不是复刻 Valhalla 的全部实现或保证两个引擎的路线、时长完全一致。现成通用模型也不自动证明台湾禁行与两段式转弯已正确处理。
+- 后续核对源代码许可证与署名要求,建立规则对照与可复现测试,再修改 Lua、重建独立机车路网。验收应覆盖禁行、单行、转向、道路吸附、距离、估时和性能;未验收前不切换正式业务。
+
+状态:**未开始**。本次未转换 C++、未修改 Lua、未重建路网、未部署 Valhalla,也未切换算路引擎。
+
+- [ ] 核对地图分支、服务器发布清单与实际接口契约,避免用旧 master 或未部署分支推断能力。
+- [ ] 对照 Valhalla 两种机车模型,识别可迁移至 OSRM Lua 的规则及不能等价迁移的能力,再形成最小实施范围与测试样例。
+- [ ] 核对 `/route/multi` 的车型参数支持;foodie 将 `vehicleType=1/2` 映射为 `motorcycle/car`,并覆盖两点、多站点与降级行为测试。
+- [ ] 补充台湾机车合法路线验收,并确定短距离、红绿灯和转向权重。
+- [ ] 补充台湾地址简繁归一化、地址解析和逆查结构化字段。
+- [ ] 修正文档地址样例,用真实门店/收货入口验证搜索、吸附与 20 米精度。
+- [ ] 明确“模型行驶时间”和“订单预计送达时间”的计算口径,再更新 ETA 相关规格。
+- [ ] 确定是否要求完全自托管瓦片;验证实际 App 加载由前端团队联调,不修改 App 源码。
+- [ ] 补齐数据周期更新、人工数据迁移、失败回退与监控,再开展约定的性能压测。
+- [ ] 按 032 既有要求完成存量地址批量体检后,再决定全量切换。
+
+032 现有“地图仅有 car 档”和“预计时间按汽车时间乘系数”的假设需要在恢复实施时重新校准。本次只记录事实与差距,未直接改写 FR-008、US6 或将新验收目标宣称为已经实现。原 032 的 P95 <2 秒与本需求的 P99 毫秒级指标也不是同一个标准,后续应明确取舍与责任边界。
+
+## 五、相关资料
+
+- foodie 既有规格:[spec.md](spec.md)。
+- foodie 地图调用现状:[map-api-usage.md](../../docs/map-api-usage.md)。
+- 地图侧分支资料:`origin/taiwan-map-profiles` 中的 `docker/osrm/profiles/motorcycle.lua`、`docs/2026-09-09-datasets-and-profiles.md`、`docs/2026-09-09-tokyo-dual-profile-deployment.md`。注意这些文件未必存在于当前 master 工作区。
+- [OSRM Profiles](https://project-osrm.org/docs/v26.4.0/profiles):车型模型在路网预处理时确定;修改模型需重新构建,不能只改变接口车型名称。
+- [Valhalla 路线 API 与车型模型](https://valhalla.github.io/valhalla/api/route/api-reference/):用于核对动态成本及 `motorcycle`、`motor_scooter` 的含义与参数。
+- [Valhalla 摩托车模型源码](https://github.com/valhalla/valhalla/blob/master/src/sif/motorcyclecost.cc)、[接口声明](https://github.com/valhalla/valhalla/blob/master/valhalla/sif/motorcyclecost.h)、[踏板机车模型源码](https://github.com/valhalla/valhalla/blob/master/src/sif/motorscootercost.cc):链接为可变的 master,恢复实施时应固定参考提交或版本,不将当前网页版本视为本项目依赖版本。
+- [OpenFreeMap 官方说明](https://openfreemap.org/):支持公共服务与自托管,公共服务无 SLA 保证。
+- [台北101官方信息](https://online.taipei-101.com.tw/zh-TW/info/privacy-policy):用于核对台北101地址,不替代配送入口的实测坐标。

+ 2 - 0
specs/032-self-hosted-map-integration/spec.md

@@ -8,6 +8,8 @@
 
 **Input**: User description: "自部署地图集成:台湾外卖/闪送切换到自部署地图服务(Foodie_HCM_Map,PostGIS 搜索 + OSRM 算路),外卖/闪送运费按平台设置计价(pos_freight / flash_delivery_pricing),所有地图功能逐步切换,已有完整差距分析结论。"
 
+**后续事项记录(2026-10-10)**:用户要求先记录、后续再做。台湾 OSM 需求对照、双车型测试服务实测、地址精度/性能差距及待办见 [osm-requirements-assessment.md](osm-requirements-assessment.md)。追加工作未开始,未改变上述既有实现进度;本规格的 car-only 和汽车时长折算假设需在恢复实施时重新校准。
+
 ## User Scenarios & Testing *(mandatory)*
 
 ### User Story 1 - 闪送报价距离切换到自部署地图(Priority: P1)