v41 第 41 批:流水线提速 + 12 项界面/交互开发 + DAV 彻底修复

DAV(用户「务必彻底解决」):
- 真根因是 home-set 层级错:CalDav::HOME_PATH 原为 /dav/calendars/、
  CardDav 原为 /carddav/addressbooks/,而日历本/通讯录的 href 在
  /dav/N.ics/ 与 /carddav/N.vcf/ —— home-set 下面一本都没有。
  Thunderbird 按 home-set 前缀认成员,于是「找不到日历本 / 找不到联系人」。
  改为 /dav/ 与 /carddav/(所有既有 URL 一字不变,老配置不作废)。
- 又发现并修掉 Depth:0 违反 RFC 4918:原实现无论 depth 都列出全部子集合,
  且第一条 response 是日历本(不含 current-user-principal)。改为按 depth 分流,
  Depth:0 只回资源自身、Depth:1 才列出子集合。

界面与交互:
- 观影:新建影院弹窗层级修正 + 详细地址;照片左右按钮悬停不再位移;
  弹框底栏改「确定 / 取消」。
- 旅程:新建旅程改弹窗并按观影式排版;详情页四个动作按钮按用户指定顺序与配色;
  编辑与新增行程弹窗化(单容器 + AJAX);行程时间控件改 AntD(精确到分);
  参与人默认带上自己;车次号/车牌号紧跟交通方式词。
- 资源:新建/编辑改弹窗 + 观影式排版 + 新建时即定共享人。
- 纪念日:新建改弹框 + 观影式排版。

验证:
- e2e 1510/0;v41 十二项真机 48/0;DAV 反向验证 8/0;
  部署断言空跑 337/0;模板 div 深度三形态穷举 33 组全配平。
- 线上 DAV 终验(--resolve 真实入口 + 真 DAV 口令)12/0;
  线上确认 Depth:0 恰好 1 个 response。
This commit is contained in:
2026-10-11 10:12:11 +08:00
parent f9e30bd7d8
commit 7544fb9f6a
19 changed files with 1616 additions and 342 deletions
+28 -2
View File
@@ -117,12 +117,38 @@ final class CalDav
*/
public const PRINCIPAL_PATH = '/dav/principal/';
public const HOME_PATH = '/dav/calendars/';
/**
* 🔴🔴 v41(用户 2026-10-10 第 11 条:「caldav 和 carddav 功能还是不好用,
* thunderbird 返回的结果是,找不到联系人,找不到日历本,再仔细检查这个功能,
* 这次要务必彻底解决。」)
*
* 真根因(线上实测出来的,不是猜的):
* 原来 `HOME_PATH = '/dav/calendars/'`,而**日历本自己的 href 是 `/dav/1.ics/`**
* —— 也就是说 **日历本并不在 home-set 之下**。
* Thunderbird(严格按 RFC 4791/6352):拿到 `calendar-home-set` 后,
* PROPFIND 它、**只看它的成员**当作日历本 ⇒ 那里一个都没有
* ⇒ 报「找不到日历本」。CardDAV 侧一模一样(`/carddav/addressbooks/`
* vs 通讯录 `/carddav/1.vcf/`)⇒ 「找不到联系人」。
*
* ⚠️ 为什么 v33 那批没抓出来:那批的探针**只验证"发现链每一步都回 207"**,
* 而每一步确实都回了 207、principal 与 home-set 也都拿到了 ——
* **层级关系错了这件事,单个请求的返回码看不出来**。
* ⇒ 新探针必须断言"**home-set 的成员里必须有日历本/通讯录**"。
*
* 修法:让 home-set 就指向**本协议自己的根**(`/dav/`、`/carddav/`)——
* ① 成员关系立刻成立(`/dav/1.ics/` 确实在 `/dav/` 之下);
* ② **所有既有 URL 一字不变**(客户端里配的 `/dav/`、`/dav/1.ics/` 继续有效,
* 不需要用户重新配置,也不会让已同步的客户端全部失效);
* ③ 少一层跳转。
* ⚠️ 别改成"把日历本挪到 /dav/calendars/ 下面"——那会让**每一个用户**的
* 客户端配置作废(已订阅的日历会 404),代价大得多。
*/
public const HOME_PATH = '/dav/';
/** 通讯录那边同义(见 App\CardDav,两边刻意保持一致) */
public const CARD_PRINCIPAL_PATH = '/carddav/principal/';
public const CARD_HOME_PATH = '/carddav/addressbooks/';
public const CARD_HOME_PATH = '/carddav/';
// ---------------- 应答 ----------------