feat: 完善接口19-顶部通知铃铛与站内通知中心
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m5s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m5s
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
## Context
|
||||
|
||||
后端通知接口已经提供稳定的管理端 REST 合约。前端需要移除对旧字段和旧响应形态的依赖,统一使用真实接口返回的数据,并把通知目标解析限制在前端已注册的内部目标映射中。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals: 顶部铃铛、通知抽屉、通知中心和共享 Pinia 状态使用同一套真实 API 类型。
|
||||
- Goals: 保证未读数、分类汇总、列表已读状态在单条已读和全部已读后与后端同步。
|
||||
- Goals: 受控目标只能通过 `target_type`、`target_id`、`target_key` 白名单映射到内部页面。
|
||||
- Non-Goals: 不实现 C 端通知,不支持任意 URL 跳转,不改变通知产生规则。
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: 通知列表请求使用 `is_read?: boolean`,不再以 `read_status` 字符串代替后端参数。
|
||||
- Decision: 通知正文统一使用 `body`;必要时仅在迁移读取阶段兼容旧数据,不再把旧 `content` 作为 API 契约类型。
|
||||
- Decision: 顶部徽标优先使用 `unread-count.data.display_count`,数值统计使用 `data.count`;不在前端重新格式化徽标文本。
|
||||
- Decision: 未读汇总只保存四个固定分类计数和总数;抽屉最近10条通过 `GET /api/admin/notifications?page=1&page_size=10` 获取,避免把分类汇总误当通知列表。
|
||||
- Decision: 全部已读服务方法接收可选 `category`,请求体始终为对象;通知中心的“全部已读”传空对象,分类操作可传具体类别。
|
||||
- Decision: `target_type`、`target_key` 由前端白名单映射到已注册路由,`target_id` 仅作为参数;未知、无权限或已失效目标只展示正文,不跳转。
|
||||
- Alternatives considered: 继续保留旧字段的宽松兼容类型。Rejected because it会掩盖真实 API 字段错误,并导致筛选和已读状态请求不符合后端契约。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 风险:后端新增通知类型。Mitigation:类型字段保持 string,页面对未知类型采用原值展示。
|
||||
- 风险:受控目标类型新增。Mitigation:未知目标默认不可跳转,不执行后端返回的任意路径。
|
||||
- 风险:列表与汇总请求并行时数据短暂不一致。Mitigation:单条已读/全部已读成功后刷新未读数、汇总和当前列表。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 先更新类型、API service 和通知 store 的真实响应结构。
|
||||
2. 再更新顶部抽屉和通知中心字段、筛选及已读交互。
|
||||
3. 最后替换目标跳转映射并验证未知目标安全降级。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- `target_type` 与 `target_key` 的完整白名单值需要以后端实际返回样例补充;未确认值统一按不可跳转处理。
|
||||
@@ -0,0 +1,40 @@
|
||||
# Change: 按实际接口更新管理端通知中心
|
||||
|
||||
## Why
|
||||
|
||||
通知中心已有前端链路,但当前类型和页面仍兼容旧字段,例如 `content`、`read_status`、旧的未读摘要结构和可路由目标结构。后端接口文档已更新为实际可用的管理端 API,需要按真实响应字段和受控目标协议修正通知铃铛、抽屉、通知中心及状态同步逻辑。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 按实际接口更新通知项字段:`body`、`is_read`、`read_at`、`ref_type`、`ref_id`、`ref_key`。
|
||||
- 按实际接口更新通知列表筛选参数:使用 `is_read` boolean,支持 `category`、`type`、`severity`、`page`、`page_size`。
|
||||
- 接入真实未读数响应 `{ count, display_count }`,顶部徽标直接展示后端 `display_count`。
|
||||
- 接入真实未读分类汇总 `{ approval, expiry, sync, system, total }`,抽屉最近通知通过通知列表接口获取,最多展示10条。
|
||||
- 全部已读调用 `PUT /api/admin/notifications/read-all`,支持可选 `category` 请求体。
|
||||
- 受控目标按 `target_type`、`target_id`、`target_key` 建立前端白名单映射,不接受或执行任意 URL/路由字段。
|
||||
- 通知中心展示正文 `body`、读取状态 `is_read`、首次已读时间 `read_at`,并支持余额、审批、临期、同步/系统分类。
|
||||
- 本变更只覆盖管理端,明确不接入 C 端 `/api/c/v1/notifications`。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `notification-center`
|
||||
- Affected code:
|
||||
- `src/types/api/notification.ts`
|
||||
- `src/api/modules/notification.ts`
|
||||
- `src/store/modules/notification.ts`
|
||||
- `src/components/core/layouts/art-header-bar/index.vue`
|
||||
- `src/components/core/layouts/art-notification/index.vue`
|
||||
- `src/views/notifications/index.vue`
|
||||
- `src/utils/business/notificationNavigation.ts`
|
||||
- API contracts:
|
||||
- `GET /api/admin/notifications`
|
||||
- `PUT /api/admin/notifications/{id}/read`
|
||||
- `PUT /api/admin/notifications/read-all`
|
||||
- `GET /api/admin/notifications/unread-count`
|
||||
- `GET /api/admin/notifications/unread-summary`
|
||||
- `GET /api/admin/notifications/{id}/target`
|
||||
- Out of scope:
|
||||
- C 端 `/api/c/v1/notifications` 接口和页面
|
||||
- 创建、编辑或删除通知
|
||||
- WebSocket、浏览器推送和新的实时推送机制
|
||||
@@ -0,0 +1,84 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Admin Notification Contract
|
||||
|
||||
The admin notification frontend SHALL use the actual notification contract returned by `GET /api/admin/notifications`. Each item SHALL preserve `id`, `title`, `body`, `category`, `type`, `severity`, `is_read`, `created_at`, `read_at`, `ref_type`, `ref_id`, and `ref_key`.
|
||||
|
||||
#### Scenario: Display actual notification fields
|
||||
|
||||
- **WHEN** 管理端通知列表返回通知项
|
||||
- **THEN** 页面 MUST display `title` and `body`
|
||||
- **AND** 页面 MUST use `is_read` for read state
|
||||
- **AND** 页面 MUST preserve `read_at` and `ref_key`
|
||||
|
||||
### Requirement: Admin Notification List Filters
|
||||
|
||||
The notification center SHALL query `GET /api/admin/notifications` with `category`, `type`, `severity`, `is_read`, `page`, and `page_size`. The frontend SHALL not send `read_status` as a replacement for `is_read`.
|
||||
|
||||
#### Scenario: Filter unread notifications
|
||||
|
||||
- **WHEN** 用户选择未读筛选
|
||||
- **THEN** 前端 MUST send `is_read=false`
|
||||
- **AND** 页面 MUST render the paginated `data.items` response
|
||||
|
||||
#### Scenario: Filter read notifications
|
||||
|
||||
- **WHEN** 用户选择已读筛选
|
||||
- **THEN** 前端 MUST send `is_read=true`
|
||||
|
||||
### Requirement: Unread Count And Summary
|
||||
|
||||
The admin header and notification drawer SHALL use `GET /api/admin/notifications/unread-count` and `GET /api/admin/notifications/unread-summary`. The unread count response SHALL preserve `count` and `display_count`; the summary response SHALL preserve `approval`, `expiry`, `sync`, `system`, and `total`.
|
||||
|
||||
#### Scenario: Display backend unread badge
|
||||
|
||||
- **WHEN** 未读数接口返回 `count` and `display_count`
|
||||
- **THEN** 顶部铃铛 MUST display the backend `display_count`
|
||||
- **AND** shared notification state MUST preserve the numeric `count`
|
||||
|
||||
#### Scenario: Load drawer notifications
|
||||
|
||||
- **WHEN** 用户打开顶部通知抽屉
|
||||
- **THEN** 前端 MUST use the summary endpoint for category counts
|
||||
- **AND** 前端 MUST query the notification list endpoint with `page=1` and `page_size=10` for recent notifications
|
||||
|
||||
### Requirement: Read State Operations
|
||||
|
||||
The admin frontend SHALL mark notifications read through `PUT /api/admin/notifications/{id}/read` and mark notifications read in bulk through `PUT /api/admin/notifications/read-all`. The bulk request SHALL support an optional JSON `category` field.
|
||||
|
||||
#### Scenario: Mark one notification read
|
||||
|
||||
- **WHEN** 用户点击通知
|
||||
- **THEN** 前端 MUST call the single-read PUT endpoint before target resolution
|
||||
- **AND** after success MUST refresh the shared unread count and relevant notification state
|
||||
|
||||
#### Scenario: Mark all notifications read
|
||||
|
||||
- **WHEN** 用户点击全部已读
|
||||
- **THEN** 前端 MUST call `PUT /api/admin/notifications/read-all` with an object body
|
||||
- **AND** after success MUST refresh the list, summary and unread count
|
||||
|
||||
### Requirement: Controlled Notification Target
|
||||
|
||||
The admin frontend SHALL resolve notification navigation through `GET /api/admin/notifications/{id}/target`. It SHALL map only recognized `target_type` or `target_key` values to registered internal routes and SHALL use `target_id` as a route parameter when required.
|
||||
|
||||
#### Scenario: Navigate to recognized target
|
||||
|
||||
- **WHEN** 目标接口返回可用的受支持 `target_type`、`target_key` 和 `target_id`
|
||||
- **THEN** 前端 MUST navigate to the corresponding registered internal route
|
||||
- **AND** 前端 MUST not execute an arbitrary URL from the response
|
||||
|
||||
#### Scenario: Handle unknown target safely
|
||||
|
||||
- **WHEN** 目标类型未知、目标不可用或当前路由未注册
|
||||
- **THEN** 前端 MUST not navigate
|
||||
- **AND** 页面 MUST display the notification body or an unavailable-target message
|
||||
|
||||
### Requirement: Admin-Only Notification Scope
|
||||
|
||||
This notification capability SHALL apply only to the admin APIs under `/api/admin/notifications` and the admin `/notifications` interface. It SHALL not add or modify C-end `/api/c/v1/notifications` APIs or pages.
|
||||
|
||||
#### Scenario: Exclude C-end notifications
|
||||
|
||||
- **WHEN** this change is implemented
|
||||
- **THEN** C-end notification APIs and pages MUST remain outside the change scope
|
||||
@@ -0,0 +1,34 @@
|
||||
## 1. Contract And Types
|
||||
|
||||
- [x] 1.1 按实际接口更新通知项、列表响应、未读数、未读汇总和受控目标类型。
|
||||
- [x] 1.2 将通知列表筛选参数从 `read_status` 改为 `is_read?: boolean`。
|
||||
- [x] 1.3 更新通知 service:未读数、未读汇总、列表、单条已读、全部已读和目标接口的请求/响应类型。
|
||||
|
||||
## 2. Shared Notification State
|
||||
|
||||
- [x] 2.1 使用 `data.count` 和 `data.display_count` 同步未读数量及顶部徽标。
|
||||
- [x] 2.2 使用 `{ approval, expiry, sync, system, total }` 保存分类汇总,并通过通知列表接口加载抽屉最近10条。
|
||||
- [x] 2.3 单条已读和全部已读成功后刷新真实未读数、汇总和相关通知状态,避免仅修改本地计数。
|
||||
|
||||
## 3. Header And Notification Center
|
||||
|
||||
- [x] 3.1 顶部铃铛展示后端 `display_count`,支持0、普通数量和 `99+`。
|
||||
- [x] 3.2 抽屉展示最近10条真实通知,支持全部、审批、临期、同步/系统分类和进入通知中心。
|
||||
- [x] 3.3 通知中心使用 `body`、`is_read`、`read_at` 展示正文、状态和已读时间。
|
||||
- [x] 3.4 通知中心筛选使用 `category`、`type`、`severity`、`is_read`、`page`、`page_size`。
|
||||
- [x] 3.5 严重级别支持 `info`、`warning`、`error`、`critical`,未知值稳定展示。
|
||||
- [x] 3.6 全部已读和单条已读严格按实际 PUT 接口调用。
|
||||
|
||||
## 4. Controlled Navigation And Scope
|
||||
|
||||
- [x] 4.1 根据 `target_type`、`target_key` 和 `target_id`实现前端内部目标白名单映射。
|
||||
- [x] 4.2 未知、无权限或不可用目标只展示通知正文,不执行任意 URL 或未注册路由跳转。
|
||||
- [x] 4.3 保留管理端通知中心路由和顶部入口,不新增 C 端通知接口、页面或逻辑。
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- [x] 5.1 验证真实通知字段 `body`、`is_read`、`read_at`、`ref_key` 正确展示和保存。
|
||||
- [x] 5.2 验证未读徽标、分类汇总、抽屉列表和通知中心状态一致。
|
||||
- [x] 5.3 验证 `is_read=true/false` 筛选及全部已读 `category` 请求体。
|
||||
- [x] 5.4 验证受控目标白名单和未知目标安全降级。
|
||||
- [x] 5.5 运行类型检查、lint、相关测试、构建和 `openspec validate update-admin-notification-center-api --strict`。
|
||||
Reference in New Issue
Block a user