docs(changelog-v2): 图标库 changelog 按团队样板逐端点重写(11接口详情+枚举+错误码汇总+业务边界)
这个提交包含在:
父节点
7b8c70f514
当前提交
8b1b3cd1cb
@ -1,30 +1,37 @@
|
||||
# 【新增接口·管理后台 + 小程序】图标库模块(全模块首次推送,11 接口)
|
||||
# 【新增接口·管理后台 + 小程序】图标库模块 11 接口(全模块首次推送 changelog)
|
||||
|
||||
> **PR**: #3396(模块主体)+ #3398 / #3399(mp 端点公开化)+ #3401(上线后审计加固)
|
||||
> **服务**: hl-user-service | **更新时间**: 2026-06-03
|
||||
>
|
||||
> **存放目录**: `changelogs-v2/2026-06/`
|
||||
> **影响范围**: 管理后台「图标库」管理页(分类 + 图标 CRUD + SVG 上传解析);小程序/前端 图标渲染(公开只读查询)
|
||||
> **影响范围**: 管理后台「图标库」管理页(分类 + 图标 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` 为像素整数,均为可选展示属性。
|
||||
图标库是后台统一维护的一套 SVG 图标,结构类似数据字典:一级「图标分类」(`icon_category`) + 二级「图标项」(`icon_item`),图标项归属某分类编码 `categoryCode`。
|
||||
|
||||
**SVG 直接存库(不走 OSS)**:图标内容是 SVG 字符串,直存数据库 `svg_content`(MEDIUMTEXT,单图标后端限 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 图标,供小程序 / 前端按分类拉取渲染(如分类入口图标、标签图标等)。
|
||||
|
||||
- 管理后台「图标库」页:分类增删改查 + 图标增删改查 + 上传 SVG 解析。
|
||||
- 小程序/前端:按分类拉启用图标列表渲染。
|
||||
管理后台「图标库」页提供:
|
||||
- 分类维护(列表 / 分页 / 新增 / 编辑 / 删除)
|
||||
- 图标维护(分页 / 详情 / 新增 / 编辑 / 删除)
|
||||
- 上传 SVG 文件解析为字符串
|
||||
|
||||
小程序 / 前端:按分类拉启用图标列表渲染(公开只读)。
|
||||
|
||||
---
|
||||
|
||||
@ -32,24 +39,24 @@
|
||||
|
||||
### 管理后台(`/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 文件解析为字符串(不落库) |
|
||||
| # | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|----------|------|
|
||||
| 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?categoryCode=xxx` | 某分类下启用图标列表 |
|
||||
| # | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|----------|------|
|
||||
| 10 | GET | `/mp/icon/categories` | 首推 | 启用分类列表 |
|
||||
| 11 | GET | `/mp/icon/items` | 首推 | 某分类下启用图标列表 |
|
||||
|
||||
---
|
||||
|
||||
@ -59,100 +66,494 @@
|
||||
|
||||
**POST** `/admin/icon/category`
|
||||
|
||||
- **使用场景**:图标库分类新增 / 编辑
|
||||
- **认证**:管理后台 JWT
|
||||
- **幂等**:否(同 `categoryCode` 重复新建抛 210701)
|
||||
|
||||
**Body 入参**(`IconCategorySaveReqVO`):
|
||||
|
||||
| 字段 | 类型 | 必填 | 校验 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| `id` | Long | ❌ | — | 空=新建,非空=修改 |
|
||||
| `categoryCode` | String | ✅ | ≤64 | 分类编码,全局唯一,创建后不可改 |
|
||||
| `categoryName` | String | ✅ | ≤100 | 分类名称 |
|
||||
| `sort` | Integer | ❌ | — | 排序号,升序,默认 0 |
|
||||
| `status` | String | ❌ | `ACTIVE`/`DISABLED` | 状态,默认 ACTIVE |
|
||||
| `remark` | String | ❌ | ≤255 | 备注 |
|
||||
| `categoryCode` | string | ✅ | `@Size(max=64)` | 分类编码(全局唯一,创建后不可改) |
|
||||
| `categoryName` | string | ✅ | `@Size(max=100)` | 分类名称 |
|
||||
| `sort` | int | ❌ | — | 排序号(升序),默认 0 |
|
||||
| `status` | string | ❌ | `@Pattern(ACTIVE\|DISABLED)` | 状态,默认 ACTIVE |
|
||||
| `remark` | string | ❌ | `@Size(max=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
|
||||
**出参**:`Result<Long>`(`data` = 分类 ID)
|
||||
|
||||
**典型示例 请求**:
|
||||
```
|
||||
POST /admin/icon/category
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
{ "categoryCode": "weather", "categoryName": "天气", "sort": 1 }
|
||||
```
|
||||
|
||||
### 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 }}
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{ "code": 200, "data": 2062087054098898945, "message": "成功" }
|
||||
```
|
||||
|
||||
- 校验失败错误码:`210753` 文件为空 / `210754` 非合法 SVG(非 XML 或根非 svg)/ `210755` 超 256KB / `210757` 含不安全内容(脚本/事件/外链)。
|
||||
**异常 响应**(编码重复):
|
||||
```json
|
||||
{ "code": 210701, "message": "图标分类编码已存在", "data": null }
|
||||
```
|
||||
|
||||
### 3.3 保存图标
|
||||
**错误码**: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 | 分类 ID(Long 序列化为 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`)
|
||||
|
||||
**异常 响应**(分类下仍有图标项):
|
||||
```json
|
||||
{ "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)
|
||||
```
|
||||
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{
|
||||
"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 | ✅ | ≤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 | 备注 |
|
||||
| `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)` | 备注 |
|
||||
|
||||
> **注意(SaveReqVO 全量快照语义)**:修改时请回填全部字段(先用 3.5 详情拉取再改),未传的可空字段会被覆盖为空。`categoryCode`/`iconCode` 创建后不可变,修改时会被忽略。
|
||||
**出参**:`Result<Long>`(`data` = 图标 ID)
|
||||
|
||||
错误码:`210701` 分类编码已存在 / `210702` 分类不存在 / `210703` 分类下有图标不可删 / `210751` 同分类图标编码已存在 / `210752` 图标不存在。
|
||||
**典型示例 请求**:
|
||||
```
|
||||
POST /admin/icon/item
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
### 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}, ...]}
|
||||
{
|
||||
"categoryCode": "weather",
|
||||
"iconCode": "sunny",
|
||||
"name": "晴天",
|
||||
"svgContent": "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\"/></svg>",
|
||||
"color": "#FFCC00",
|
||||
"size": 24,
|
||||
"keywords": "晴,太阳,sunny"
|
||||
}
|
||||
```
|
||||
|
||||
**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}, ...]}
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{ "code": 200, "data": 2062087056506441730, "message": "成功" }
|
||||
```
|
||||
|
||||
- 只返回 `ACTIVE` 分类下的 `ACTIVE` 图标;分类被禁用时该分类的图标不再返回。
|
||||
- `categoryCode` 必填(缺失返 400)。
|
||||
**异常 响应**(SVG 含脚本 / 事件 / 外链):
|
||||
```json
|
||||
{ "code": 210757, "message": "SVG 含不安全内容(脚本/事件/外链),请清理后重试", "data": null }
|
||||
```
|
||||
|
||||
**错误码**:400 参数校验 / **210702** 分类不存在 / **210751** 同分类图标编码已存在 / **210752** 图标不存在(修改时 id 无效)/ **210754** SVG 非法 / **210755** SVG 超 256KB / **210757** SVG 含不安全内容 / 401 未登录
|
||||
|
||||
> **SaveReqVO 全量快照语义**:修改时请先用 §3.8 详情拉取再整体回填提交,未传的可空字段会被覆盖为空。`categoryCode` / `iconCode` 创建后不可变,修改时被忽略。
|
||||
|
||||
---
|
||||
|
||||
## 4. 前端对接要点
|
||||
### 3.7 图标分页
|
||||
|
||||
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。
|
||||
**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 未登录
|
||||
|
||||
---
|
||||
|
||||
> 本模块已在测试服完成端到端验证(admin CRUD + 上传解析 + mp 公开查询 + 大 SVG 入库 + 恶意 SVG 拒绝)。如需调整字段或新增端点请反馈。
|
||||
### 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 | 时间 |
|
||||
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
```
|
||||
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
```
|
||||
|
||||
**典型示例 响应**:
|
||||
```json
|
||||
{
|
||||
"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 / #3399(mp 公开化)/ #3401(审计加固)
|
||||
- **关联工单**:#3390
|
||||
|
||||
### 13.2 联系人
|
||||
|
||||
- **后端负责人**: @wx
|
||||
- **前端对接(管理后台 + 小程序)**: 待指派
|
||||
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户