feat: add notification feature

This commit is contained in:
luo
2026-07-29 10:25:13 +08:00
parent d115363115
commit d060edb698
11 changed files with 785 additions and 10 deletions

305
docs/通知.md Normal file
View File

@@ -0,0 +1,305 @@
# 通知接口
服务地址:`https://cmp-api.boss160.cn`
认证方式:`Bearer <JWT>`
## 通知枚举
### 通知类别
| 值 | 说明 |
| ---------- | --- |
| `approval` | 审批 |
| `expiry` | 临期 |
| `sync` | 同步 |
| `system` | 系统 |
### 通知级别
| 值 | 说明 |
| ---------- | --- |
| `info` | 提示 |
| `warning` | 警告 |
| `error` | 错误 |
| `critical` | 严重 |
## 查询通知未读数
### GET /api/admin/notifications/unread-count
查询当前认证后台账号的未过期未读通知数量,超过 99 条时显示 `99+`
#### 成功响应
```json
{
"code": 0,
"data": {
"count": 120,
"display_count": "99+"
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
| 字段 | 类型 | 说明 |
| ------------------ | ------- | --------------------- |
| data.count | integer | 未读通知数量 |
| data.display_count | string | 徽标显示文本,超过 99 时为 `99+` |
## 查询通知未读分类汇总
### GET /api/admin/notifications/unread-summary
查询当前账号未过期通知的总未读数及分类数量。
#### 成功响应
```json
{
"code": 0,
"data": {
"approval": 2,
"expiry": 3,
"sync": 1,
"system": 0,
"total": 6
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
| 字段 | 类型 | 说明 |
| ------------- | ------- | ------- |
| data.approval | integer | 审批类未读数量 |
| data.expiry | integer | 临期类未读数量 |
| data.sync | integer | 同步类未读数量 |
| data.system | integer | 系统类未读数量 |
| data.total | integer | 未读通知总数 |
## 查询通知列表
### GET /api/admin/notifications
查询当前认证后台账号的未过期通知,按创建时间和通知 ID 倒序返回。
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
| --------- | ------- | --- | ---------------------------------------- |
| category | string | 否 | 通知类别:`approval``expiry``sync``system` |
| type | string | 否 | 稳定通知类型 |
| severity | string | 否 | 通知级别:`info``warning``error``critical` |
| is_read | boolean | 否 | 已读状态;不传查询全部 |
| page | integer | 否 | 页码,默认 1范围 110000 |
| page_size | integer | 否 | 每页数量,默认 20范围 150 |
#### 成功响应
```json
{
"code": 0,
"data": {
"items": [
{
"id": 1,
"category": "approval",
"type": "refund_approval_pending",
"severity": "warning",
"title": "退款审批待处理",
"body": "退款单 REFUND202607250001 正在审批中",
"is_read": false,
"read_at": null,
"ref_type": "refund",
"ref_id": "100",
"ref_key": "REFUND202607250001",
"created_at": "2026-07-25T00:00:00Z"
}
],
"page": 1,
"size": 20,
"total": 1
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
#### 返回字段
| 字段 | 类型 | 说明 |
| ------------------ | ----------- | ---------------- |
| data.items | array | 通知列表 |
| data.page | integer | 当前页码 |
| data.size | integer | 每页数量 |
| data.total | integer | 总数量 |
| items[].id | integer | 通知 ID |
| items[].category | string | 通知类别 |
| items[].type | string | 稳定通知类型 |
| items[].severity | string | 通知级别 |
| items[].title | string | 纯文本标题 |
| items[].body | string | 纯文本正文 |
| items[].is_read | boolean | 是否已读 |
| items[].read_at | string/null | 首次已读时间 |
| items[].ref_type | string | 受控资源类型,不是前端路由 |
| items[].ref_id | string | 受控资源数字 ID 字符串 |
| items[].ref_key | string | 受控资源稳定 Key 或展示快照 |
| items[].created_at | string | 创建时间 |
## 标记单条通知已读
### PUT /api/admin/notifications/{id}/read
标记当前账号的一条通知为已读。通知不存在、属于其他账号或已经已读时均幂等成功。
#### 路径参数
| 参数 | 类型 | 必填 | 说明 |
| --- | ------- | --- | ------------ |
| id | integer | 是 | 通知 ID最小值为 0 |
#### 请求体
| 字段 | 类型 | 必填 | 说明 |
| --- | ------- | --- | --------------- |
| id | integer | 是 | 通知 ID必须与路径参数一致 |
请求示例:
```json
{
"id": 1
}
```
#### 成功响应
```json
{
"code": 0,
"data": {
"success": true
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
## 批量标记通知已读
### PUT /api/admin/notifications/read-all
批量标记当前账号通知为已读。未传类别时更新全部未过期未读通知,传类别时只更新对应类别。
#### 请求体
| 字段 | 类型 | 必填 | 说明 |
| -------- | ------ | --- | ---------------------------------------- |
| category | string | 否 | 通知类别:`approval``expiry``sync``system` |
请求示例:
```json
{
"category": "approval"
}
```
#### 成功响应
```json
{
"code": 0,
"data": {
"updated_count": 2
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
| 字段 | 类型 | 说明 |
| ------------------ | ------- | ----------- |
| data.updated_count | integer | 本次实际更新的通知数量 |
## 解析通知受控目标
### GET /api/admin/notifications/{id}/target
校验通知属于当前账号后,复核目标资源当前权限,返回白名单结构化目标。不会返回任意 URL。
#### 路径参数
| 参数 | 类型 | 必填 | 说明 |
| --- | ------- | --- | ------------ |
| id | integer | 是 | 通知 ID最小值为 0 |
#### 成功响应
```json
{
"code": 0,
"data": {
"available": true,
"target_type": "refund_detail",
"target_id": 100,
"target_key": "REFUND202607250001"
},
"msg": "success",
"timestamp": "2026-07-25T00:00:00Z"
}
```
| 字段 | 类型 | 说明 |
| ---------------- | ------------ | ------------------ |
| data.available | boolean | 当前账号是否仍可访问目标 |
| data.target_type | string | 前端白名单目标类型,空表示不支持跳转 |
| data.target_id | integer/null | ID 型目标业务主键 |
| data.target_key | string | Key 型目标稳定定位值 |
### target_type 白名单
| 值 | 说明 |
| ----------------------- | ------- |
| `refund_detail` | 退款详情 |
| `agent_recharge_detail` | 代理充值详情 |
| `wecom_approval_detail` | 企微审批详情 |
| `iot_card_detail` | IoT 卡详情 |
| `device_detail` | 设备详情 |
| `expiring_asset_list` | 临期资产列表 |
| `shop_fund_summary` | 店铺资金概况 |
| `system_config` | 系统配置 |
## 业务规则
- 通知只返回当前认证后台账号的未过期通知。
- `ref_type``ref_id``ref_key` 是受控资源引用,不是前端 URL。
- 点击通知后必须调用目标解析接口,由 `target_type``available` 决定是否跳转。
- `available=false``target_type` 为空时,只展示通知正文,不执行跳转。
- 前端维护 `target_type` 到页面的白名单映射,不得根据通知字段拼接任意 URL。
- 已读操作必须支持幂等调用。
## 错误响应
适用于以上接口:
| HTTP 状态码 | 说明 |
| -------- | --------- |
| 400 | 请求参数错误 |
| 401 | 未认证或认证已过期 |
| 403 | 无权访问 |
| 500 | 服务器内部错误 |
错误响应示例:
```json
{
"code": 1001,
"data": {},
"msg": "参数验证失败",
"timestamp": "2026-07-25T00:00:00Z"
}
```