# 培训接口文档(泰额版) > 模块:培训 / 资料中心 > 范围:泰额版后端(租户业务库,非 `antis-foodlabeling-host`) > 认证:`Authorization: Bearer {token}`;业务请求建议带 `__tenant: {tenantId}` > JSON:camelCase > Base URL 示例:`http://127.0.0.1:19002`(以实际部署为准) > 更新日期:2026-08-07 --- ## 1. 业务说明 | 概念 | 说明 | |------|------| | 一级分类 | `parentId` 为空 | | 二级分类 | `parentId` 指向一级分类 Id;**仅两级** | | 培训文件 | **只能挂在二级分类下** | | 文件权限 | 落在**文件**上:Company / Region / Location,各为 `ALL` 或 `SPECIFIED` | | Company | `fl_partner`;字段 `partnerIds` / `companyIds` 等价 | | Region | `fl_group`;字段 `regionIds` / `groupIds` 等价 | | Location | `location`;字段 `locationIds` | ### 权限约定(推荐做法) **主路径:上传 / 编辑文件时直接传公司、区域、门店范围**(可传 `ALL` 或具体多值 Id)。 独立「编辑文件权限」接口仅作兼容保留,前端可不使用。 | 维度 | 类型字段 | Id 数组 | 说明 | |------|----------|---------|------| | Company | `appliedPartnerType` | `partnerIds` / `companyIds` | `ALL` 或 `SPECIFIED` + Guid 列表(可含哨兵 `"ALL"`) | | Region | `appliedRegionType` | `regionIds` / `groupIds` | 同上 | | Location | `availabilityType`(别名 `appliedLocationType`) | `locationIds` | 同上 | - 上传时**不传**任何 scope 字段 → 默认三维度均为 `ALL` - 编辑时**不传**任何 scope 字段 → **不改**原权限(仅改文件名/排序) - 编辑时传入任一 scope 字段 → 整套权限按入参覆盖保存 - `SPECIFIED` 且仅选当前上下文「恰好全集」时,回显保持具体 Guid / 声明类型,不误折成 `["ALL"]`(与标签模板等一致) ### 文件存储 | 环境 | 路径 | |------|------| | Linux 生产 | `/www/wwwroot/FoodLabelingManagementSAAS/training` | | 本地兜底 | `{API ContentRoot}/wwwroot/FoodLabelingManagementSAAS/training` | | 库中 `fileUrl` | `/training/{存储文件名}` | - 单文件最大 **20MB** - 扩展名:`.jpg/.jpeg/.png/.webp/.gif/.bmp`、`.pdf/.doc/.docx/.xls/.xlsx/.ppt/.pptx/.txt/.csv` - `fileType`:`image` / `doc` / `other` ### 建表 - 脚本:`module/food-labeling-us/scripts/fl_training.sql` - 在每个**租户业务库**执行;新租户开通时会自动执行 --- ## 2. 接口一览 ### 2.1 管理端 `TrainingAppService` | 功能 | 方法 | 路由 | |------|------|------| | 分类树 | GET | `/api/app/training/category-tree` | | 新增分类 | POST | `/api/app/training/category` | | 编辑分类 | PUT | `/api/app/training/category/{id}` | | 删除分类 | DELETE | `/api/app/training/category/{id}` | | 上传文件(含权限) | POST | `/api/app/training/file/upload` | | 编辑文件(含权限) | PUT | `/api/app/training/{id}/file` | | 删除文件 | DELETE | `/api/app/training/{id}/file` | | 文件排序 | PUT | `/api/app/training/sort-files` | | 获取文件权限(兼容) | GET | `/api/app/training/file-scope/{id}` | | 设置文件权限(兼容) | PUT | `/api/app/training/file-scope/{id}` | > 上传、`file-scope` 为显式路由;其余多为 ABP 约定路由。以 Swagger 为准。 > 另有约定路径 `GET/PUT /api/app/training/{id}/file-scope`,与 `file-scope/{id}` 等价兼容。 ### 2.2 APP `UsAppTrainingAppService` | 功能 | 方法 | 路由 | |------|------|------| | 门店可见分类树+文件 | GET | `/api/app/us-app-training/tree` | --- ## 3. 管理端接口详情 ### 3.1 获取分类树 `GET /api/app/training/category-tree` **Query** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | keyword | string | 否 | 匹配分类名或文件名 | | locationId | string | 否 | 按门店过滤可见文件;不传则不过滤权限 | | includeFiles | bool | 否 | 是否返回文件,默认 `true` | **响应要点**:一级 `children` 为二级;二级 `files` 为 `TrainingFileDto`(含完整 scope 回显字段)。 ```json [ { "id": "1001", "categoryName": "分类1", "parentId": null, "orderNum": 100, "children": [ { "id": "1002", "categoryName": "子1", "parentId": "1001", "orderNum": 90, "children": [], "files": [ { "id": "23148558702612480", "categoryId": "1002", "fileName": "学情测评试题.pdf", "fileUrl": "/training/20260807120000_xxx.pdf", "fileType": "doc", "fileSize": 102400, "orderNum": 100, "appliedPartnerType": "ALL", "company": "All Companies", "partnerIds": ["ALL"], "companyIds": ["ALL"], "appliedRegionType": "SPECIFIED", "region": "武侯区", "regionIds": ["3a22e2f6-0679-9a5b-19b4-fc6db2d92dcc"], "groupIds": ["3a22e2f6-0679-9a5b-19b4-fc6db2d92dcc"], "availabilityType": "SPECIFIED", "location": "分店2", "locationIds": ["3a22e2f6-e944-2038-ffd8-fc82d9d388ac"], "creationTime": "2026-08-07T12:00:00", "lastModificationTime": "2026-08-07T12:00:00" } ] } ], "files": [] } ] ``` --- ### 3.2 新增分类 `POST /api/app/training/category` ```json { "categoryName": "Training", "parentId": null, "orderNum": 100 } ``` | 字段 | 说明 | |------|------| | categoryName | 必填;同级不可重名 | | parentId | 空 = 一级;传一级 Id = 二级 | | orderNum | 排序 | **响应**:`TrainingCategoryGetOutputDto`(`id` / `categoryName` / `parentId` / `orderNum` / 时间字段) 常见错误:名称为空、父级不存在、在二级下再建子级、同级重名。 --- ### 3.3 编辑分类 `PUT /api/app/training/category/{id}` ```json { "categoryName": "Training(更新)", "orderNum": 90 } ``` 不可改层级(`parentId` 不可改)。 --- ### 3.4 删除分类 `DELETE /api/app/training/category/{id}` 软删除。规则:一级下仍有二级不可删;二级下仍有文件不可删。 --- ### 3.5 上传文件(主路径,含权限) `POST /api/app/training/file/upload` `Content-Type: multipart/form-data` | 表单字段 | 类型 | 必填 | 说明 | |----------|------|------|------| | file | file | 是 | 文件本体 | | categoryId | string | 是 | **二级**分类 Id | | orderNum | int | 否 | 排序 | | appliedPartnerType | string | 否 | `ALL` / `SPECIFIED` | | partnerIds | string[] | 否 | 可重复传多个 form 字段;可含 `ALL` | | companyIds | string[] | 否 | 同 partnerIds | | appliedRegionType | string | 否 | `ALL` / `SPECIFIED` | | regionIds | string[] | 否 | 可含 `ALL` | | groupIds | string[] | 否 | 同 regionIds | | availabilityType | string | 否 | Location:`ALL` / `SPECIFIED` | | appliedLocationType | string | 否 | `availabilityType` 别名 | | locationIds | string[] | 否 | 可含 `ALL` | **curl 示例(全 ALL)** ```bash curl -X POST "http://127.0.0.1:19002/api/app/training/file/upload" \ -H "Authorization: Bearer " \ -H "__tenant: " \ -F "file=@./手册.pdf" \ -F "categoryId=<二级分类Id>" \ -F "orderNum=100" \ -F "appliedPartnerType=ALL" \ -F "partnerIds=ALL" \ -F "appliedRegionType=ALL" \ -F "regionIds=ALL" \ -F "availabilityType=ALL" \ -F "locationIds=ALL" ``` **curl 示例(指定公司 + 门店)** ```bash curl -X POST "http://127.0.0.1:19002/api/app/training/file/upload" \ -H "Authorization: Bearer " \ -H "__tenant: " \ -F "file=@./手册.pdf" \ -F "categoryId=<二级分类Id>" \ -F "appliedPartnerType=SPECIFIED" \ -F "partnerIds=" \ -F "appliedRegionType=SPECIFIED" \ -F "regionIds=" \ -F "availabilityType=SPECIFIED" \ -F "locationIds=" \ -F "locationIds=" ``` **响应**:完整 `TrainingFileDto`(含 scope 回显,结构见 3.1)。 --- ### 3.6 编辑文件(主路径,含权限) `PUT /api/app/training/{id}/file` ```json { "fileName": "操作手册.pdf", "orderNum": 100, "appliedPartnerType": "SPECIFIED", "partnerIds": ["3a22e2f5-a785-ba39-9847-de45bdd47e50"], "companyIds": ["3a22e2f5-a785-ba39-9847-de45bdd47e50"], "appliedRegionType": "ALL", "regionIds": ["ALL"], "groupIds": ["ALL"], "availabilityType": "SPECIFIED", "locationIds": ["3a22e2f6-e944-2038-ffd8-fc82d9d388ac"] } ``` | 字段 | 说明 | |------|------| | fileName | 必填;展示名(不换物理文件) | | orderNum | 排序 | | scope 各字段 | 见第 1 节;**只要传了任一 scope 字段即整套覆盖**;全不传则保持原权限 | **响应**:完整 `TrainingFileDto`。 ```bash curl -X PUT "http://127.0.0.1:19002/api/app/training//file" \ -H "Authorization: Bearer " \ -H "__tenant: " \ -H "Content-Type: application/json" \ -d "{\"fileName\":\"学情测评试题.pdf\",\"orderNum\":100,\"appliedPartnerType\":\"ALL\",\"partnerIds\":[\"ALL\"],\"appliedRegionType\":\"ALL\",\"availabilityType\":\"ALL\",\"locationIds\":[\"ALL\"]}" ``` --- ### 3.7 删除文件 `DELETE /api/app/training/{id}/file` 软删除,并清理 scope 关联行。 --- ### 3.8 文件排序 `PUT /api/app/training/sort-files` ```json { "items": [ { "id": "23148558702612480", "orderNum": 100 }, { "id": "23148558702612481", "orderNum": 90 } ] } ``` --- ### 3.9 获取 / 设置文件权限(兼容,非推荐主路径) | 方法 | 路由 | |------|------| | GET | `/api/app/training/file-scope/{id}` | | PUT | `/api/app/training/file-scope/{id}` | **推荐**:权限在 **上传 / 编辑文件** 中一并提交,不必单独调本接口。 本接口与 create/update 共用同一套 scope 保存逻辑;PUT body 与 scope 字段相同(无 `fileName`)。 **GET 响应**(`TrainingFileScopeOutputDto`) ```json { "appliedPartnerType": "SPECIFIED", "company": "成都分店", "partnerIds": ["3a22e2f5-a785-ba39-9847-de45bdd47e50"], "companyIds": ["3a22e2f5-a785-ba39-9847-de45bdd47e50"], "appliedRegionType": "SPECIFIED", "region": "武侯区", "regionIds": ["3a22e2f6-0679-9a5b-19b4-fc6db2d92dcc"], "groupIds": ["3a22e2f6-0679-9a5b-19b4-fc6db2d92dcc"], "availabilityType": "SPECIFIED", "location": "分店2", "locationIds": ["3a22e2f6-e944-2038-ffd8-fc82d9d388ac"] } ``` **PUT body 示例** ```json { "appliedPartnerType": "ALL", "partnerIds": ["ALL"], "appliedRegionType": "SPECIFIED", "regionIds": ["3a22e2f6-0679-9a5b-19b4-fc6db2d92dcc"], "availabilityType": "SPECIFIED", "locationIds": ["3a22e2f6-e944-2038-ffd8-fc82d9d388ac"] } ``` --- ## 4. APP 接口 ### 4.1 门店可见分类树 `GET /api/app/us-app-training/tree` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | locationId | string | 是 | 当前门店 Id;校验用户可访问该门店 | | keyword | string | 否 | 匹配分类名或文件名 | 按门店过滤文件:Company / Region / Location 三维度 `ALL` 或 `SPECIFIED` 命中该门店才可见。 响应结构同管理端分类树(`includeFiles=true`)。 ```bash curl -G "http://127.0.0.1:19002/api/app/us-app-training/tree" \ --data-urlencode "locationId=" \ -H "Authorization: Bearer " \ -H "__tenant: " ``` --- ## 5. TrainingFileDto 字段说明 | 字段 | 说明 | |------|------| | id | 文件 Id | | categoryId | 所属二级分类 | | fileName / fileUrl / fileType / fileSize | 文件信息 | | orderNum | 排序 | | appliedPartnerType | Company:`ALL` / `SPECIFIED` | | company | Company 展示文案 | | partnerIds / companyIds | Company Id 列表(可含 `ALL`) | | appliedRegionType | Region:`ALL` / `SPECIFIED` | | region | Region 展示文案 | | regionIds / groupIds | Region Id 列表 | | availabilityType | Location:`ALL` / `SPECIFIED` | | location | Location 展示文案 | | locationIds | 门店 Id 列表 | | creationTime / lastModificationTime | 时间 | --- ## 6. 联调检查清单 - [ ] 分类仅两级;文件只能挂二级 - [ ] 上传可不传 scope → 默认三维度 ALL - [ ] 上传/编辑传 `ALL` 或具体多值 Guid → 库表 type 字段 + 关联表正确 - [ ] 分类树 `files[]` 含完整 scope 回显 - [ ] `PUT .../file` 不传 scope 时不改权限;传了则覆盖 - [ ] `GET/PUT /api/app/training/file-scope/{id}` 不再 404(兼容路径) - [ ] APP `us-app-training/tree` 按 locationId 过滤可见文件 - [ ] 文件 ≤20MB、扩展名合法 查库(租户业务库): ```sql SELECT Id, FileName, AppliedPartnerType, AppliedRegionType, AvailabilityType FROM fl_training_file WHERE IsDeleted = 0; SELECT * FROM fl_training_file_partner WHERE TrainingFileId = ''; SELECT * FROM fl_training_file_region WHERE TrainingFileId = ''; SELECT * FROM fl_training_file_location WHERE TrainingFileId = ''; ``` --- ## 7. 相关代码 | 说明 | 路径 | |------|------| | 管理端服务 | `FoodLabeling.Application/Services/TrainingAppService.cs` | | APP 服务 | `FoodLabeling.Application/Services/UsAppTrainingAppService.cs` | | Scope 辅助 | `FoodLabeling.Application/Helpers/TrainingFileScopeHelper.cs` | | 建表脚本 | `module/food-labeling-us/scripts/fl_training.sql` | --- ## 8. 变更记录 | 日期 | 说明 | |------|------| | 2026-08-07 | 重写文档:权限并入上传/编辑;file-scope 改为兼容路径;补全 TrainingFileDto scope 回显与 curl 示例 | | (历史) | 初版:分类树、文件 CRUD、独立 file-scope、APP tree |