hl-api-changelog/changelogs-v2/2026-06/03_3390_图标库模块-新增接口-管理后台+小程序.md

18 KiB

【新增接口·管理后台 + 小程序】图标库模块 11 接口(全模块首次推送 changelog

PR: #3396模块主体+ #3398 / #3399mp 端点公开化)+ #3401上线后审计加固 服务: hl-user-service | 更新时间: 2026-06-03

存放目录: changelogs-v2/2026-06/ 影响范围: 管理后台「图标库」管理页(分类 + 图标 CRUD + SVG 上传解析);小程序 / 前端 图标渲染(公开只读查询)


⚠️ 关键说明

图标库是后台统一维护的一套 SVG 图标,结构类似数据字典:一级「图标分类」(icon_category) + 二级「图标项」(icon_item),图标项归属某分类编码 categoryCode

SVG 直接存库(不走 OSS:图标内容是 SVG 字符串,直存数据库 svg_contentMEDIUMTEXT,单图标后端限 256KB。前端拿到 svgContent 直接渲染。

新增图标分两步:先调 §3.5 POST /admin/icon/parse-svg 上传 .svg 文件,后端读成字符串并做安全校验后只返回字符串、不落库;前端拿到 svgContent 再连同 color / size 等填进保存表单调 §3.6 POST /admin/icon/item 保存。

mp 查询公开无需 token§3.10 / §3.11 是 UI 参考数据(同字典),无需登录即可访问,只返回 status=ACTIVE 的分类与图标,且分类被禁用时其图标也不再返回。

SVG 安全校验:上传 / 保存的 SVG 经服务端 XXE-safe 解析校验,拒绝含 <script> / <foreignObject> 元素、on* 事件属性、javascript: 外链的内容;前端渲染时仍建议做一次 sanitize 作纵深防御。


1. 接口背景

运营在后台「图标库」页统一维护 SVG 图标,供小程序 / 前端按分类拉取渲染(如分类入口图标、标签图标等)。

管理后台「图标库」页提供:

  • 分类维护(列表 / 分页 / 新增 / 编辑 / 删除)
  • 图标维护(分页 / 详情 / 新增 / 编辑 / 删除)
  • 上传 SVG 文件解析为字符串

小程序 / 前端:按分类拉启用图标列表渲染(公开只读)。


2. 变更清单

管理后台(/admin/icon/**,需 admin JWT

# 方法 路径 变更类型 说明
1 POST /admin/icon/category 首推 保存分类id 空=新建,非空=修改)
2 GET /admin/icon/category/page 首推 分类分页status / keyword 过滤)
3 GET /admin/icon/category/list 首推 启用分类列表(下拉用)
4 DELETE /admin/icon/category/{id} 首推 删除分类(其下有图标项时拒删)
5 POST /admin/icon/parse-svg 首推 上传 SVG 文件解析为字符串(不落库)
6 POST /admin/icon/item 首推 保存图标id 空=新建,非空=修改)
7 GET /admin/icon/item/page 首推 图标分页categoryCode / keyword 过滤)
8 GET /admin/icon/item/{id} 首推 图标详情(含 svgContent
9 DELETE /admin/icon/item/{id} 首推 删除图标

小程序 / 前端(/mp/icon/**,公开无需 token

# 方法 路径 变更类型 说明
10 GET /mp/icon/categories 首推 启用分类列表
11 GET /mp/icon/items 首推 某分类下启用图标列表

3. 接口详情

3.1 保存分类

POST /admin/icon/category

  • 使用场景:图标库分类新增 / 编辑
  • 认证:管理后台 JWT
  • 幂等:否(同 categoryCode 重复新建抛 210701

Body 入参IconCategorySaveReqVO

字段 类型 必填 校验 说明
id Long 空=新建,非空=修改
categoryCode string @Size(max=64) 分类编码(全局唯一,创建后不可改)
categoryName string @Size(max=100) 分类名称
sort int 排序号(升序),默认 0
status string @Pattern(ACTIVE|DISABLED) 状态,默认 ACTIVE
remark string @Size(max=255) 备注

出参Result<Long>data = 分类 ID

典型示例 请求

POST /admin/icon/category
Authorization: Bearer <token>
Content-Type: application/json

{ "categoryCode": "weather", "categoryName": "天气", "sort": 1 }

典型示例 响应

{ "code": 200, "data": 2062087054098898945, "message": "成功" }

异常 响应(编码重复):

{ "code": 210701, "message": "图标分类编码已存在", "data": null }

错误码400 参数校验 / 210701 分类编码已存在 / 210702 分类不存在(修改时 id 无效)/ 401 未登录


3.2 分类分页

GET /admin/icon/category/page

  • 使用场景:分类管理列表页
  • 认证:管理后台 JWT
  • 幂等:是(只读)

Query 入参IconCategoryPageReqVO

字段 类型 必填 默认 说明
page int 1 页码
pageSize int 20 每页条数
status string 状态精确过滤ACTIVE / DISABLED
keyword string 模糊匹配分类编码 / 名称

出参Result<PageResult<IconCategoryRespVO>>

字段 类型 说明
records[].id string 分类 IDLong 序列化为 String
records[].categoryCode string 分类编码
records[].categoryName string 分类名称
records[].sort int 排序号
records[].status string 状态ACTIVE / DISABLED
records[].remark string 备注
records[].createTime datetime 创建时间
total / page / pageSize int 分页元数据

错误码400 参数校验 / 401 未登录


3.3 启用分类列表

GET /admin/icon/category/list

  • 使用场景:图标新增页的分类下拉
  • 认证:管理后台 JWT
  • 幂等:是

入参:无

出参Result<List<IconCategoryRespVO>>(仅 status=ACTIVE,按 sort 升序)

错误码401 未登录


3.4 删除分类

DELETE /admin/icon/category/{id}

  • 使用场景:删除空分类
  • 认证:管理后台 JWT
  • 幂等:是

路径入参

字段 类型 必填 说明
id Long 分类 ID

出参Result<Void>data: null

异常 响应(分类下仍有图标项):

{ "code": 210703, "message": "该分类下存在图标项,无法删除", "data": null }

错误码210702 分类不存在 / 210703 分类下存在图标项不可删 / 401 未登录


3.5 上传 SVG 解析(保存图标前置步骤)

POST /admin/icon/parse-svg

  • 使用场景:新增图标前上传 .svg 文件,拿到 SVG 字符串回填表单
  • 认证:管理后台 JWT
  • 幂等:是(仅解析,不落库)

入参multipart/form-data

字段 类型 必填 说明
file file SVG 文件(.svg

出参Result<IconSvgParseRespVO>

字段 类型 说明
svgContent string 解析出的 SVG 字符串(回填保存表单)
fileName string 原始文件名
size long 文件字节数

典型示例 请求

POST /admin/icon/parse-svg
Authorization: Bearer <token>
Content-Type: multipart/form-data; boundary=...

form-data: file=@sunny.svg

典型示例 响应

{
  "code": 200,
  "data": {
    "svgContent": "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/></svg>",
    "fileName": "sunny.svg",
    "size": 1234
  },
  "message": "成功"
}

错误码210753 文件为空 / 210754 非合法 SVG非 XML 或根元素不是 svg/ 210755 超 256KB / 210756 文件读取失败 / 210757 含不安全内容(脚本 / 事件 / 外链)/ 401 未登录


3.6 保存图标

POST /admin/icon/item

  • 使用场景:图标新增 / 编辑
  • 认证:管理后台 JWT
  • 幂等:否(同分类 iconCode 重复新建抛 210751

Body 入参IconItemSaveReqVO

字段 类型 必填 校验 说明
id Long 空=新建,非空=修改
categoryCode string @Size(max=64) 所属分类编码(新建校验分类存在;修改时不可改
iconCode string @Size(max=64) 图标编码(同分类内唯一;修改时不可改
name string @Size(max=100) 图标名称
svgContent string @Size(max=262144) SVG 字符串(来自 §3.5 解析结果,经安全校验)
color string @Size(max=16) 默认色值 hex,如 #FFCC00
size int 默认尺寸(像素)
keywords string @Size(max=255) 搜索关键词(逗号分隔)
sort int 排序号,默认 0
status string @Pattern(ACTIVE|DISABLED) 状态,默认 ACTIVE
remark string @Size(max=255) 备注

出参Result<Long>data = 图标 ID

典型示例 请求

POST /admin/icon/item
Authorization: Bearer <token>
Content-Type: application/json

{
  "categoryCode": "weather",
  "iconCode": "sunny",
  "name": "晴天",
  "svgContent": "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/></svg>",
  "color": "#FFCC00",
  "size": 24,
  "keywords": "晴,太阳,sunny"
}

典型示例 响应

{ "code": 200, "data": 2062087056506441730, "message": "成功" }

异常 响应SVG 含脚本 / 事件 / 外链):

{ "code": 210757, "message": "SVG 含不安全内容(脚本/事件/外链),请清理后重试", "data": null }

错误码400 参数校验 / 210702 分类不存在 / 210751 同分类图标编码已存在 / 210752 图标不存在(修改时 id 无效)/ 210754 SVG 非法 / 210755 SVG 超 256KB / 210757 SVG 含不安全内容 / 401 未登录

SaveReqVO 全量快照语义:修改时请先用 §3.8 详情拉取再整体回填提交,未传的可空字段会被覆盖为空。categoryCode / iconCode 创建后不可变,修改时被忽略。


3.7 图标分页

GET /admin/icon/item/page

  • 使用场景:图标管理列表页
  • 认证:管理后台 JWT
  • 幂等:是

Query 入参IconItemPageReqVO

字段 类型 必填 默认 说明
page int 1 页码
pageSize int 20 每页条数
categoryCode string 所属分类精确过滤
keyword string 模糊匹配图标编码 / 名称 / 关键词

出参Result<PageResult<IconItemRespVO>>records[] 为完整图标 VO字段见 §3.8

错误码400 参数校验 / 401 未登录


3.8 图标详情

GET /admin/icon/item/{id}

  • 使用场景:详情查看 / 编辑前预填
  • 认证:管理后台 JWT
  • 幂等:是

路径入参

字段 类型 必填 说明
id Long 图标 ID

出参Result<IconItemRespVO>

字段 类型 说明
id string 图标 ID
categoryCode string 所属分类编码
iconCode string 图标编码
name string 图标名称
svgContent string SVG 字符串
color string 默认色值 hex
size int 默认尺寸(像素)
keywords string 搜索关键词
sort int 排序号
status string 状态ACTIVE / DISABLED
remark string 备注
createTime / updateTime datetime 时间

典型示例 响应

{
  "code": 200,
  "data": {
    "id": "2062087056506441730",
    "categoryCode": "weather",
    "iconCode": "sunny",
    "name": "晴天",
    "svgContent": "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/></svg>",
    "color": "#FFCC00",
    "size": 24,
    "keywords": "晴,太阳,sunny",
    "sort": 0,
    "status": "ACTIVE",
    "remark": null,
    "createTime": "2026-06-03 16:00:00",
    "updateTime": "2026-06-03 16:00:00"
  },
  "message": "成功"
}

错误码210752 图标不存在 / 401 未登录


3.9 删除图标

DELETE /admin/icon/item/{id}

  • 使用场景:删除图标
  • 认证:管理后台 JWT
  • 幂等:是

路径入参

字段 类型 必填 说明
id Long 图标 ID

出参Result<Void>data: null

错误码210752 图标不存在 / 401 未登录


3.10【公开】启用分类列表

GET /mp/icon/categories

  • 使用场景:小程序 / 前端拉图标分类
  • 认证公开,无需 token
  • 幂等:是

入参:无

出参Result<List<IconCategorySimpleRespVO>>(仅 status=ACTIVE,按 sort 升序)

字段 类型 说明
categoryCode string 分类编码
categoryName string 分类名称
sort int 排序号

典型示例 请求

GET /mp/icon/categories
(无需 Authorization

典型示例 响应

{
  "code": 200,
  "data": [
    { "categoryCode": "weather", "categoryName": "天气", "sort": 1 }
  ],
  "message": "成功"
}

3.11【公开】某分类下启用图标列表

GET /mp/icon/items

  • 使用场景:小程序 / 前端按分类渲染图标
  • 认证公开,无需 token
  • 幂等:是

Query 入参

字段 类型 必填 说明
categoryCode string 分类编码

出参Result<List<IconItemSimpleRespVO>>(仅 status=ACTIVE 的图标,且分类须为 ACTIVE

字段 类型 说明
iconCode string 图标编码
name string 图标名称
svgContent string SVG 字符串(直接渲染)
color string 默认色值 hex
size int 默认尺寸(像素)

典型示例 请求

GET /mp/icon/items?categoryCode=weather
(无需 Authorization

典型示例 响应

{
  "code": 200,
  "data": [
    {
      "iconCode": "sunny",
      "name": "晴天",
      "svgContent": "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/></svg>",
      "color": "#FFCC00",
      "size": 24
    }
  ],
  "message": "成功"
}

错误码400 categoryCode 缺失


6. 枚举 / 数据字典

6.1 状态(status

所属字段:分类 / 图标的 status | 类型String | 必填(默认 ACTIVE

@Pattern 强校验枚举

含义
ACTIVE 启用
DISABLED 禁用

mp 端只返回 ACTIVE 的分类与图标;分类 DISABLED 时其图标在 mp 端也不返回。


7. 错误码(全模块汇总)

code 含义 触发条件 涉及接口
400 参数校验失败 字段缺失 / 长度 / 枚举值不合法 所有写入 + §3.11
401 未登录 admin JWT 无效或缺失 全部 admin 接口mp 端公开不需要)
210701 分类编码已存在 categoryCode 唯一约束 §3.1
210702 分类不存在 分类 id / categoryCode 无效 §3.1 §3.4 §3.6
210703 分类下存在图标项,不可删 删分类时其下仍有图标 §3.4
210751 同分类图标编码已存在 (categoryCode, iconCode) 唯一约束 §3.6
210752 图标不存在 图标 id 无效 §3.6 §3.8 §3.9
210753 上传 SVG 文件为空 文件为空 §3.5
210754 SVG 内容不合法 非合法 XML 或根元素不是 <svg> §3.5 §3.6
210755 SVG 内容过大 超过 256KB §3.5 §3.6
210756 SVG 文件读取失败 文件流读取异常 §3.5
210757 SVG 含不安全内容 <script> / <foreignObject> / on* 事件 / javascript: 外链 §3.5 §3.6

错误码段位 210700-210799 归属图标库模块。


9. 业务边界

  • 两级结构:分类(icon_category+ 图标(icon_item),图标按 categoryCode 归属分类
  • SVG 直存库svg_content MEDIUMTEXT 直存 SVG 字符串,不走 OSS;单图标后端限 256KB
  • 上传解析与保存分离§3.5 只解析返回字符串不落库;§3.6 才落库
  • 唯一约束categoryCode 全局唯一;(categoryCode, iconCode) 联合唯一
  • 编码不可变:图标的 categoryCode / iconCode 创建后不可改(修改时忽略)
  • SVG 服务端安全校验XXE-safe 解析 + 拒绝脚本 / 事件 / 外链
  • mp 公开只读§3.10 §3.11 无需 token,只返 ACTIVE;分类禁用其图标也不返

11. 影响评估

  • 是否破坏向后兼容:否(全新模块,无存量接口改动)
  • 前端是否必须同步上线:是(新功能,前端按本 changelog 对接管理页 + 渲染端)
  • 影响已有数据:无(新建 icon_category / icon_item 两表)

12. 注意事项

  • 分页参数名page / pageSize(不是 pageNo
  • Long 主键序列化id 等 Long 字段 JSON 返回为 String,前端不要当 Number 解析
  • 新增图标流程:先 §3.5 上传解析拿 svgContent → 填表单(补 color / size / 分类 / 编码)→ §3.6 保存
  • 渲染 SVG:拿到 svgContent 直接内联渲染,务必先做一次前端 sanitize(后端已拒明显恶意内容,前端作纵深防御)
  • mp 端公开§3.10 / §3.11 无需 token;先拉分类再用 categoryCode 拉图标
  • 状态语义ACTIVE 启用 / DISABLED 禁用,mp 端只见 ACTIVE

13. 关联 / 联系人

13.1 链接

  • 同模块 PR#3396主体/ #3398 / #3399mp 公开化)/ #3401审计加固
  • 关联工单#3390

13.2 联系人

  • 后端负责人: @wx
  • 前端对接(管理后台 + 小程序): 待指派