docs(handoff): 补上 §一·二十五(租户域名)—— 上一版幂等判断被正文引用误判,整节没写进去

上一版脚本用 "§一·二十五" in text 当"已存在就跳过"的判断,而 §一·二十四 的结尾
正好有「见下一节(§一·二十五)」这句话 → 误判为已存在 → 整节没落进文档。

教训:幂等判断要认**标题**(## §一·二十五),不能认"整篇里有没有这个词";
并且写完要**自查一次**标题能否找到(这次加了)。

另:本次提交还顺带记录了一条同类教训 —— 在 PowerShell 里用 git commit -m "带中文引号的消息"
会被 shell 拆成多个 pathspec(这次就炸了)。本项目一直用消息文件,就是为此。
This commit is contained in:
2026-09-29 21:37:54 +08:00
parent d05cec0a02
commit 5b2f2c109d
+61
View File
@@ -1999,3 +1999,64 @@ input.dispatchEvent(new Event('change', { bubbles: true }))
> 但客户端提了一个**新的架构要求**(租户按域名前缀区分,如 `t1.xxx.com` / `t2.xxx.com`),
> 它会同时影响 PC 登录、H5 微信授权回调、小程序域名白名单与二维码 scene ——
> 见下一节(§一·二十五),那里记录了分析与待拍板的点。
---
## §一·二十五 租户域名:用子域前缀判断"这是哪个单位的站点"
> 客户原话:**不同的租户采用不同的地址前缀来获取租户ID,如 t1.xxx.com 和 t2.xxx.com**
> 落地提交:`d05cec0`(细节与完整论证在那条提交信息里,这里只留要点与待办)
### 它解决什么
在此之前"这次是哪个单位"**都要人填或扫**:PC 登录手填租户码(跨单位重名时报 `40012`
「请填写租户码」,而用户根本不知道该填什么)、H5 与小程序靠扫码 scene。
而域名本身就是"我是谁"最自然的答案 —— 在 `t1.xxx.com` 上打开页面时,不该再问。
前缀用 **`t{tenantId}`**,与扫码 scene 的 `t{tid}` 是**同一套约定**(scene 早就是 `t1-k17`)。
### ★ 唯一必须守住的规则:Host 定位,token 授权
```
未登录 → Host 决定"去哪个单位里找这个人" (定位)
已登录 → 一律以 token 里的 tenantId 为准 (授权)
并且:Host 与 token 的租户**不一致就拒绝**(403)
```
不校验后者,在 `t1.xxx.com` 上拿 t2 的 token 就能读 t2 的数据 ——
而**界面一切正常**(前端本来就会带 token),是个安静、极难被发现的越权。
### 两条来自微信的硬约束(决定架构,不是可选)
1. **公众号网页授权的「授权回调页面域名」只能配一个** → 每租户一个子域时,
回调**统一落在主域名**,再按 scene 里的 `t{tid}` 302 回该单位站点
(复用既有的 state 走一圈 + token 放 fragment 的机制)。
2. **小程序的 request 合法域名是固定白名单**,配不了 `*.xxx.com` →
**小程序端用不了域名**,继续靠 scene 里的 `t{tid}`。这点客户需要知道。
### 另一个已处理的坑
开发期 `base` 是 `localhost`,真按它回跳会把手机上的学生从穿透域名扔到
`t1.localhost`(打不开,且是浏览器级失败,看不出是我们跳错了)→
H5 回跳里加了本机地址判断,本机 base 一律回退到 `h5-base-url`。
### 语义:`null` 不是错误
解析不出来 = "这是统一入口/单域名部署",调用方回退老路径。
所以**单域名部署仍然完全合法**(`enabled: false`)。
### E2E:`scripts/e2e-m1-tenant-domain.py` → 16/16
t1 域名免租户码登录 / 与手填租户码矛盾时明确报错 / **跨租户 token → 403** /
四种边界回退(非 t 前缀、租户不存在、主域名、无关域名)。
用 `http.client` 伪造 `Host`(urllib 会按 URL 自己算 Host,覆盖不掉)。
全量 **676 条断言、0 失败**。
### ⏳ 需拍板 / 待办
1. **正式域名(base)** + 泛域名解析 + **泛域名证书 `*.xxx.com`**(备案一个主域名即覆盖子域)。
2. **要不要保留"手填租户码"**:我保留了(统一入口、老客户端、运维排查都需要)。
3. 小程序端确认继续靠 scene(上面第 2 条硬约束)。
4. **补文档**:这条新需求目前只落在代码与本节,应补进 `数据信息填报系统PRD.md`
与 `03-接口设计.md`(登录段的"租户从哪来"要多一种来源)。