docs(changelog-v2): 图标库模块全套接口契约(admin 9 + mp 2)
这个提交包含在:
父节点
2322a0cd49
当前提交
7b8c70f514
@ -0,0 +1,158 @@
|
|||||||
|
# 【新增接口·管理后台 + 小程序】图标库模块(全模块首次推送,11 接口)
|
||||||
|
|
||||||
|
> **PR**: #3396(模块主体)+ #3398 / #3399(mp 端点公开化)+ #3401(上线后审计加固)
|
||||||
|
> **服务**: hl-user-service | **更新时间**: 2026-06-03
|
||||||
|
>
|
||||||
|
> **存放目录**: `changelogs-v2/2026-06/`
|
||||||
|
> **影响范围**: 管理后台「图标库」管理页(分类 + 图标 CRUD + SVG 上传解析);小程序/前端 图标渲染(公开只读查询)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键说明
|
||||||
|
|
||||||
|
1. **两级结构(类似数据字典)**:一级「图标分类」(`icon_category`) + 二级「图标项」(`icon_item`)。图标项归属某分类编码 `categoryCode`。
|
||||||
|
2. **SVG 直接存库(不走 OSS)**:图标内容是 SVG 字符串,直存数据库 `svg_content`(MEDIUMTEXT,上限 16MB,单图标后端限 256KB)。前端拿到的 `svgContent` 即可直接渲染。
|
||||||
|
3. **上传解析与保存分两步**:先调 `POST /admin/icon/parse-svg` 上传 `.svg` 文件,后端读成字符串校验后**只返回字符串不落库**;前端拿到 `svgContent` 再连同 `color`/`size` 等填进保存表单调 `POST /admin/icon/item` 保存。
|
||||||
|
4. **SVG 安全校验(重要)**:上传/保存的 SVG 会经服务端 XXE-safe 解析校验,**拒绝**含 `<script>`、`<foreignObject>`、`on*` 事件属性(onload/onclick…)、`javascript:` 外链的内容(返回错误码 `210757`);非合法 XML 或根元素不是 `<svg>` 返回 `210754`。**前端渲染 SVG 时仍建议做一次 sanitize 作为纵深防御。**
|
||||||
|
5. **mp 查询端点公开无需 token**:`/mp/icon/categories`、`/mp/icon/items` 是 UI 参考数据(同字典),**无需登录、无需 token** 即可访问;只返回 `status=ACTIVE` 的分类与图标,且**分类被禁用时其图标也不再返回**。
|
||||||
|
6. **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 | 备注 |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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 文件)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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. 前端对接要点
|
||||||
|
|
||||||
|
1. **新增图标流程**:上传 `.svg` → 调 3.2 拿 `svgContent` → 填表单(补 color/size/分类/编码)→ 调 3.3 保存。
|
||||||
|
2. **渲染 SVG**:拿到 `svgContent` 直接内联渲染;**务必先做一次前端 sanitize**(后端已拒明显恶意内容,前端 sanitize 作纵深防御)。
|
||||||
|
3. **小程序拉图标**:先 `/mp/icon/categories` 拿启用分类,再用 `categoryCode` 调 `/mp/icon/items`。两端点均公开无需 token。
|
||||||
|
4. **状态语义**:`ACTIVE` 启用 / `DISABLED` 禁用;mp 端只见 ACTIVE。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 本模块已在测试服完成端到端验证(admin CRUD + 上传解析 + mp 公开查询 + 大 SVG 入库 + 恶意 SVG 拒绝)。如需调整字段或新增端点请反馈。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户