10 KiB
上传模板系统 API 文档
更新时间:2026-05-13
本文档按当前代码重新核对,覆盖主应用里真实存在的模板、字段和分类相关 API。旧文档中的 /api/field-pool、/api/admin/field-pool/sync 当前没有路由,不应再作为可调用接口。
1. 基础规则
- Base URL:
/api - 认证方式:Session Cookie
- 管理端接口要求当前用户角色为
ADMIN - 自定义模板的创建、更新、删除、迁移会检查许可证能力
customTemplates - 字段池同步是内部实现,主要由
syncFieldPoolFromTemplates()在模板和分类接口中触发
常见错误结构:
{
"error": "错误信息",
"errorCode": "ERROR_CODE",
"details": {}
}
当前常见错误码:
| 错误码 | 含义 |
|---|---|
UNAUTHORIZED |
未登录或不是管理员 |
LICENSE_REQUIRED |
当前许可证不允许使用自定义模板能力 |
VALIDATION_ERROR |
请求参数不合法 |
LIST_FAILED / GET_FAILED / UPDATE_FAILED |
服务端读取或写入失败 |
VERSION_CONFLICT |
更新模板时版本号冲突 |
TEMPLATE_STRUCTURE_LOCKED |
模板已有素材使用,不能直接做破坏性结构变更 |
TEMPLATE_IN_USE |
模板已被素材使用,不能删除 |
DUPLICATE_ENTRY / DUPLICATE_NAME |
名称或字段等唯一约束冲突 |
2. 管理端模板接口
2.1 获取模板列表
GET /api/admin/upload-templates
返回所有数据库模板,包含字段和 materialCount。
关键返回字段:
idnamedescriptionstatus:draft、active、archivedmaterialTypematerialKind:IMAGE、VIDEO、AUDIO、OTHERversionfieldGroupsdisplayConfigfieldsmaterialCount
2.2 获取单个模板详情
GET /api/admin/upload-templates/:id
返回模板字段、使用该模板的素材预览和字段填写率统计。
补充说明:
materials最多返回 10 条,用于后台预览。fieldStats是管理员模板详情能力,不是普通用户侧接口。- 填写率低于 30% 的字段会标记为低填写率。
2.3 创建模板
POST /api/admin/upload-templates
Content-Type: application/json
示例:
{
"name": "家具音频素材模板",
"description": "用于音频素材上传",
"status": "draft",
"materialType": "音频",
"materialKind": "AUDIO",
"fieldGroups": null,
"displayConfig": null,
"fields": [
{
"key": "skuCode",
"label": "SKU编码",
"type": "text",
"required": false,
"orderIndex": 0,
"layoutWidth": "half",
"validations": null,
"optionsSource": null,
"optionsStatic": null,
"allowedExtensions": null,
"visibleOnHomeFilter": false,
"isSearchable": true,
"isReadOnly": false,
"addToCategory": false,
"conditions": null
}
]
}
当前后端强校验:
name必填且不能为空materialType必填且不能为空materialKind必须是IMAGE、VIDEO、AUDIO、OTHERfields必须是数组;当前后端允许空数组,但业务上建议至少保留一个有效字段fields[].key必填且在同一模板内唯一
注意:
- 旧文档写过“字段 key 必须以字母开头、只能包含字母数字下划线”,当前后端模板保存接口没有做这条正则强校验;它可以作为前端/团队约定,但不能写成后端事实。
- 创建成功会触发字段池内部同步。
2.4 更新模板
PUT /api/admin/upload-templates/:id
Content-Type: application/json
更新时建议带上 version:
{
"name": "更新后的模板名称",
"status": "active",
"materialType": "音频",
"materialKind": "AUDIO",
"version": 1,
"fields": []
}
规则:
- 如果
version与服务端当前版本不一致,返回VERSION_CONFLICT。 - 更新成功后版本号自动递增。
- 模板已有素材使用时,不能直接修改素材类型、素材大类、删除字段、修改字段 key 或修改字段类型;这类修改会返回
TEMPLATE_STRUCTURE_LOCKED。 - 更新成功会触发字段池内部同步。
2.5 删除模板
DELETE /api/admin/upload-templates/:id
规则:
- 模板不存在返回
NOT_FOUND。 - 模板已有素材使用时返回
TEMPLATE_IN_USE。 - 删除成功返回:
{
"success": true
}
2.6 迁移素材模板
POST /api/admin/upload-templates/:id/migrate
该接口用于把素材迁移到目标模板。它同样要求管理员权限和自定义模板许可证能力。
当前行为:
- 迁移前会检查目标模板必填字段是否能从旧素材自动补齐;不能补齐时返回
TARGET_TEMPLATE_FIELDS_UNRESOLVED,不会写库。 - 迁移写库阶段按数据库事务提交;任一素材失败或已不属于源模板时,整体失败并回滚。
- 目标模板不接收的旧自定义字段和旧标准字段会进入
metadata.templateMigrationBackups备份区。 - 可安全清空的旧标准字段会从素材主表清理,避免继续污染新模板字段。
- 迁移生成的 metadata 超限时返回
METADATA_TOO_LARGE,不会进入事务。
具体请求体以当前代码 app/api/admin/upload-templates/[id]/migrate/route.ts 为准,执行前必须先在测试环境核对目标模板和素材范围。
3. 普通读取接口
3.1 上传页模板列表
GET /api/upload-templates
用于上传弹窗选择模板,返回启用的预制模板和自定义模板。
特点:
- 不要求管理员权限。
- 预制模板由
lib/upload-template-presets.ts生成。 - 当前预制模板包含音频模板,
builtin-audio-material的materialKind为AUDIO。 - 自定义模板只返回
status = active且非builtin-开头的模板。
3.2 首页模板筛选列表
GET /api/templates
用于首页模板标签筛选,只返回模板 id、name 和 isBuiltin。
4. 分类字段接口
4.1 分类字段配置
GET /api/category-fields
PATCH /api/category-fields
PUT /api/category-fields
GET 返回:
{
"managedFields": ["categoryName", "sceneName"],
"availableFields": [
{
"key": "categoryName",
"label": "使用店铺",
"source": "standard",
"type": "select",
"filterDisplayName": "店铺",
"filterOrder": 0
}
]
}
PATCH 用于启用或停用某个字段作为首页筛选字段:
{
"fieldKey": "categoryName",
"enabled": true
}
PUT 用于批量保存筛选显示名和排序:
{
"updates": [
{
"fieldKey": "categoryName",
"filterDisplayName": "店铺",
"filterOrder": 1
}
]
}
4.2 分类选项读取
GET /api/template-categories
GET /api/template-categories?includeAll=true
默认只返回已启用为筛选项的字段;includeAll=true 返回全部字段。
返回示例:
[
{
"fieldKey": "categoryName",
"fieldLabel": "店铺",
"fieldDefaultLabel": "使用店铺",
"filterOrder": 0,
"options": ["默认店铺"]
}
]
4.3 单个分类字段选项
GET /api/template-categories/:fieldKey
PUT /api/template-categories/:fieldKey
GET 返回单个字段的选项:
{
"fieldKey": "categoryName",
"fieldLabel": "店铺",
"options": ["默认店铺"]
}
PUT 仅管理员可用,用于重写该字段的选项:
{
"options": ["默认店铺", "旗舰店"]
}
保存时会去重、规范化,并同步更新同 key 的下拉字段。
5. 字段与模板数据边界
模板字段常用结构:
| 字段 | 说明 |
|---|---|
key |
数据键名,同一模板内必须唯一 |
label |
页面显示名 |
type |
字段类型,如 text、textarea、number、select、checkbox、radio、date、uppercase |
required |
是否必填 |
layoutWidth |
full 或 half |
optionsStatic |
下拉/单选选项,后端可接收数组并保存为 JSON 字符串 |
visibleOnHomeFilter |
历史字段,当前首页筛选主要看字段池里的 isFilterEnabled |
isSearchable |
是否参与搜索 |
isReadOnly |
上传页是否过滤只读字段 |
addToCategory |
是否同步进分类管理 |
conditions |
字段条件配置 |
conditions 当前已在上传页、编辑页和后端模板字段校验中生效。条件不满足的字段不会显示,不参与必填校验,上传时不会写入本次 metadata;编辑时隐藏字段不参与本次提交覆盖,已有历史值默认保留,避免用户只是保存其他字段时误删旧数据。
模板字段提交链路:
POST /api/materials/upload:只接收当前可见字段,隐藏字段会被过滤;metadata 超限返回METADATA_TOO_LARGE。PUT /api/materials/update:用素材现有值和本次提交值合并判断条件,只保存当前可见字段;metadata 超限返回METADATA_TOO_LARGE。PUT /api/materials/[id]:旧 JSON 更新入口也执行同一套模板字段校验,不能绕过条件规则。
6. 性能与索引
当前 Prisma schema 中已为模板、字段、字段池和选项保留常用索引,重点包括:
UploadTemplate.statusUploadTemplate.materialTypeUploadTemplate.materialKindUploadField.templateIdUploadField.keyUploadField.visibleOnHomeFilterUploadField.isSearchableUploadField.addToCategoryFieldDefinition.sourceFieldDefinition.isFilterEnabledFieldOption.fieldId
7. 使用建议
- 更新模板前先读取详情,带上最新
version再保存。 - 已被素材使用的模板不要直接改结构;新建模板后迁移更稳。
- 迁移前仍建议先在测试环境核对字段差异;系统会保留旧字段备份,但管理员仍应确认目标模板字段设计。
- 字段 key 虽然当前后端只强校验非空和唯一,但团队约定仍建议使用英文、数字、下划线,避免后续搜索、导出和脚本处理出问题。
- 分类管理只适合稳定、选项可控的下拉字段;不要把高频变化的大字段放到首页筛选。
- 不要直接调用不存在的字段池 API;需要刷新字段池时,通过模板保存或分类接口触发内部同步。