chore(tenant): 租户域名改为默认关闭(客户决定先不做)—— 能力保留,关得干净
客户:先不做多租户了。
处理:fill.tenant-domain.enabled 默认 true → **false**,代码全部保留。
理由:
· 这个能力是隔离的:关掉后 TenantDomainResolver 一律返回 null,
登录走老路径、AuthInterceptor 那段校验根本不触发 —— 不影响任何既有行为
· 它已经验过(§一·二十五 16/16),删掉等于扔掉一份验过、将来要用的东西
· 要真用上它,前提是有正式域名 + 泛域名解析 + 泛域名证书;这些没定之前
开它只会让人以为"域名能区分单位了",实际并不能。等定了改一个配置 + 重启即可。
「关得干净」是本轮的验收点:E2E 改成**两种模式都能跑**,按当前模式选断言。
现在跑的是关闭态这一节(6 条):
✓ t1.localhost + 不填租户码 → 40012(域名不参与判断)
✓ 同一域名 + 填租户码 → 照样能登;主域名 + 租户码 → 照样能登
✓ 主域名 + 不填租户码 → 仍是 40012(跨单位重名的原行为)
✓ 关掉之后不引入任何新校验:带 t1 的 token 访问 t14 域名照常通过
脚本自己探测模式(免租户码登录成功=开、40012=关),所以开着时跑它会跑那 16 条,
不会因为模式不对而假绿。
将来开启:填 base → 配泛域名解析+证书 → XT_TENANT_DOMAIN_ENABLED=true → 重启 → 跑 E2E。
两条硬约束不变:微信授权回调域名只能配一个(回调统一落主域名再按 scene 跳回)、
小程序域名固定白名单(小程序端永远靠 scene)。
随之暂缓:§一·二十五 的三条待拍板(正式域名 / 手填租户码 / 小程序确认)现在都不用追了;
PRD 与 03-接口设计的补文档也等真要做时再写(现在写进去只会多一条"设计了但没启用")。
全量 666 条断言、0 失败(租户域名 16 → 6,因为跑的是关闭态那节)。
This commit is contained in:
@@ -138,14 +138,20 @@ fill:
|
||||
# 代码里还会再判一次 profile 不含 prod(双保险)。
|
||||
stub-enabled: ${WX_H5_STUB:true}
|
||||
|
||||
# ── 租户域名(用子域前缀区分租户)──────────────────────────────────────
|
||||
# ── 租户域名(用子域前缀区分租户)—— 客户 2026-09-29 决定**先不做**,默认关闭 ──
|
||||
#
|
||||
# 形态:t{tenantId}.{base},如 t1.example.com / t2.example.com。
|
||||
# 前缀用的是 **tenantId**,与扫码 scene 的 t{tid} 是同一套约定(scene 早就是 t1-k17)。
|
||||
#
|
||||
# ⚠️ **默认关闭**:单域名部署是合法形态,而且这个能力要真正用上,
|
||||
# 前提是**有正式域名 + 泛域名解析 + 泛域名证书**(`*.example.com`)——
|
||||
# 这些没定之前开它只会让人以为"域名能区分单位了",实际并不能。
|
||||
# 客户说先不做,所以这里是 false;**能力本身保留且已验过**
|
||||
# (e2e-m1-tenant-domain.py 16/16,见 §一·二十五),
|
||||
# 将来只需:填 base、配泛域名证书、把这里改成 true、重启。
|
||||
#
|
||||
# 打开之后:
|
||||
# · PC 登录在 t1.xxx.com 上**不用再填租户码** —— 域名已经回答了"你是哪个单位的",
|
||||
# 那句「该账号存在于多个单位,请填写租户码」在这条路径下不会再出现
|
||||
# · PC 登录在 t1.xxx.com 上**不用再填租户码** —— 域名已经回答了"你是哪个单位的"
|
||||
# · H5 微信授权回调仍落在主域名(微信的"授权回调页面域名"只能配一个),
|
||||
# 回调再按 scene 里的 t{tid} 跳回该单位的站点
|
||||
#
|
||||
@@ -154,9 +160,8 @@ fill:
|
||||
# 就能读 t2 的数据,而界面看起来一切正常。
|
||||
# ⚠️ 小程序端**用不了它**:微信的 request 合法域名是固定白名单,配不了 *.xxx.com,
|
||||
# 所以小程序继续靠 scene 里的 t{tid}。
|
||||
# ⚠️ 生产要配**泛域名解析 + 泛域名证书**(*.example.com)。
|
||||
tenant-domain:
|
||||
enabled: ${XT_TENANT_DOMAIN_ENABLED:true}
|
||||
enabled: ${XT_TENANT_DOMAIN_ENABLED:false}
|
||||
base: ${XT_TENANT_DOMAIN_BASE:localhost}
|
||||
scheme: ${XT_TENANT_DOMAIN_SCHEME:http}
|
||||
|
||||
|
||||
@@ -2060,3 +2060,66 @@ t1 域名免租户码登录 / 与手填租户码矛盾时明确报错 / **跨租
|
||||
3. 小程序端确认继续靠 scene(上面第 2 条硬约束)。
|
||||
4. **补文档**:这条新需求目前只落在代码与本节,应补进 `数据信息填报系统PRD.md`
|
||||
与 `03-接口设计.md`(登录段的"租户从哪来"要多一种来源)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## §一·二十六 租户域名:客户决定**先不做** —— 能力保留、默认关闭
|
||||
|
||||
> 客户原话:**先不做多租户了。**
|
||||
|
||||
### 怎么处理的:关掉,但**不删**
|
||||
|
||||
`fill.tenant-domain.enabled` 默认值从 `true` 改成 **`false`**(`application-dev.yml`)。
|
||||
**代码全部保留**,理由:
|
||||
|
||||
- 这个能力是**隔离的**:关掉之后 `TenantDomainResolver` 一律返回 `null`,
|
||||
登录走老路径、`AuthInterceptor` 里那段校验根本不会触发 ——
|
||||
**不影响任何既有行为**(下面有断言专守这一点)。
|
||||
- 它已经**验过**(§一·二十五 的 16/16),删掉等于把一份验过的、将来要用的东西扔掉。
|
||||
- 要真正用上它,前提是**有正式域名 + 泛域名解析 + 泛域名证书** ——
|
||||
这些没定之前开它只会让人以为"域名能区分单位了",实际并不能。
|
||||
所以"先不做"是对的;等域名定了,**改一个配置 + 重启**就能用。
|
||||
|
||||
### 「关得干净」是这一轮的验收点
|
||||
|
||||
关一个功能键,最怕的是**关不干净**(悄悄留下一点新行为)。
|
||||
所以 `e2e-m1-tenant-domain.py` 改成**两种模式都能跑**,并按当前模式选断言:
|
||||
|
||||
```
|
||||
[关闭态] 域名不再提供租户身份,老路径完全不变 ← 当前跑的就是这一节
|
||||
✓ t1.localhost + 不填租户码 → 40012(域名不参与判断)
|
||||
✓ 同一域名 + 填租户码 → 照样能登
|
||||
✓ 主域名 + 租户码 → 照样能登
|
||||
✓ 主域名 + 不填租户码 → 仍是 40012(跨单位重名的原行为)
|
||||
✓ 关掉之后**不引入任何新校验**:带 t1 的 token 访问 t14 域名照常通过
|
||||
(这是关闭前的既有行为;开启态下会被 403 拒掉,见 run_on)
|
||||
```
|
||||
|
||||
脚本自己探测模式(用 `t1.localhost` 免租户码试登录:成功=开、`40012`=关),
|
||||
所以**开着的时候跑它就会跑那 16 条**,不会因为模式不对而假绿。
|
||||
|
||||
**全量 666 条断言、0 失败。**
|
||||
|
||||
### 将来要开,需要四步
|
||||
|
||||
1. 定下正式主域名,填 `XT_TENANT_DOMAIN_BASE`(如 `example.com`)
|
||||
2. 配**泛域名解析**与**泛域名证书** `*.example.com`(备案一个主域名即覆盖子域)
|
||||
3. 把 `XT_TENANT_DOMAIN_ENABLED` 设为 `true`
|
||||
4. 重启后跑 `python scripts/e2e-m1-tenant-domain.py`(它会自动切到开启态的 16 条断言)
|
||||
|
||||
⚠️ 别忘了两条硬约束(详见 §一·二十五):**微信授权回调域名只能配一个**
|
||||
(所以回调统一落主域名、再按 scene 跳回)、**小程序域名是固定白名单**
|
||||
(所以小程序端永远靠 scene,不受这个能力影响)。
|
||||
|
||||
### 随之暂缓的待办
|
||||
|
||||
§一·二十五 里那三条待拍板(正式域名 / 要不要保留手填租户码 / 小程序确认),
|
||||
**现在都不用追了** —— 它们只在开启这个能力时才有意义。
|
||||
`PRD` 与 `03-接口设计` 的补文档同理:**等真要做时再补**,
|
||||
现在写进去反而会让文档里多一条"设计了但没启用"的东西。
|
||||
|
||||
### 下一步
|
||||
|
||||
回到 §一·二十二 的顺序:**第 4 步 = 审核闭环(后端)** ——
|
||||
初审 / 终审 / 撤回 / 批量通过(只带无问题项)/ 驳回(逐条填原因)。
|
||||
|
||||
@@ -94,7 +94,7 @@ def login(host: str, tenant_code: str | None = None, account: str = "admin",
|
||||
return call("POST", "/api/v1/auth/login", host, body=body)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
def run_on() -> int:
|
||||
ap = argparse.ArgumentParser(description="租户域名 E2E")
|
||||
ap.add_argument("--password", default="Admin@123")
|
||||
args = ap.parse_args()
|
||||
@@ -174,5 +174,69 @@ def main() -> int:
|
||||
return 1 if FAILED else 0
|
||||
|
||||
|
||||
def detect_mode() -> str:
|
||||
"""
|
||||
探测后端有没有启用租户域名:用 t1.localhost 且不填租户码试一次登录。
|
||||
|
||||
为什么要探测而不是写死:客户 2026-09-29 决定"先不做",默认已关;
|
||||
但这个能力保留着(填个域名就能开)。脚本两种模式都要能跑,
|
||||
而且**两种模式的断言是不同的** —— 关闭态要验的是"行为与从前完全一致"。
|
||||
"""
|
||||
r = login("t1.localhost", None)
|
||||
if r.get("code") == 0:
|
||||
return "ON"
|
||||
if r.get("code") == 40012:
|
||||
return "OFF"
|
||||
raise SystemExit(f" ✗ 探测失败(既不是 0 也不是 40012):{r}")
|
||||
|
||||
|
||||
def run_off() -> int:
|
||||
"""
|
||||
关闭态(当前默认):验"关掉之后,行为与从前**完全一致**"。
|
||||
|
||||
这一节看着简单,但它是"先不做"这件事的验收 ——
|
||||
关一个功能键,最怕的是关不干净(悄悄留下一点新行为)。
|
||||
"""
|
||||
print("=" * 76)
|
||||
print("租户域名 E2E —— 当前是**关闭态**(客户先不做,能力保留)")
|
||||
print("=" * 76)
|
||||
|
||||
print("\n[关闭态] 域名不再提供租户身份,老路径完全不变")
|
||||
a = login("t1.localhost", None)
|
||||
check("t1.localhost + 不填租户码 → 40012(域名不参与判断)",
|
||||
a.get("code") == 40012, f"{a}")
|
||||
b = login("t1.localhost", "demo")
|
||||
check("同一域名 + 填租户码 → 照样能登(老路径不受影响)", b.get("code") == 0, f"{b}")
|
||||
c = login("localhost", "demo")
|
||||
check("主域名 + 租户码 → 照样能登", c.get("code") == 0, f"{c}")
|
||||
d = login("localhost", None)
|
||||
check("主域名 + 不填租户码 → 仍是 40012(跨单位重名的原行为)",
|
||||
d.get("code") == 40012, f"{d}")
|
||||
|
||||
token = (b.get("data") or {}).get("token")
|
||||
e = call("GET", "/api/v1/users?pageNum=1&pageSize=1", "t14.localhost", token)
|
||||
check("关掉之后**不引入任何新校验**:带着 t1 的 token 访问 t14 域名照常通过",
|
||||
e.get("code") == 0,
|
||||
f"{e}(这是关闭前的既有行为;开启态下会被 403 拒掉,见 run_on)")
|
||||
|
||||
ok("要验开启态:以 XT_TENANT_DOMAIN_ENABLED=true 重启后端,再跑本脚本")
|
||||
|
||||
print()
|
||||
print("=" * 76)
|
||||
print(f"通过 {len(PASSED)} 失败 {len(FAILED)} 共 {len(PASSED) + len(FAILED)}")
|
||||
if FAILED:
|
||||
for n, det in FAILED:
|
||||
print(f" · {n}\n {det}")
|
||||
print("=" * 76)
|
||||
return 1 if FAILED else 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
mode = detect_mode()
|
||||
if mode == "ON":
|
||||
return run_on()
|
||||
return run_off()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user