Commit Graph

3 Commits

Author SHA1 Message Date
lovife 9f6d26c650 fix(e2e,pc): 不再产生跨租户同名的登录账号
账号唯一性是 (tenant_id, username),跨租户重名本身是合法行为(登录靠租户码区分);但默认值与 e2e 残留会让它变成默认状态:不带租户码登录会撞上 40012(该账号存在于多个单位),平台侧看账号也分不清哪个 admin 属于哪所学校。三处一起收口 ——

1) scripts/e2e-m0.py 跑完不再留租户:以前每跑一次就留下一个 e2e###### 租户 + 里面叫 admin 的管理员 + demo 下 3 个测试组织,库里那两个孤儿租户就是这么来的。现在按本次跑出的 id 自清(崩溃路径也兜住),不越界成 LIKE 'e2e%' 全清。

2) scripts/e2e-m1-tenant-domain.py 自己造、自己清「另一个租户里也有 admin」的夹具:它原本靠第 1 条的残留凑出 40012 这个前提,残留一被清掉就会静默反转成 8 条 FAIL。顺带修掉同族的第二处隐式依赖 —— 它假定 t14.localhost 是个真实租户,而那个租户早就不存在了。

3) PC 端「开通租户」的登录账号默认值不再写死 admin,改为跟随租户编码生成 admin_<编码>;运营一旦手改就不再被编码覆盖。

验证:全量 python scripts/verify-all.py --with-e2e 通过(37 脚本 / 1597 断言 / 0 失败),跑完库里仍只有 1 个租户、0 处跨租户重名;PC 端 pnpm typecheck 0 错、pnpm lint 干净、check-wiring 未接线 0 条,浏览器实演默认值跟随与手改不回退。
2026-10-13 10:21:40 +08:00
lovife e6cd1049d4 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,因为跑的是关闭态那节)。
2026-09-29 21:42:00 +08:00
lovife d05cec0a02 feat(tenant): 租户域名 —— 用子域前缀判断"这是哪个单位的站点"(t1.xxx.com / t2.xxx.com)
客户新提:不同的租户采用不同的地址前缀来获取租户ID。

一、它解决什么
   在此之前"这次是哪个单位"都要人填或扫:PC 登录要手填租户码(跨单位重名时报 40012
   「请填写租户码」,而用户根本不知道该填什么)、H5 与小程序靠扫码 scene。
   而域名本身就是"我是谁"最自然的答案 —— 在 t1.xxx.com 上打开页面时,
   系统不该再问"你是哪个单位的"。
   前缀用 t{tenantId},与扫码 scene 的 t{tid} **同一套约定**(scene 早就是 t1-k17),
   不引入第二套编号。

二、★ 唯一必须守住的规则:Host 定位,token 授权
   未登录 → Host 决定"去哪个单位里找这个人"
   已登录 → 一律以 token 里的 tenantId 为准,且 Host 不一致就拒绝
   不校验后者的话:在 t1.xxx.com 上拿 t2 的 token 就能读 t2 的数据 ——
   而界面一切正常(前端本来就会带 token),是个安静且极难发现的越权。
   所以 AuthInterceptor 里那段判断比"登录免填租户码"重要得多,E2E 专门守着它。

三、落地
   FillProperties.TenantDomain(enabled/base/scheme)  默认关,siteOf(tid) 拼站点地址
   support/TenantDomainResolver                      解析不出来返回 null(不是错误)
   TenantMapper.selectActiveById                     停用/已删的租户不解析
   UserMapper.selectByTenantIdAndUsername            域名路径按 id 找用户
   AuthService.locateUser                            域名优先;手填租户码与域名矛盾 → 明确报错
   AuthInterceptor                                   Host 与 token 租户不一致 → 403
   MpH5AuthController.redirectToApp                  按 scene 的 t{tid} 跳回该单位站点
   application-dev.yml                               enabled/base=localhost/scheme=http

   null 的语义很重要:解析不出来 ≠ 出错,而是"这是统一入口/单域名部署",回退老路径。
   所以单域名部署仍然完全合法(enabled=false 即可)。

四、两条来自微信的硬约束(决定架构,不是可选)
   1. 公众号网页授权的「授权回调页面域名」**只能配一个** → 每租户一个子域时,
      回调统一落在主域名,再按 scene 里的 t{tid} 302 回该单位站点。
      正好复用既有的 state(scene 随 state 走)+ token 放 fragment 的机制。
   2. 小程序的 request 合法域名是**固定白名单**,配不了 *.xxx.com →
      小程序端用不了域名,继续靠 scene 里的 t{tid}。这点客户需要知道。

五、本地开发的坑(已处理)
   开发期 base 是 localhost,真按它回跳会把手机上的学生从穿透域名扔到 t1.localhost ——
   打不开,且是浏览器级失败,看不出是我们跳错了。所以回跳加了本机地址判断
   (localhost/127.0.0.1/192.168.*/10.*/*.local),本机 base 一律回退到 h5-base-url。

六、E2E:scripts/e2e-m1-tenant-domain.py → 16/16
   [1] t1.localhost + 不填租户码 → 登录成功(对照:不带域名 → 40012);
       带端口/大小写/多一层子域都能解析
   [2] 域名与手填租户码矛盾 → 明确报错;一致则正常
   [3] ★ 拿 t1 的 token 访问 t14 域名 → 403,提示可理解
   [4] 边界:x1.localhost / t999999.localhost / example.com → 都回退老路径
   用 http.client 伪造 Host 头(urllib 会按 URL 自己算 Host,覆盖不掉)。

七、全量 676 条断言、0 失败(新增 16);verify-docs 19 项通过

八、⏳ 需要拍板的三件事(已写进交接摘要 §一·二十五)
   1. 正式域名(base)+ 泛域名解析 + 泛域名证书 *.xxx.com(备案一个主域名即可覆盖子域)
   2. 要不要保留"手填租户码"(我保留了:统一入口、老客户端、运维排查都需要)
   3. 小程序端确认继续靠 scene
   另:这条新需求应补进 PRD 与 03-接口设计(目前只落在代码与本摘要里)——下一轮补。
2026-09-29 21:37:11 +08:00