acf259d5
李曜臣
2026-07-24
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
|
# 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 <platform-token>"
```
### 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 <company-token>" \
-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 库)
|