org.guard.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import { inject } from '@angular/core';
  2. import { CanActivateFn, Router } from '@angular/router';
  3. import { FmodeApi } from './api/fmode-api';
  4. import { SessionService } from './session.service';
  5. /** 无组织用户停留的等待页路径 */
  6. export const PENDING_PATH = '/pending';
  7. /**
  8. * 组织归属守卫(2026-11-10)
  9. *
  10. * ## 为什么要它
  11. *
  12. * App 原本只判「登录了没」,不判「有没有组织」—— 没有组织的账号
  13. * 照常进入,看到的是一堆空列表,用户分不清是没权限还是真没数据。
  14. *
  15. * ## 🔴 为什么不能只看 `me.companyId`
  16. *
  17. * 实测(2026-11-10):新用户注册时,系统会**自动创建一个以他命名的组织**
  18. * (如 liujun 的组织就叫「liujun」),并写入 `enterpriseTrialClaimedAt`。
  19. * 所以「有 companyId」≠「被组织接纳」。
  20. *
  21. * 真正的判定在服务端(`fmodeagent-users.js` 的 `opMe`):
  22. * ① 组织带 `enterpriseTrialClaimedAt`(个人试用组织)
  23. * ② 且该组织里只有他一个人
  24. * → 两项都满足,视为**无组织**。
  25. *
  26. * ## 与 `authGuard` 的关系
  27. *
  28. * 必须排在 `authGuard` 之后(`canActivate: [authGuard, orgGuard]`)。
  29. * `/login` 与 `/pending` 自身**不得**挂本守卫,否则自我重定向死循环。
  30. */
  31. export const orgGuard: CanActivateFn = async (_route, state) => {
  32. const session = inject(SessionService);
  33. const api = inject(FmodeApi);
  34. const router = inject(Router);
  35. // 未登录交给 authGuard,这里不重复判
  36. if (!session.isLoggedIn()) return true;
  37. const hasOrg = await queryHasOrg(api);
  38. if (hasOrg) return true;
  39. return router.createUrlTree([PENDING_PATH], { queryParams: { redirect: state.url } });
  40. };
  41. /**
  42. * 询问服务端「我有没有真正的组织」。
  43. *
  44. * 🔴 查询失败按**放行**处理:判成无组织会让所有人卡在等待页,风险更大;
  45. * 放行后页面自身的错误态会如实提示。
  46. */
  47. export async function queryHasOrg(api: FmodeApi): Promise<boolean> {
  48. try {
  49. const m = await api.fetchOrgMembership();
  50. return m.hasOrg;
  51. } catch {
  52. return true;
  53. }
  54. }