feat: 完善接口19-顶部通知铃铛与站内通知中心
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m5s

This commit is contained in:
luo
2026-07-25 10:20:53 +08:00
parent eb13763020
commit 9289a6e940
37 changed files with 1264 additions and 226 deletions

View File

@@ -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` 的完整白名单值需要以后端实际返回样例补充;未确认值统一按不可跳转处理。

View File

@@ -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、浏览器推送和新的实时推送机制

View File

@@ -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

View File

@@ -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`