v24 第 17 批:城市聚合 / 高德地址 / 表单精简(用户 10 条)

用户 10 条原话要点与落地:
- ①「统计出来的不是目的地,也不是车站,而是城市…城市要聚合,有多段行程,
  都有青岛作为出发地或者目的地,那么就只记一次」
  → trip_nodes 补 from_city/to_city(Schema v22),Trip::cities() 改成
    「出发地 ∪ 到达地」去重、按最早时间 + 出发地优先排。
    详情页信息区显示的就是这个聚合结果(第 4 条:「图片右侧显示的是聚合的城市,
    而不是终点地址」)。
- ②「类型不够丰富,你看看 triplog 里面都有哪些,都拿过来」
  → NodeType::TRIPLOG_TYPES(休息/突发/购物/拜访亲友),在 all() 里读时自愈补齐。
- ③「出行方式,只有选择自驾,才让选车辆;车辆下拉框不需要出现什么财务里登记的车,
  只把车辆列出来就行了」→ DRIVE_MODES 收窄成 ['drive'];
  Vehicle::pickable() 只回车辆库;车辆页删掉「财务里已登记的车」那块。
- ③「飞机、铁路,只记录车次、航班号就可以…座位什么的不记录了」
  → seat_info / gate 从表单与列表**彻底移除**(列保留,老数据不丢;
    saveNode 的 UPDATE 也不再写这两列,否则会抹掉迁来的数据)。
- ③「出发地和到达地,都用高德地图的接口,记录详细地址」+「参与人弹框选择」
  +「给参与人标注角色这个功能去掉」
  → 新增 templates/trips/_loc_field.php(高德联想 + 详细地址 + 坐标 + 城市 +
    导航链接,没配 key 也能手输);参与人改用全局弹框选人组件;
    角色输入框删除,saveNodeMembers 改成差集增删(否则老 role_tag 被静默抹掉)。
- ④「triplog 中的行程花销数据,没有同步过来,需要迁移过来」
  → 新增 _tooling/_fb_b17_backfill.php:迁地址(20 条)、花销(4 笔 ¥8584)、
    车辆(3 台)。线上已执行并核对(笔数与金额一分不差)。
- ④「行程中,时间明明具体到了时分秒,怎么不显示?」
  → Trip::nodeSpan():行程时间精确到时分(同一天只写一次日期、跨天写月日、
    两头都没时分就不写 00:00)。

其他:
- App\Geo 扩展 cityFromPoint / cityFromText / navUrl / jsConfig。
  中文地址提城市修掉四种错法(白名单→黑名单、取最后→取第一个、直辖市最先认、
  离线表只能兜底),并加了「五大连池 ≠ 大连」的位置守卫。
- config.example.php 加 amap 段(key / security_js / nav_src)。

验证:
- e2e 920/0(+29 条本批断言)· 升级 149/0(+15 条 v22 断言)·
  城市解析 32/32(新套件,样例全是 TripLog 线上真实地址)·
  桌面几何 428/0 · 移动端 688/0 · 交互 92/0 · 窄屏溢出 24/0。
- 反向验证 5/5:城市退回读 place / 只算到达地 / 时间退回只到日期 /
  打车也要车 / 类型不补 —— 五种坏法全部被断言抓住。
- 部署:服务器自检 19/19;线上 schema v22;补迁后花销 4 笔 ¥8584 与源库对齐。
This commit is contained in:
2026-10-05 16:45:10 +08:00
parent 2e51d3f5e4
commit c30ca99d22
13 changed files with 1158 additions and 235 deletions
+57 -1
View File
@@ -73,6 +73,15 @@ final class Schema
* (实际起止 / 参与人 / 备注),可无限次,供日后对比。
* 迁移:category='reality' → stage='actual','plan' → stage='plan'
* (按现有轨道自动迁,用户已确认)。
* 22 → **城市的聚合与地址(用户 2026-10-05 第 17 批)**:
* 用户第 1 条:「统计出来的不是目的地,也不是车站,而是城市…城市要聚合,
* 比如说有多段行程,都有青岛作为出发地或者目的地,那么就只记一次。」
* → 只靠 `trip_nodes.place` 一个自由文本字段是算不出"城市"的
* (那里填的是「青岛胶东国际机场」「溪水国家森林公园」这类**地点**)。
* 给 trip_nodes 补两列 `from_city` / `to_city`(出发地/到达地所属城市),
* 在**保存行程时**由 `App\Geo` 解析好存下来,读的时候直接去重。
* 为什么不每次读时现算:解析要调高德接口(有配额、有网络),
* 而城市是"写一次看很多次"的字段 —— 现算会让列表页变慢且可能超配额。
*/
/**
* 结构版本(**人读**用:日志、设置页上显示"库结构是哪一版")。
@@ -80,7 +89,7 @@ final class Schema
* ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。
* 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。
*/
public const VERSION = '21';
public const VERSION = '22';
/**
* 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。
@@ -1106,6 +1115,53 @@ SQL,
}
});
// ---- v24 第 17 批:城市的聚合与地址(Schema v22)----
// 用户第 1 条要的是**城市**("经过了几个城市"),而 `place` 里填的是
// 「青岛胶东国际机场」这种**地点名**,直接拿它去重会得到一堆机场/车站名。
// 用户第 4 条又点明「图片右侧显示的是聚合的城市,而不是终点地址」——
// 所以必须在**出发地 / 到达地**上各解析出一个城市,去重后再展示。
//
// ⚠️ 解析结果**落库**而不是每次读时现算:
// · 解析要调高德逆地理接口(有配额、可能失败),
// · 而城市是"写一次、看很多次"的字段(列表页、日历、首页都要用),
// 现算会让每次列表刷新都打好几次接口 —— 既慢又可能把配额烧光。
// 高德拿不到时 `App\Geo` 会从地址文本里提「XX市」兜底,实在提不出就留空
// (留空只是这个城市不计入,不影响页面)。
self::addColumn($db, 'trip_nodes', 'from_city',
"ADD COLUMN `from_city` VARCHAR(60) DEFAULT NULL COMMENT '出发地所属城市(聚合统计用;由 App\\\\Geo 解析)' AFTER `to_place`");
self::addColumn($db, 'trip_nodes', 'to_city',
"ADD COLUMN `to_city` VARCHAR(60) DEFAULT NULL COMMENT '到达地所属城市(聚合统计用;由 App\\\\Geo 解析)' AFTER `from_city`");
// 地址的**详细地址**也一并存下来(TripLog 同款):用户第 3 条要
// 「出发地和到达地,都用高德地图的接口,记录详细地址」——
// 光有城市不够,导航要用的是详细地址(或坐标)。
self::addColumn($db, 'trip_nodes', 'from_addr',
"ADD COLUMN `from_addr` VARCHAR(300) DEFAULT NULL COMMENT '出发地详细地址(高德返回)' AFTER `to_city`");
self::addColumn($db, 'trip_nodes', 'to_addr',
"ADD COLUMN `to_addr` VARCHAR(300) DEFAULT NULL COMMENT '到达地详细地址(高德返回)' AFTER `from_addr`");
self::addColumn($db, 'trip_nodes', 'from_lng',
"ADD COLUMN `from_lng` DECIMAL(10,6) DEFAULT NULL COMMENT '出发地经度(高德返回,用于导航链接)' AFTER `to_addr`");
self::addColumn($db, 'trip_nodes', 'from_lat',
"ADD COLUMN `from_lat` DECIMAL(10,6) DEFAULT NULL COMMENT '出发地纬度' AFTER `from_lng`");
self::addColumn($db, 'trip_nodes', 'to_lng',
"ADD COLUMN `to_lng` DECIMAL(10,6) DEFAULT NULL COMMENT '到达地经度' AFTER `from_lat`");
self::addColumn($db, 'trip_nodes', 'to_lat',
"ADD COLUMN `to_lat` DECIMAL(10,6) DEFAULT NULL COMMENT '到达地纬度' AFTER `to_lng`");
// ⚠️ `seat_info` / `gate` **保留列、不再使用**(用户第 3 条:
// 「飞机、铁路,只记录车次、航班号就可以,其他的没法记录,
// 因为可能是多个人同行,座位什么的不记录了」)。
// 不删列的理由:删列不可逆,而 TripLog 迁来的老数据里这两个字段
// 是有真实内容的(本人当时逐条填过);留着它们在库里至少可查。
// 界面上彻底不再出现 —— 见 node_form.php / show.php。
// 类型扩充(用户第 2 条:「类型不够丰富,你看看 triplog 里面都有哪些,都拿过来」)。
// TripLog 线上实际有 7 种(含两个自定义的),本地只有 5 种内置,差的就是这些。
// ⚠️ 只**补**不删:已有的(用户可能改过名)一律保留。
self::tryStep('补齐 TripLog 那几种节点类型', static function () use ($db) {
foreach ($db->query('SELECT id FROM families')->fetchAll() as $fam) {
\App\NodeType::seedFromTripLog((int)$fam['id']);
}
});
// ---- v24 第 13 批:旅程模块重构(Schema v19)----
// ① 给 trip_nodes 补三列(老库建表语句不会更新已存在的表,必须显式加)
self::addColumn($db, 'trip_nodes', 'stage',