# 2026-07-23 泰额版:平台 / 公司登录与拉菜单用法 本文说明泰额版 **平台管理员** 与 **公司账号** 应如何登录、如何拉菜单,以及完整传参 / 反参示例。 > 相关文档: > - `2026-07-23泰额版当前登录账号菜单接口.md` > - `2026-07-23泰额版平台端操作公司级账号逻辑.md` --- ## 一、先分清两套身份 | 角色 | 登录接口 | 用户所在库 | 拉菜单接口 | 菜单数据来源 | |------|----------|------------|------------|--------------| | **平台管理员** | `POST /api/app/th-web-auth/login`(选 **Default** + 平台邮箱)或 `POST /api/app/account/login` | 主库 `antis-foodlabeling-host`.`user` | `GET /api/app/account/Vue3Router/vben5`(H5 实际调用) | 主库 `menu`(`MenuSource=0`,后端 vben5 空时回退 Ruoyi) | | **公司账号** | `POST /api/app/th-web-auth/login`(选具体公司 tenantId) | 该公司业务库 `user` | `GET /api/app/auth-session/my-menus` 或 H5 侧 `Vue3Router/vben5` | **当前租户业务库** `menu` | **H5 同一登录框(2026-07-23 后端兼容):** - 前端**始终**打 `POST /api/app/th-web-auth/login`,不会打 `account/login`。 - 下拉选 **Default**(`tenantId=11111111-1111-1111-1111-111111111111`)+ 邮箱 `admin@example.com` → 后端在主库校验,签发**无 TenantId** 的平台 Token,`tenantName` 返回 `Platform`。 - 下拉选 **具体公司**(如「中国麦当劳公司」)→ 仍在该公司业务库校验,JWT 含 `TenantId`。 - Default 租户业务库可能指向 `antis-foodlabeling-us`;平台邮箱账号**不会**误入 US 库。 **禁止混用:** - 公司 Token 不要用平台-only 的管理接口(无租户上下文) - 平台 Token 调 `auth-session/my-menus` 现已可读主库菜单(兼容);H5 仍优先 `Vue3Router/vben5` Base URL 示例:`http://127.0.0.1:19002`(以部署为准)。 ```mermaid flowchart TB subgraph platform [平台管理员] P1[POST account/login] P2[GET account/Vue3Router/ruoyi] PHost[(antis-foodlabeling-host)] P1 --> PHost P2 --> PHost end subgraph company [公司账号] C1[POST th-web-auth/login] C2[GET auth-session/my-menus] CBiz[(租户业务库)] C1 --> CBiz C2 --> CBiz end ``` --- ## 二、平台管理员 ### 2.1 登录 | 项 | 值 | |----|------| | 方法 / 路径 | `POST /api/app/account/login` | | Content-Type | `application/json` | | 说明 | 校验主库用户;登录框按 **邮箱** 匹配(`Email` 优先,其次 `UserName`);值须含 `@` | #### 传参(`LoginInputVo`) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `userName` | string | 是 | **填邮箱**,例如种子账号 `admin@example.com`(字段名虽为 userName) | | `password` | string | 是 | 明文密码;种子默认 `123456` | | `uuid` | string | 视环境 | 验证码会话 Id;未开验证码可省略 | | `code` | string | 视环境 | 图形验证码;未开验证码可省略 | ```json { "userName": "admin@example.com", "password": "123456" } ``` #### 反参(`LoginOutputDto`) | 字段 | 类型 | 说明 | |------|------|------| | `token` | string | JWT(后续请求 `Authorization: Bearer {token}`) | | `refreshToken` | string | 刷新令牌 | ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "..." } ``` > 若全局包装了统一响应,实际可能是 `{ "data": { "token": "...", "refreshToken": "..." } }`,以 Swagger / 实际返回为准。 ```bash curl -X POST "http://127.0.0.1:19002/api/app/account/login" \ -H "Content-Type: application/json" \ -d "{\"userName\":\"admin@example.com\",\"password\":\"123456\"}" ``` ### 2.2 拉菜单(路由) | 项 | 值 | |----|------| | 方法 / 路径 | `GET /api/app/account/Vue3Router/ruoyi` | | Header | `Authorization: Bearer {token}` | | 说明 | 将当前用户菜单转为 **Ruoyi** 路由树。主库种子菜单 `MenuSource=0`,须用 **`ruoyi`**,不要用 `vben5`(否则按 `MenuSource=2` 过滤会空) | 路径参数 `routerType`: | 值 | 对应 MenuSource | 何时用 | |----|-----------------|--------| | `ruoyi` | 0 | **当前平台种子推荐** | | `pure` | 1 | Pure 前端 | | `vben5` | 2 | 仅当菜单已标成 Vben5 时 | #### 传参 无 Query Body;路径上带 `ruoyi` 即可。 #### 反参 返回 **前端路由结构数组/对象**(由 `Vue3RuoYiRouterBuild` 生成),不是 `auth-session/my-menus` 那种 `menus` 树。字段随框架版本可能含:`name`、`path`、`component`、`meta`、`children` 等。以实际 JSON / Swagger 为准。 超级管理员(用户名 `admin`)会拿到对应 `MenuSource` 下全部未删菜单再构建路由。 ```bash curl -G "http://127.0.0.1:19002/api/app/account/Vue3Router/ruoyi" \ -H "Authorization: Bearer " ``` ### 2.3 平台菜单数据位置 | 库表 | 说明 | |------|------| | `antis-foodlabeling-host`.`menu` | 约 26 条:首页概览、平台管理/SAAS 公司、标签管理…、管理/账户管理… | | `user` / `role` / `rolemenu` | 平台 `admin` 已绑定上述菜单 | 种子脚本:`th-tenant-menu-seed.sql`(menu)、`th-platform-user-role-seed.sql`(user/role)。菜单 Id 均为合法 Guid(见下文「平台应有菜单清单」)。 --- ## 七、平台应有菜单清单 主库 `antis-foodlabeling-host`.`menu` 共 **26** 条未删食品 SaaS 菜单,`MenuSource=0`(Ruoyi)。根菜单 `ParentId='0'`;子菜单 `ParentId` 为父目录 Guid。 | 菜单名称 | Id | router | routerName | component | menuType | parentId | orderNum | menuIcon | |---|---|---|---|---:|---|---:|---:|---| | 首页概览 | `f0010001-0001-4000-8000-000000000001` | `/analytics` | `FoodLabelingDashboard` | `/food-labeling/dashboard/index` | 1 | `0` | -1 | `lucide:layout-dashboard` | | 平台管理 | `f0010002-0001-4000-8000-000000000002` | `/platform` | `FoodLabelingPlatform` | 空 | 0 | `0` | 5 | `lucide:cloud-cog` | | SAAS 公司 | `f0010003-0001-4000-8000-000000000003` | `/platform/tenants` | `FoodLabelingPlatformTenants` | `/food-labeling/platform/tenants/index` | 1 | `f0010002-0001-4000-8000-000000000002` | 6 | `lucide:building` | | 标签管理 | `f0010010-0001-4000-8000-000000000010` | `/labeling` | `FoodLabelingLabeling` | 空 | 0 | `0` | 10 | `lucide:tags` | | 标签 | `f0010011-0001-4000-8000-000000000011` | `/labels` | `FoodLabelingLabels` | `/food-labeling/labeling/labels/index` | 1 | `f0010010-0001-4000-8000-000000000010` | 11 | `lucide:tag` | | 标签分类 | `f0010012-0001-4000-8000-000000000012` | `/label-categories` | `FoodLabelingLabelCategories` | `/food-labeling/labeling/label-categories/index` | 1 | `f0010010-0001-4000-8000-000000000010` | 12 | `lucide:folder-tree` | | 标签类型 | `f0010013-0001-4000-8000-000000000013` | `/label-types` | `FoodLabelingLabelTypes` | `/food-labeling/labeling/label-types/index` | 1 | `f0010010-0001-4000-8000-000000000010` | 13 | `lucide:layers` | | 标签模板 | `f0010014-0001-4000-8000-000000000014` | `/label-templates` | `FoodLabelingLabelTemplates` | `/food-labeling/labeling/label-templates/index` | 1 | `f0010010-0001-4000-8000-000000000010` | 14 | `lucide:layout-template` | | 多选选项集 | `f0010015-0001-4000-8000-000000000015` | `/multiple-options` | `FoodLabelingMultipleOptions` | `/food-labeling/labeling/multiple-options/index` | 1 | `f0010010-0001-4000-8000-000000000010` | 15 | `lucide:list-checks` | | 业务模块 | `f0010020-0001-4000-8000-000000000020` | `/modules` | `FoodLabelingModules` | 空 | 0 | `0` | 15 | `lucide:boxes` | | 培训 | `f0010021-0001-4000-8000-000000000021` | `/training` | `FoodLabelingTraining` | `/food-labeling/modules/training/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 16 | `lucide:graduation-cap` | | 告警 | `f0010022-0001-4000-8000-000000000022` | `/alerts` | `FoodLabelingAlerts` | `/food-labeling/modules/alerts/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 17 | `lucide:bell` | | 任务 | `f0010023-0001-4000-8000-000000000023` | `/tasks` | `FoodLabelingTasks` | `/food-labeling/modules/tasks/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 18 | `lucide:list-todo` | | 传感器 | `f0010024-0001-4000-8000-000000000024` | `/sensors` | `FoodLabelingSensors` | `/food-labeling/modules/sensors/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 19 | `lucide:activity` | | 食物浪费 | `f0010025-0001-4000-8000-000000000025` | `/food-waste` | `FoodLabelingFoodWaste` | `/food-labeling/modules/food-waste/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 20 | `lucide:apple` | | 电子标签 | `f0010026-0001-4000-8000-000000000026` | `/e-label-module` | `FoodLabelingELabelModule` | `/food-labeling/modules/e-label/index` | 1 | `f0010020-0001-4000-8000-000000000020` | 21 | `lucide:file-digit` | | 管理 | `f0010030-0001-4000-8000-000000000030` | `/management` | `FoodLabelingManagement` | 空 | 0 | `0` | 20 | `lucide:building-2` | | 账户管理 | `f0010031-0001-4000-8000-000000000031` | `/account-management` | `FoodLabelingAccountManagement` | `/food-labeling/management/account-management/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 21 | `lucide:users` | | 系统菜单 | `f0010032-0001-4000-8000-000000000032` | `/system-menu` | `FoodLabelingSystemMenu` | `/food-labeling/management/system-menu/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 22 | `lucide:menu-square` | | 菜单管理 | `f0010033-0001-4000-8000-000000000033` | `/menu-management` | `FoodLabelingMenuManagement` | `/food-labeling/management/menu-management/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 23 | `lucide:utensils` | | 设备 | `f0010034-0001-4000-8000-000000000034` | `/devices` | `FoodLabelingDevices` | `/food-labeling/modules/devices/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 24 | `lucide:smartphone` | | 报表 | `f0010035-0001-4000-8000-000000000035` | `/reports` | `FoodLabelingReports` | `/food-labeling/management/reports/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 25 | `lucide:file-bar-chart` | | 发票 | `f0010036-0001-4000-8000-000000000036` | `/invoices` | `FoodLabelingInvoices` | `/food-labeling/modules/invoices/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 26 | `lucide:receipt` | | 二维码 | `f0010037-0001-4000-8000-000000000037` | `/qr-codes` | `FoodLabelingQrCodes` | `/food-labeling/modules/qr-codes/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 27 | `lucide:qr-code` | | 支持 | `f0010038-0001-4000-8000-000000000038` | `/support` | `FoodLabelingSupport` | `/food-labeling/management/support/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 28 | `lucide:life-buoy` | | API | `f0010039-0001-4000-8000-000000000039` | `/api-settings` | `FoodLabelingApi` | `/food-labeling/modules/api/index` | 1 | `f0010030-0001-4000-8000-000000000030` | 29 | `lucide:code-2` | 平台 admin 账号:`UserName=admin`,`Email=admin@example.com`,`Id=f0020001-0001-4000-8000-000000000001`;角色 `Id=f0020002-0001-4000-8000-000000000002`;`rolemenu` 绑定上述 26 条菜单。 --- ## 三、公司账号 ### 3.1 登录 | 项 | 值 | |----|------| | 方法 / 路径 | `POST /api/app/th-web-auth/login` | | Content-Type | `application/json` | | 说明 | 须指定租户;在**该公司业务库**校验账号,JWT 含 `TenantId` | #### 传参(`ThWebLoginInputVo`) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `tenantId` | guid | 与 `tenantName` 二选一 | 租户 Id(推荐) | | `tenantName` | string | 与 `tenantId` 二选一 | 租户名称,如 `中国麦当劳公司` | | `userName` | string | 是 | 登录账号(可为邮箱或用户名) | | `password` | string | 是 | 明文密码 | | `uuid` / `code` | string | 视环境 | 验证码 | ```json { "tenantId": "3a229a02-77eb-1dcc-afb6-09e2a2b6386c", "userName": "admin", "password": "123456" } ``` 或: ```json { "tenantName": "中国麦当劳公司", "userName": "admin", "password": "123456" } ``` #### 反参(`ThWebLoginOutputDto`) | 字段 | 类型 | 说明 | |------|------|------| | `token` | string | JWT(含租户与 RBAC Claims) | | `refreshToken` | string | 刷新令牌 | | `tenantId` | guid | 当前租户 Id | | `tenantName` | string | 当前租户名称 | ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "...", "tenantId": "3a229a02-77eb-1dcc-afb6-09e2a2b6386c", "tenantName": "中国麦当劳公司" } ``` ```bash curl -X POST "http://127.0.0.1:19002/api/app/th-web-auth/login" \ -H "Content-Type: application/json" \ -d "{\"tenantId\":\"3a229a02-77eb-1dcc-afb6-09e2a2b6386c\",\"userName\":\"admin\",\"password\":\"123456\"}" ``` ### 3.2 拉菜单 | 项 | 值 | |----|------| | 方法 / 路径 | `GET /api/app/auth-session/my-menus` | | Header | `Authorization: Bearer {公司 token}` | | 建议 Header | `__tenant: {tenantId}`(与 JWT 中 TenantId 一致更稳妥) | | 说明 | 读**当前租户业务库** `menu` + 角色;用户名为 `admin` 时返回该库全部未删菜单 | #### 传参 无 Body;依赖 Token(及可选 `__tenant`)。 #### 反参(`CurrentUserMenuPermissionsOutputDto`,字段一般为 camelCase) | 字段 | 类型 | 说明 | |------|------|------| | `user` | object | 当前用户简要信息 | | `user.id` | guid/string | 用户 Id | | `user.userName` | string | 登录名 | | `user.nick` | string? | 昵称 | | `user.email` | string? | 邮箱 | | `user.icon` | string? | 头像 | | `roleCodes` | string[] | 角色编码 | | `permissionCodes` | string[] | 权限码(超管常见 `*:*:*`) | | `accessPermissionCodes` | string[] | 角色访问权限编码 | | `menus` | array | **菜单树**(根列表,`children` 嵌套) | | `role` | string | 角色展示名 | | `fullName` | string | 全名 | | `lastUpdated` | datetime? | 供前端刷新缓存 | **菜单节点**(`CurrentUserMenuNodeDto`)常见字段: | 字段 | 说明 | |------|------| | `id` / `parentId` | 菜单 Id、父 Id(根多为 `"0"`) | | `menuName` | 名称 | | `router` / `routerName` / `component` | 路由与组件 | | `menuType` / `menuSource` / `orderNum` | 类型、来源、排序 | | `permissionCode` | 权限码 | | `menuIcon` / `isShow` / `state` | 图标、显示、启用 | | `children` | 子节点数组 | ```json { "user": { "id": "...", "userName": "admin", "nick": "超级管理员", "email": "admin@example.com" }, "roleCodes": ["admin"], "permissionCodes": ["*:*:*"], "accessPermissionCodes": [], "menus": [ { "id": "...", "parentId": "0", "menuName": "系统管理", "router": "/system", "orderNum": 100, "children": [] } ], "role": "管理员", "fullName": "超级管理员", "lastUpdated": "2026-07-23T10:00:00" } ``` ```bash curl -G "http://127.0.0.1:19002/api/app/auth-session/my-menus" \ -H "Authorization: Bearer " \ -H "__tenant: 3a229a02-77eb-1dcc-afb6-09e2a2b6386c" ``` 未识别租户时典型错误: > 未识别租户上下文。请使用泰额登录接口(th-web-auth / th-app-auth)或请求头 `__tenant` 携带租户 Id。 ### 3.3 公司库菜单现状说明 | 库 | 当前菜单大致情况 | |----|------------------| | 主库 host | 已种食品 SaaS 中文菜单(平台用) | | Default → `antis-foodlabeling-us` | 美国版英文业务菜单 | | 如 `antis-foodlabeling-t46d56528`(中国麦当劳) | 多为 **Yi 框架**种子(系统管理/用户管理…),**尚未**种 `th-tenant-menu-seed.sql` 那套食品 SaaS 菜单 | 若公司端侧边栏也要与平台同一套「首页概览 / 标签管理 / 业务模块…」: 1. 对该公司业务库执行 `项目相关文档/th-tenant-menu-seed.sql`(**禁止**对 `antis-foodlabeling-us` 执行,以免污染美国版) 2. 为公司管理员角色绑定对应 `rolemenu`(或公司 `admin` 用户名可直接看全库未删菜单) 3. 再用 `th-web-auth/login` + `auth-session/my-menus` 验证 --- ## 四、对照速查 | 步骤 | 平台管理员(H5 选 Default) | 公司账号 | |------|------------|----------| | 1. 登录 | `POST /api/app/th-web-auth/login`(Default tenantId + 邮箱) | `POST /api/app/th-web-auth/login` | | 2. 关键 | Default `tenantId` + 邮箱 `userName` + `password` | 公司 `tenantId` + `userName` + `password` | | 3. 登录反参 | `token` / `refreshToken` / `tenantName=Platform` | `token` / `refreshToken` / `tenantId` / `tenantName` | | 4. 拉菜单(H5) | `GET /api/app/account/Vue3Router/vben5` | `GET /api/app/auth-session/my-menus` 或 H5 同 vben5 | | 5. 菜单 Header | `Authorization: Bearer …` | `Authorization` + 建议 `__tenant` | | 6. 菜单反参形态 | Vben5 路由树(Ruoyi 回退) | `user` + `menus` 树 + 权限码 | --- ## 五、前端对接注意(H5 / Vben5,只读不改源码) 1. **登录**:`POST /api/app/th-web-auth/login`(见 `th-web-auth.ts`),Header 带 `__tenant: tenantId`。 2. **登录 Body**:`tenantId`、`userName`(Email 字段)、`password`;可选 `uuid`/`code`(验证码)。 3. **tenant-select**:`GET /api/app/th-multi-tenancy/tenant-select` 现返回 `id` + `name`;前端用 `id` 作为下拉 value。 4. **平台管理员操作**:选 **Default**,邮箱填 `admin@example.com`,密码 `123456`。 5. **公司操作**:选 **中国麦当劳公司** 等公司项,填该公司业务库账号(如 `admin`)。 6. **拉菜单**:H5 dist 实际调 `GET /api/app/account/Vue3Router/vben5`(非 `my-menus`);平台 Token 无租户即可;主库 `MenuSource=0` 时后端自动回退 Ruoyi 再 Vben5 构建。 7. **公司端**仍可用 `auth-session/my-menus`(须 Token + `__tenant`)。 8. 统一响应包装时从 `data` 取 `token` / `menus`。 ### 5.1 H5 登录请求示例(平台) ```json POST /api/app/th-web-auth/login Header: __tenant: 11111111-1111-1111-1111-111111111111 Body: { "tenantId": "11111111-1111-1111-1111-111111111111", "userName": "admin@example.com", "password": "123456" } ``` 成功反参(平台): ```json { "token": "eyJ...", "refreshToken": "...", "tenantId": "00000000-0000-0000-0000-000000000000", "tenantName": "Platform" } ``` ### 5.2 H5 登录请求示例(公司) ```json { "tenantId": "3a229a02-77eb-1dcc-afb6-09e2a2b6386c", "userName": "admin", "password": "123456" } ``` --- ## 六、自检清单 - [ ] H5 选 Default + `admin@example.com`:`th-web-auth/login` 成功,`tenantName=Platform`,Token 无 TenantId Claim - [ ] 平台:`Vue3Router/vben5` 返回非空路由(Ruoyi 菜单回退) - [ ] 公司:选公司 tenantId + 公司账号登录未破坏;`my-menus` 带 `__tenant` 成功 - [ ] 公司业务库菜单内容符合产品预期(必要时单独执行租户库 menu 种子,避开 US 库)