docs: 更新通知系统设计方案
主要修改: - 添加 star_id 数据隔离字段到 notifications 和 notification_stats 表 - 更新索引包含 star_id - 添加数据一致性保证章节(事务边界、点赞通知防重复唯一约束) - 所有 API 接口参数增加 starID - JSON data 示例增加 star_id 字段 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
20f90d7120
commit
ec46004551
@ -31,6 +31,7 @@
|
|||||||
CREATE TABLE notifications (
|
CREATE TABLE notifications (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
user_id BIGINT NOT NULL, -- 接收通知的用户ID
|
user_id BIGINT NOT NULL, -- 接收通知的用户ID
|
||||||
|
star_id BIGINT NOT NULL, -- 数据隔离(star ID)
|
||||||
type VARCHAR(20) NOT NULL, -- 通知类型: like / system / activity
|
type VARCHAR(20) NOT NULL, -- 通知类型: like / system / activity
|
||||||
title VARCHAR(200) NOT NULL, -- 通知标题
|
title VARCHAR(200) NOT NULL, -- 通知标题
|
||||||
content VARCHAR(500), -- 通知内容
|
content VARCHAR(500), -- 通知内容
|
||||||
@ -41,8 +42,8 @@ CREATE TABLE notifications (
|
|||||||
read_at BIGINT, -- 阅读时间(毫秒时间戳)
|
read_at BIGINT, -- 阅读时间(毫秒时间戳)
|
||||||
|
|
||||||
-- 索引
|
-- 索引
|
||||||
INDEX idx_notifications_user_type_created (user_id, type, created_at DESC),
|
INDEX idx_notifications_user_type_created (user_id, star_id, type, created_at DESC),
|
||||||
INDEX idx_notifications_user_unread (user_id, is_read, created_at DESC)
|
INDEX idx_notifications_user_unread (user_id, star_id, is_read, created_at DESC)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -52,15 +53,20 @@ CREATE TABLE notifications (
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE notification_stats (
|
CREATE TABLE notification_stats (
|
||||||
user_id BIGINT PRIMARY KEY, -- 用户ID
|
user_id BIGINT NOT NULL, -- 用户ID
|
||||||
|
star_id BIGINT NOT NULL, -- 数据隔离(star ID)
|
||||||
like_unread_count INT DEFAULT 0, -- 点赞通知未读数
|
like_unread_count INT DEFAULT 0, -- 点赞通知未读数
|
||||||
system_unread_count INT DEFAULT 0, -- 系统通知未读数
|
system_unread_count INT DEFAULT 0, -- 系统通知未读数
|
||||||
activity_unread_count INT DEFAULT 0, -- 活动通知未读数
|
activity_unread_count INT DEFAULT 0, -- 活动通知未读数
|
||||||
total_unread_count INT DEFAULT 0, -- 总未读数
|
total_unread_count INT DEFAULT 0, -- 总未读数
|
||||||
updated_at BIGINT NOT NULL -- 更新时间
|
updated_at BIGINT NOT NULL, -- 更新时间
|
||||||
|
|
||||||
|
PRIMARY KEY (user_id, star_id)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **注意**:`notification_stats` 使用 `(user_id, star_id)` 作为联合主键,支持多 star 场景下各 star 独立统计未读数。
|
||||||
|
|
||||||
### 2.3 JSON data 字段示例
|
### 2.3 JSON data 字段示例
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@ -72,7 +78,8 @@ CREATE TABLE notification_stats (
|
|||||||
"actor_name": "张三",
|
"actor_name": "张三",
|
||||||
"actor_avatar": "https://example.com/avatar/456.png",
|
"actor_avatar": "https://example.com/avatar/456.png",
|
||||||
"asset_title": "我的藏品",
|
"asset_title": "我的藏品",
|
||||||
"asset_cover": "https://example.com/asset/123/cover.png"
|
"asset_cover": "https://example.com/asset/123/cover.png",
|
||||||
|
"star_id": 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 系统通知 (type: "system")
|
// 系统通知 (type: "system")
|
||||||
@ -89,7 +96,8 @@ CREATE TABLE notification_stats (
|
|||||||
"activity_title": "端午节活动",
|
"activity_title": "端午节活动",
|
||||||
"activity_cover": "https://example.com/activity/789/cover.png",
|
"activity_cover": "https://example.com/activity/789/cover.png",
|
||||||
"reward_type": "badge",
|
"reward_type": "badge",
|
||||||
"reward_name": "端午限定徽章"
|
"reward_name": "端午限定徽章",
|
||||||
|
"star_id": 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -118,14 +126,17 @@ CREATE TABLE notification_stats (
|
|||||||
│ Social Service │────▶│ Notification Service │
|
│ Social Service │────▶│ Notification Service │
|
||||||
│ (点赞业务) │ │ (存储 + 查询) │
|
│ (点赞业务) │ │ (存储 + 查询) │
|
||||||
└─────────────────┘ └─────────────────────┘
|
└─────────────────┘ └─────────────────────┘
|
||||||
│
|
│ │
|
||||||
▼
|
▼ ▼
|
||||||
┌─────────────────┐
|
┌─────────────────┐ ┌─────────────────────┐
|
||||||
│ Asset Service │
|
│ Asset Service │ │ PostgreSQL │
|
||||||
│ (更新点赞数) │
|
│ (更新点赞数) │ │ notifications + │
|
||||||
└─────────────────┘
|
└─────────────────┘ │ notification_stats │
|
||||||
|
└─────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **事务边界**:Notification Service 在数据库事务中同时写入 `notifications` 表和更新 `notification_stats` 表。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. API 接口设计
|
## 4. API 接口设计
|
||||||
@ -134,12 +145,14 @@ CREATE TABLE notification_stats (
|
|||||||
|
|
||||||
| 方法 | 描述 | 参数 |
|
| 方法 | 描述 | 参数 |
|
||||||
|-----|------|------|
|
|-----|------|------|
|
||||||
| CreateNotification | 创建通知 | userID, type, title, content, data |
|
| CreateNotification | 创建通知 | userID, starID, type, title, content, data |
|
||||||
| GetNotifications | 查询通知列表 | userID, type, page, pageSize |
|
| GetNotifications | 查询通知列表 | userID, starID, type, page, pageSize |
|
||||||
| GetUnreadCount | 获取未读数 | userID |
|
| GetUnreadCount | 获取未读数 | userID, starID |
|
||||||
| MarkAsRead | 标记已读 | notificationID, userID |
|
| MarkAsRead | 标记已读 | notificationID, userID, starID |
|
||||||
| MarkAllAsRead | 全部标已读 | userID, type |
|
| MarkAllAsRead | 全部标已读 | userID, starID, type |
|
||||||
| DeleteNotification | 删除通知 | notificationID, userID |
|
| DeleteNotification | 删除通知 | notificationID, userID, starID |
|
||||||
|
|
||||||
|
> **说明**:所有接口都需要传入 `starID`,确保数据隔离。
|
||||||
|
|
||||||
### 4.2 HTTP 接口(供前端调用)
|
### 4.2 HTTP 接口(供前端调用)
|
||||||
|
|
||||||
@ -173,12 +186,14 @@ GET /api/v1/notifications?type=like&tab=today&page=1&pageSize=20
|
|||||||
1. 用户点赞 → Social Service 处理
|
1. 用户点赞 → Social Service 处理
|
||||||
2. Social Service 调用 Asset Service RPC 更新点赞数
|
2. Social Service 调用 Asset Service RPC 更新点赞数
|
||||||
3. Social Service 调用 Notification Service CreateNotification
|
3. Social Service 调用 Notification Service CreateNotification
|
||||||
4. Notification Service:
|
4. Notification Service(在事务中完成):
|
||||||
- 写入 notifications 表
|
- 写入 notifications 表
|
||||||
- 更新 notification_stats 表 (+1 未读数)
|
- 更新 notification_stats 表 (+1 未读数,INSERT ... ON CONFLICT DO UPDATE 处理首次创建)
|
||||||
5. 返回成功响应
|
5. 返回成功响应
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **事务保证**:创建通知和更新统计必须在同一个事务中,确保数据一致性。
|
||||||
|
|
||||||
### 5.2 查询逻辑
|
### 5.2 查询逻辑
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -193,9 +208,11 @@ GET /api/v1/notifications?type=like&tab=today&page=1&pageSize=20
|
|||||||
|
|
||||||
### 5.3 未读数统计
|
### 5.3 未读数统计
|
||||||
|
|
||||||
- 每次创建通知时,更新 `notification_stats` 表对应类型的未读数
|
- 每次创建通知时,在同一事务中更新 `notification_stats` 表对应类型的未读数
|
||||||
- 每次标记已读时,减少未读数
|
- 使用 `INSERT ... ON CONFLICT DO UPDATE` 确保首次创建时自动插入记录
|
||||||
|
- 每次标记已读时,减少对应类型的未读数
|
||||||
- 批量标已读时,重置对应类型的未读数为 0
|
- 批量标已读时,重置对应类型的未读数为 0
|
||||||
|
- 删除通知时,同步减少未读数
|
||||||
|
|
||||||
### 5.4 通知直达
|
### 5.4 通知直达
|
||||||
|
|
||||||
@ -233,25 +250,76 @@ GET /api/v1/notifications?type=like&tab=today&page=1&pageSize=20
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. 项目结构
|
## 8. 数据一致性保证
|
||||||
|
|
||||||
|
### 8.1 事务边界
|
||||||
|
|
||||||
|
创建通知和更新统计必须在同一个数据库事务中完成:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s *NotificationService) CreateNotification(ctx context.Context, ...) error {
|
||||||
|
return s.db.Transaction(func(tx *sql.Tx) error {
|
||||||
|
// 1. 写入 notifications 表
|
||||||
|
_, err := tx.Exec("INSERT INTO notifications (...) VALUES (...)", ...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 更新 notification_stats 表(使用 INSERT ... ON CONFLICT DO UPDATE)
|
||||||
|
_, err = tx.Exec(`
|
||||||
|
INSERT INTO notification_stats (user_id, star_id, like_unread_count, updated_at)
|
||||||
|
VALUES (?, ?, 1, ?)
|
||||||
|
ON CONFLICT (user_id, star_id) DO UPDATE SET
|
||||||
|
like_unread_count = notification_stats.like_unread_count + 1,
|
||||||
|
total_unread_count = notification_stats.total_unread_count + 1,
|
||||||
|
updated_at = ?
|
||||||
|
`, userID, starID, now, now)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 点赞通知防重复
|
||||||
|
|
||||||
|
由于藏品可下架再上架,同一用户对同一藏品当天可能产生多条点赞通知。为避免重复:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 为点赞通知添加唯一约束(可选,取决于业务需求)
|
||||||
|
CREATE UNIQUE INDEX idx_like_notification_daily
|
||||||
|
ON notifications (user_id, star_id, type, data->>'target_id', data->>'actor_id', date_trunc('day', to_timestamp(created_at/1000)));
|
||||||
|
```
|
||||||
|
|
||||||
|
> **说明**:如果业务上允许同一天多条点赞通知(每条都展示),则不需要此唯一约束。
|
||||||
|
|
||||||
|
### 8.2 补偿机制
|
||||||
|
|
||||||
|
如果事务提交后 RPC 调用方未收到响应,调用方会重试。此时:
|
||||||
|
- 使用唯一约束 `UNIQUE (user_id, star_id, type, target_type, target_id, actor_id, date)` 防止重复创建点赞通知
|
||||||
|
- 或者通过幂等性设计(如每次点赞生成唯一 notification_id)处理重复
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
services/notificationService/
|
services/notificationService/
|
||||||
├── main.go
|
├── main.go
|
||||||
├── repository/
|
├── repository/
|
||||||
│ ├── notification_repository.go
|
│ ├── notification_repository.go -- 通知 CRUD
|
||||||
│ └── notification_stats_repository.go
|
│ └── notification_stats_repository.go -- 统计更新
|
||||||
├── service/
|
├── service/
|
||||||
│ └── notification_service.go
|
│ └── notification_service.go -- 业务逻辑 + 事务处理
|
||||||
├── provider/
|
├── provider/
|
||||||
│ └── notification_provider.go
|
│ └── notification_provider.go -- RPC 接口
|
||||||
|
├── model/
|
||||||
|
│ └── notification.go -- 数据模型
|
||||||
└── client/
|
└── client/
|
||||||
└── (供其他服务调用的客户端,如需要)
|
└── (供其他服务调用的客户端,如需要)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. 后续扩展
|
## 10. 后续扩展
|
||||||
|
|
||||||
- 支持评论通知 (type: "comment")
|
- 支持评论通知 (type: "comment")
|
||||||
- 支持 @提及通知 (type: "mention")
|
- 支持 @提及通知 (type: "mention")
|
||||||
@ -259,12 +327,14 @@ services/notificationService/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. 确认点
|
## 11. 确认点
|
||||||
|
|
||||||
- [x] 统一通知表存储所有类型
|
- [x] 统一通知表存储所有类型
|
||||||
- [x] 独立 Notification Service
|
- [x] 独立 Notification Service
|
||||||
|
- [x] 支持 star_id 数据隔离
|
||||||
- [x] 支持今日/历史 Tab 查询
|
- [x] 支持今日/历史 Tab 查询
|
||||||
- [x] 不合并点赞记录
|
- [x] 不合并点赞记录
|
||||||
- [x] 支持未读数统计
|
- [x] 支持未读数统计(事务保证一致性)
|
||||||
- [x] 支持通知直达
|
- [x] 支持通知直达
|
||||||
- [x] 支持批量操作
|
- [x] 支持批量操作
|
||||||
|
- [x] INSERT ... ON CONFLICT DO UPDATE 处理首次创建统计
|
||||||
Loading…
Reference in New Issue
Block a user