8.0 KiB
【新增接口·管理后台 + 小程序】图标库模块(全模块首次推送,11 接口)
PR: #3396(模块主体)+ #3398 / #3399(mp 端点公开化)+ #3401(上线后审计加固) 服务: hl-user-service | 更新时间: 2026-06-03
存放目录:
changelogs-v2/2026-06/影响范围: 管理后台「图标库」管理页(分类 + 图标 CRUD + SVG 上传解析);小程序/前端 图标渲染(公开只读查询)
⚠️ 关键说明
- 两级结构(类似数据字典):一级「图标分类」(
icon_category) + 二级「图标项」(icon_item)。图标项归属某分类编码categoryCode。 - SVG 直接存库(不走 OSS):图标内容是 SVG 字符串,直存数据库
svg_content(MEDIUMTEXT,上限 16MB,单图标后端限 256KB)。前端拿到的svgContent即可直接渲染。 - 上传解析与保存分两步:先调
POST /admin/icon/parse-svg上传.svg文件,后端读成字符串校验后只返回字符串不落库;前端拿到svgContent再连同color/size等填进保存表单调POST /admin/icon/item保存。 - SVG 安全校验(重要):上传/保存的 SVG 会经服务端 XXE-safe 解析校验,拒绝含
<script>、<foreignObject>、on*事件属性(onload/onclick…)、javascript:外链的内容(返回错误码210757);非合法 XML 或根元素不是<svg>返回210754。前端渲染 SVG 时仍建议做一次 sanitize 作为纵深防御。 - mp 查询端点公开无需 token:
/mp/icon/categories、/mp/icon/items是 UI 参考数据(同字典),无需登录、无需 token 即可访问;只返回status=ACTIVE的分类与图标,且分类被禁用时其图标也不再返回。 - admin 端点需管理后台 JWT;
color为 hex(如#FFCC00),size为像素整数,均为可选展示属性。
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/item |
保存图标(id 空=新建,非空=修改) |
| 6 | GET | /admin/icon/item/page |
图标分页(categoryCode / keyword 过滤) |
| 7 | GET | /admin/icon/item/{id} |
图标详情(含 svgContent) |
| 8 | DELETE | /admin/icon/item/{id} |
删除图标 |
| 9 | POST | /admin/icon/parse-svg |
上传 SVG 文件解析为字符串(不落库) |
小程序 / 前端(/mp/icon/**,公开无需 token)
| # | 方法 | 路径 | 说明 |
|---|---|---|---|
| 10 | GET | /mp/icon/categories |
启用分类列表 |
| 11 | GET | /mp/icon/items?categoryCode=xxx |
某分类下启用图标列表 |
3. 接口详情
3.1 保存分类
POST /admin/icon/category
| 字段 | 类型 | 必填 | 校验 | 说明 |
|---|---|---|---|---|
id |
Long | ❌ | — | 空=新建,非空=修改 |
categoryCode |
String | ✅ | ≤64 | 分类编码,全局唯一,创建后不可改 |
categoryName |
String | ✅ | ≤100 | 分类名称 |
sort |
Integer | ❌ | — | 排序号,升序,默认 0 |
status |
String | ❌ | ACTIVE/DISABLED |
状态,默认 ACTIVE |
remark |
String | ❌ | ≤255 | 备注 |
curl -X POST "https://api.test.1814.love:9443/admin/icon/category" \
-H "Authorization: Bearer {adminToken}" -H "Content-Type: application/json" \
-d '{"categoryCode":"weather","categoryName":"天气","sort":1}'
# 返回: {"code":200,"message":"成功","data":2062087054098898945} // data=分类ID
3.2 上传 SVG 解析(保存图标前置步骤)
POST /admin/icon/parse-svg,multipart/form-data,字段名 file(.svg 文件)
curl -X POST "https://api.test.1814.love:9443/admin/icon/parse-svg" \
-H "Authorization: Bearer {adminToken}" \
-F "file=@sunny.svg"
# 返回:
# {"code":200,"message":"成功","data":{
# "svgContent":"<svg viewBox=\"0 0 24 24\">...</svg>",
# "fileName":"sunny.svg","size":1234 }}
- 校验失败错误码:
210753文件为空 /210754非合法 SVG(非 XML 或根非 svg)/210755超 256KB /210757含不安全内容(脚本/事件/外链)。
3.3 保存图标
POST /admin/icon/item
| 字段 | 类型 | 必填 | 校验 | 说明 |
|---|---|---|---|---|
id |
Long | ❌ | — | 空=新建,非空=修改 |
categoryCode |
String | ✅ | ≤64 | 所属分类编码(新建时校验分类存在;修改时不可改) |
iconCode |
String | ✅ | ≤64 | 图标编码,同分类内唯一 |
name |
String | ✅ | ≤100 | 图标名称 |
svgContent |
String | ✅ | ≤256KB | SVG 字符串(来自 3.2 解析结果) |
color |
String | ❌ | ≤16 | 默认色值 hex,如 #FFCC00 |
size |
Integer | ❌ | — | 默认尺寸(像素) |
keywords |
String | ❌ | ≤255 | 搜索关键词(逗号分隔) |
sort |
Integer | ❌ | — | 排序号,默认 0 |
status |
String | ❌ | ACTIVE/DISABLED |
状态,默认 ACTIVE |
remark |
String | ❌ | ≤255 | 备注 |
注意(SaveReqVO 全量快照语义):修改时请回填全部字段(先用 3.5 详情拉取再改),未传的可空字段会被覆盖为空。
categoryCode/iconCode创建后不可变,修改时会被忽略。
错误码:210701 分类编码已存在 / 210702 分类不存在 / 210703 分类下有图标不可删 / 210751 同分类图标编码已存在 / 210752 图标不存在。
3.4 图标分页
GET /admin/icon/item/page?page=1&pageSize=20&categoryCode=weather&keyword=晴
categoryCode精确过滤(可空);keyword模糊匹配 iconCode/name/keywords(可空)。- 返回标准分页结构
{records, total, page, pageSize},records[]为完整图标 VO(含 svgContent)。
3.5 图标详情
GET /admin/icon/item/{id} → 返回完整图标 VO(含 svgContent/color/size/keywords/status 等)。
3.6【公开】小程序查询
GET /mp/icon/categories (无需 token)
curl "https://api.test.1814.love:9443/mp/icon/categories"
# 返回: {"code":200,"data":[{"categoryCode":"weather","categoryName":"天气","sort":1}, ...]}
GET /mp/icon/items?categoryCode=weather (无需 token)
curl "https://api.test.1814.love:9443/mp/icon/items?categoryCode=weather"
# 返回: {"code":200,"data":[
# {"iconCode":"sunny","name":"晴天","svgContent":"<svg ...>...</svg>","color":"#FFCC00","size":24}, ...]}
- 只返回
ACTIVE分类下的ACTIVE图标;分类被禁用时该分类的图标不再返回。 categoryCode必填(缺失返 400)。
4. 前端对接要点
- 新增图标流程:上传
.svg→ 调 3.2 拿svgContent→ 填表单(补 color/size/分类/编码)→ 调 3.3 保存。 - 渲染 SVG:拿到
svgContent直接内联渲染;务必先做一次前端 sanitize(后端已拒明显恶意内容,前端 sanitize 作纵深防御)。 - 小程序拉图标:先
/mp/icon/categories拿启用分类,再用categoryCode调/mp/icon/items。两端点均公开无需 token。 - 状态语义:
ACTIVE启用 /DISABLED禁用;mp 端只见 ACTIVE。
本模块已在测试服完成端到端验证(admin CRUD + 上传解析 + mp 公开查询 + 大 SVG 入库 + 恶意 SVG 拒绝)。如需调整字段或新增端点请反馈。