docs(tag-library): 新增 v5.14 私人标签库 6 接口契约(管理后台)
- GET /v3/admin/tag-library 查我的标签库(keyword/pinnedOnly)
- POST /v3/admin/tag-library 新增预设(tagName/tagColor)
- PUT /v3/admin/tag-library/{id} 修改预设(重命名/换色)
- DELETE /v3/admin/tag-library/{id} 软删预设
- PUT /v3/admin/tag-library/sort 批量排序(拖拽)
- PUT /v3/admin/tag-library/{id}/pin 置顶/取消置顶 toggle
错误码段位 581420-581429(hl-order-service-v3 拥有)。
当前 ServiceImpl 为 Mock 实现,契约本次发布后不再变。
关联 PR:wx/HL#2045(骨架)+ wx/HL#2240(Gateway 路由修复)
这个提交包含在:
父节点
a47588b9a4
当前提交
0bbcdd23e7
@ -0,0 +1,500 @@
|
||||
# tag-library: 私人标签库管理 6 接口(v5.14 骨架)
|
||||
|
||||
> **存放目录**: `changelogs-v2/2026-05/`(v3 二期)
|
||||
> **服务**: hl-order-service-v3(端口 8086)
|
||||
> **PR**: #2045(骨架合并)/ #2240(Gateway 路由修复)
|
||||
> **Issue**: #2045(v5.14 §14 章节实现)
|
||||
> **日期**: 2026-05-19
|
||||
> **影响范围**: 管理后台「我的标签库」管理页 + 创建订单时的标签下拉
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化(首次发布无)
|
||||
|
||||
本次为首次发布;非纠错变更。
|
||||
|
||||
⚠️ **当前实现状态**:6 个接口已上线,**ServiceImpl 为 Mock 实现**(返回硬编码样例数据),真实 DB 业务在跟进中。**接口契约本次发布后不再变**,前端可按契约对接联调。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
v5.14 引入「私人标签库」概念:每个管理员账号有自己独立的标签预设库,可在订单创建/编辑页打标签时从下拉中选取(取代以前自由文本输入)。
|
||||
|
||||
**模型隔离**:
|
||||
- userId 从 JWT 自动派生,前端**不传**操作人
|
||||
- 仅本人可读/写/排序/置顶/删除自己的库
|
||||
- 删除标签库预设**不**影响已挂订单上的历史 order_tag 实例(保留快照)
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 |
|
||||
|---|------|------|------|----------|
|
||||
| 1 | 查我的标签库 | GET | `/v3/admin/tag-library` | 新增 |
|
||||
| 2 | 新增标签库预设 | POST | `/v3/admin/tag-library` | 新增 |
|
||||
| 3 | 修改标签库预设(重命名/换色) | PUT | `/v3/admin/tag-library/{id}` | 新增 |
|
||||
| 4 | 删除标签库预设(软删) | DELETE | `/v3/admin/tag-library/{id}` | 新增 |
|
||||
| 5 | 批量排序标签库(拖拽) | PUT | `/v3/admin/tag-library/sort` | 新增 |
|
||||
| 6 | 置顶/取消置顶(toggle) | PUT | `/v3/admin/tag-library/{id}/pin` | 新增 |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 查我的标签库 `GET /v3/admin/tag-library`
|
||||
|
||||
**VO**: `TagLibraryListReqVO` → `Result<List<TagLibraryItemVO>>`
|
||||
|
||||
#### 入参(Query)
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| keyword | Query | String | ❌ | - | 模糊搜索标签名(管理页搜索框) |
|
||||
| pinnedOnly | Query | Boolean | ❌ | 默认 false | 只返置顶 |
|
||||
|
||||
> userId 从 JWT 自动注入,前端**不要**在 Query 里传 userId。
|
||||
|
||||
#### 出参 `Result<List<TagLibraryItemVO>>`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | Long | 标签库行 ID(雪花,前端按字符串处理避免精度丢失) |
|
||||
| tagName | String | 标签名 |
|
||||
| tagColor | String | HEX 色值(如 `#5B8FF9`) |
|
||||
| sortOrder | Integer | 排序值 |
|
||||
| isPinned | Boolean | 是否置顶 |
|
||||
| usedCount | Integer | 使用次数 |
|
||||
| lastUsedAt | LocalDateTime | 最近使用时间(ISO 8601,可能为 null) |
|
||||
|
||||
**列表排序**:`isPinned DESC, sortOrder ASC, lastUsedAt DESC`(后端固定,前端不要前端再排)
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```
|
||||
GET /v3/admin/tag-library?keyword=高&pinnedOnly=false
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 2050000000000001001,
|
||||
"tagName": "高净值客户",
|
||||
"tagColor": "#FF6B6B",
|
||||
"sortOrder": 1,
|
||||
"isPinned": true,
|
||||
"usedCount": 12,
|
||||
"lastUsedAt": "2026-05-15T14:32:10"
|
||||
},
|
||||
{
|
||||
"id": 2050000000000001002,
|
||||
"tagName": "高频复购",
|
||||
"tagColor": "#5B8FF9",
|
||||
"sortOrder": 2,
|
||||
"isPinned": false,
|
||||
"usedCount": 5,
|
||||
"lastUsedAt": "2026-05-10T09:15:00"
|
||||
}
|
||||
],
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据响应(首次访问无库)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": [],
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 新增标签库预设 `POST /v3/admin/tag-library`
|
||||
|
||||
**VO**: `TagLibraryCreateReqVO` → `Result<TagLibraryItemVO>`
|
||||
|
||||
#### 入参(Body)
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| tagName | Body | String | ✅ | 非空,长度 ≤ 50 | 标签名 |
|
||||
| tagColor | Body | String | ❌ | HEX 格式 | 默认 `#5B8FF9` |
|
||||
|
||||
#### 出参 `Result<TagLibraryItemVO>`
|
||||
|
||||
字段同接口 1 出参(返回新建行完整字段,`sortOrder` 追加到末尾,`isPinned=false`,`usedCount=0`,`lastUsedAt=null`)。
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"tagName": "VIP",
|
||||
"tagColor": "#FFD700"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 2050000000000001005,
|
||||
"tagName": "VIP",
|
||||
"tagColor": "#FFD700",
|
||||
"sortOrder": 6,
|
||||
"isPinned": false,
|
||||
"usedCount": 0,
|
||||
"lastUsedAt": null
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应(重名)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581421,
|
||||
"message": "标签名已存在",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 修改标签库预设 `PUT /v3/admin/tag-library/{id}`
|
||||
|
||||
**VO**: `TagLibraryUpdateReqVO` → `Result<TagLibraryItemVO>`
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| id | Path | Long | ✅ | - | 标签库行 ID |
|
||||
| tagName | Body | String | ❌ | 长度 ≤ 50 | 不传则不改 |
|
||||
| tagColor | Body | String | ❌ | HEX | 不传则不改 |
|
||||
|
||||
> 两字段都不传 → 等同空操作(不报错)。
|
||||
|
||||
#### 出参
|
||||
|
||||
返回修改后的完整 `TagLibraryItemVO`(字段同接口 1)。
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"tagName": "VIP-Gold",
|
||||
"tagColor": "#FFA500"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": 2050000000000001005,
|
||||
"tagName": "VIP-Gold",
|
||||
"tagColor": "#FFA500",
|
||||
"sortOrder": 6,
|
||||
"isPinned": false,
|
||||
"usedCount": 0,
|
||||
"lastUsedAt": null
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应(非本人标签)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581423,
|
||||
"message": "无权操作该标签",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 删除标签库预设 `DELETE /v3/admin/tag-library/{id}`
|
||||
|
||||
**VO**: - → `Result<Boolean>`
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| id | Path | Long | ✅ | - | 标签库行 ID |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Boolean | true=删除成功 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```
|
||||
DELETE /v3/admin/tag-library/2050000000000001005
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": true,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应(id 不存在)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581424,
|
||||
"message": "标签不存在或已删除",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 批量排序标签库 `PUT /v3/admin/tag-library/sort`
|
||||
|
||||
**VO**: `TagLibrarySortReqVO` → `Result<Boolean>`
|
||||
|
||||
#### 入参(Body)
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| items | Body | Array | ✅ | 非空 | 批量排序映射 |
|
||||
| items[].id | - | Long | ✅ | - | 标签库行 ID |
|
||||
| items[].sortOrder | - | Integer | ✅ | - | 新排序值 |
|
||||
|
||||
> 仅会更新本人库的行;若 `items` 含非本人 id,**整批拒绝**(581425),不部分成功。
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Boolean | true=全量成功 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{ "id": 2050000000000001001, "sortOrder": 1 },
|
||||
{ "id": 2050000000000001002, "sortOrder": 2 },
|
||||
{ "id": 2050000000000001005, "sortOrder": 3 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": true,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应(混入他人 id)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581425,
|
||||
"message": "排序列表含非本人标签 ID",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 置顶/取消置顶 `PUT /v3/admin/tag-library/{id}/pin`
|
||||
|
||||
**VO**: `TagLibraryPinReqVO` → `Result<Boolean>`
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| id | Path | Long | ✅ | - | 标签库行 ID |
|
||||
| pinned | Body | Boolean | ✅ | - | `true`=置顶 / `false`=取消置顶 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Boolean | true=操作成功 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"pinned": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": true,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应(非本人)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581423,
|
||||
"message": "无权操作该标签",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### userId 派生规则
|
||||
|
||||
| 场景 | payload |
|
||||
|------|---------|
|
||||
| ✅ 调用任一接口 | 仅传业务字段,不传 userId(后端从 JWT 取) |
|
||||
| ❌ Body / Query 传 userId | 后端忽略(不报错但无效) |
|
||||
|
||||
### 排序接口幂等性
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| ✅ 整列重新提交全量 items | 全量覆盖排序 |
|
||||
| ✅ 提交部分 items | 仅更新提交的行,其他行 sort_order 保持 |
|
||||
| ❌ items 含他人 id | 581425 整批拒绝(不部分成功) |
|
||||
|
||||
### tagColor 兜底
|
||||
|
||||
- POST 不传 `tagColor` → 写入 `#5B8FF9`(深蓝默认)
|
||||
- PUT 不传 `tagColor` → 不改原值
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
| 前端动作 | 数据库影响 |
|
||||
|----------|-----------|
|
||||
| POST 新建 | `tag_library` 表插入一行,`user_id` 取 JWT,`sort_order = MAX(sort_order)+1`,`is_pinned=false`,`used_count=0` |
|
||||
| PUT 修改 | 仅更新 `tag_name` / `tag_color`;不传字段不动 |
|
||||
| DELETE | 软删(`deleted=1`),不物理删;不级联清理已挂订单的历史 `order_tag` |
|
||||
| PUT sort | 批量 UPDATE `sort_order`,事务包裹(全成功或全回滚) |
|
||||
| PUT pin | 仅更新 `is_pinned`,`updated_at` 同步刷新 |
|
||||
|
||||
**软删与订单标签的关系**:
|
||||
|
||||
| 时间线 | 状态 |
|
||||
|--------|------|
|
||||
| T0:管理员将"VIP"标签挂到订单 A | `order_tag` 表插入快照(tag_name="VIP", tag_color="#FFD700") |
|
||||
| T1:管理员从标签库删除"VIP"预设 | `tag_library.deleted=1`;`order_tag` 表**不变** |
|
||||
| T2:再次打开订单 A | 仍显示"VIP"标签(快照),但下拉里不再出现 |
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 未登录 → 401(网关拦截)
|
||||
- 列表为空(首次访问/全部删完)→ `Result.success([])`,**不**返 404 / 不返 581420
|
||||
- DELETE/PUT 操作不存在的 id → 581424
|
||||
- DELETE/PUT 操作他人 id → 581423
|
||||
- 排序提交空 items 数组 → 400(`@NotEmpty`)
|
||||
- 新增标签名同用户已存在 → 581421
|
||||
- 新增标签名超长 / 空 / 全空格 → 400 / 581422
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 管理后台「我的标签库」管理页 + 订单页打标签下拉
|
||||
- **零影响**:
|
||||
- C 端小程序(无 /mp 接口)
|
||||
- 订单创建 / 编辑接口(订单上的 `order_tag` 写入是另外的接口链路,不在本批)
|
||||
- 历史已挂订单的标签(快照不动)
|
||||
- 其他管理员的标签库(用户隔离)
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
⚠️ **当前实现为 Mock**:ServiceImpl 返回硬编码样例数据,未走 DB。前端可按本契约对接联调;联调通过后后端会接入真实 DB(**契约不再变**)。
|
||||
|
||||
```
|
||||
GET /v3/admin/tag-library → 200 + 5 条 Mock 样例 ✓
|
||||
POST /v3/admin/tag-library → 200 + 新增样例回显 ✓
|
||||
PUT /v3/admin/tag-library/{id} → 200 + 修改样例回显 ✓
|
||||
DELETE /v3/admin/tag-library/{id} → 200 + true ✓
|
||||
PUT /v3/admin/tag-library/sort → 200 + true ✓
|
||||
PUT /v3/admin/tag-library/{id}/pin → 200 + true ✓
|
||||
```
|
||||
|
||||
Swagger UI:`http://<host>:8086/doc.html` → 找「[admin] 私人标签库管理(v5.14)」分组。
|
||||
|
||||
---
|
||||
|
||||
## 九、相关历史 PR
|
||||
|
||||
| PR | Issue | 说明 | 是否仍有效 |
|
||||
|----|-------|------|------------|
|
||||
| #2045 | - | 首发:6 接口空骨架 + Mock ServiceImpl + 错误码段位 581420-581429 | ✅ 有效 |
|
||||
| #2240 | - | 修复 Gateway 缺 `/v3/admin/tag-library/**` 路由(404 修复) | ✅ 有效 |
|
||||
|
||||
---
|
||||
|
||||
## 十、错误码
|
||||
|
||||
| 错误码 | 含义 | 触发场景 |
|
||||
|--------|------|---------|
|
||||
| 400 | 参数校验失败 | tagName 空 / 超长 / items 空 |
|
||||
| 401 | 未登录 | 无 JWT 或 JWT 过期 |
|
||||
| 581420 | 当前用户暂无标签库 | 保留位(当前实现不抛此错,空库返 `[]`) |
|
||||
| 581421 | 标签名已存在 | POST 时同用户已有同名标签 |
|
||||
| 581422 | 标签名格式非法或长度超过 50 字符 | 含特殊字符 / 长度 > 50(兜底校验) |
|
||||
| 581423 | 无权操作该标签 | PUT/DELETE/PIN 操作他人的 id |
|
||||
| 581424 | 标签不存在或已删除 | id 不存在 / 已软删 |
|
||||
| 581425 | 排序列表含非本人标签 ID | sort 接口 items 混入他人 id |
|
||||
|
||||
---
|
||||
|
||||
## 十一、相关文档
|
||||
|
||||
- 关联 PR(骨架):[wx/HL#2045](https://git.1814.love:8443/wx/HL/pulls/2045)
|
||||
- 关联 PR(路由修复):[wx/HL#2240](https://git.1814.love:8443/wx/HL/pulls/2240)
|
||||
- 后续:真实 DB 业务实现 PR 合并后将追加一条 follow-up changelog(契约不变,仅去掉 Mock 标注)
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户