更新说明

This commit is contained in:
2026-03-10 22:02:46 +08:00
parent 58e228b0dc
commit 7289460b29

301
README.md
View File

@@ -1,17 +1,160 @@
# Simple Todo API (Monolith)
# SuperTodo 任务与通知系统
This project is a single backend service built with Gin.
The `iam/` directory is kept as a separate scaffold module and is not part of the monolith build.
`SuperTodo` 是一个基于 `Gin + PostgreSQL + Redis + Kafka + Vue` 的任务管理与定时通知系统。
当前系统不仅支持基础的任务创建、编辑、完成,还支持通知偏好、通知组、到期提醒、过期提醒、每日摘要、重试与死信队列。
## Run
## 项目概览
系统当前由以下几部分组成:
- 后端 API`Go + Gin`
- 前端页面:`Vue 3 + Vite`
- 关系型存储:`PostgreSQL`
- 会话与缓存:`Redis`
- 消息中间件:`Kafka`
- 邮件投递:`SMTP`
核心能力包括:
- 用户注册、登录、刷新令牌、会话管理
- 任务 CRUD
- 用户通知偏好管理
- 通知组管理
- 任务创建、状态变化、即将到期、已过期等事件通知
- 定时调度、异步投递、失败重试、DLQ 死信记录
## 消息中间件说明
本项目中的消息中间件主要使用 `Kafka`,它不是装饰性组件,而是通知系统可靠性的核心。
### 为什么要引入消息中间件
如果任务到期、任务状态变化这类业务事件直接在 HTTP 请求里同步发邮件,会有几个明显问题:
- 接口响应时间变长,用户操作会被外部邮件服务拖慢
- 邮件服务短暂失败时,业务请求容易被连带失败
- 突发高峰时,大量通知会集中压垮投递链路
- 难以实现统一重试、削峰、幂等和死信补偿
引入 Kafka 后,系统会把“业务处理”和“通知投递”拆成两个阶段:
1. 业务接口先落库并生成通知事件 / 通知任务
2. 通知任务进入 Kafka topic由后台 worker 异步消费并发送邮件
这样做的结果是:
- API 更快返回
- 投递链路与业务链路解耦
- 可以按批次消费,避免瞬时洪峰
- 可以做失败重试与死信记录
- 可以通过幂等键避免重复发送
### 本项目里的 Kafka 角色
当前用到的 topic 主要有两个:
- `todo.tasks`
- 用于任务事件输出
- `notification.jobs`
- 用于通知任务分发
通知相关链路如下:
1. 用户创建任务或更新任务
2. 系统写入 `notification_events`
3. 规则引擎根据事件类型、通知组、用户偏好生成 `notification_jobs`
4. dispatcher 将可投递任务写入 `notification.jobs`
5. worker 消费消息并发送邮件
6. 发送成功写回 `sent`
7. 临时失败回到 `pending` 并按指数退避重试
8. 超过重试次数或不可重试错误进入 `notification_dlq`
### 当前可靠性机制
通知系统当前已经实现:
- 幂等控制:同一事件不会重复生成相同通知任务
- 扫描去重:同一 `task_id + event_type + due_at` 不会被调度器反复重发
- 重试机制:失败任务按退避策略重新投递
- 死信队列:无法恢复的任务进入 DLQ
- 状态可观测:支持查看 `pending / sent / dead`
## 系统架构
### 后端模块
- `cmd/server`
- HTTP 服务入口
- `internal/iam`
- 登录认证、令牌、会话管理
- `internal/notification`
- 通知偏好、通知组、调度器、Kafka dispatcher、worker、SMTP 发送、DLQ 与指标
### 前端模块
- `web/src/views/TodoView.vue`
- 任务列表与编辑
- `web/src/views/UserSettingsView.vue`
- 通知偏好设置
- `web/src/views/NotificationGroupsView.vue`
- 通知组管理
- `web/src/views/NotificationDlqView.vue`
- 死信查看
## 运行方式
### 方式一Docker Compose 启动整套系统
推荐本地联调直接使用容器。
```bash
docker compose up -d
```
启动后默认端口:
- API`http://localhost:8080`
- Kafka UI`http://localhost:8081`
- PostgreSQL`localhost:5432`
- Redis`localhost:6379`
- Kafka`localhost:29092`
健康检查:
```bash
curl http://localhost:8080/api/health
```
### 方式二:本机运行后端
如果你已经单独启动了 PostgreSQL、Redis、Kafka也可以直接运行后端
```bash
go run ./cmd/server
```
The server listens on `:8080`.
常用环境变量:
## Frontend
```bash
DATABASE_URL=postgres://todo:todo@localhost:5432/todo?sslmode=disable
REDIS_ADDR=localhost:6379
KAFKA_BROKERS=localhost:29092
KAFKA_TOPIC=todo.tasks
NOTIFY_ENABLED=true
NOTIFY_TOPIC=notification.jobs
NOTIFY_DISPATCH_BATCH=200
NOTIFY_SCHED_TICK_SECONDS=60
NOTIFY_MAX_RETRY=5
NOTIFY_BACKOFF_BASE_SECONDS=30
SMTP_HOST=smtp.qq.com
SMTP_PORT=465
SMTP_USER=your_mail@example.com
SMTP_PASS=your_smtp_password
SMTP_FROM=your_mail@example.com
SMTP_USE_TLS=true
```
## 前端运行
```bash
cd web
@@ -19,32 +162,142 @@ npm install
npm run dev
```
The Vue app calls `http://localhost:8080/api/v1` by default.
默认访问地址:
## Quick Demo
- 前端开发服务:通常为 `http://localhost:5173`
- 前端请求后端 API`http://localhost:8080/api/v1`
## 主要数据流
通知系统的数据流可以概括为:
1. 业务事件产生
- 如任务创建、任务状态变化、任务即将到期、任务已过期
2. 事件入库
- 写入 `notification_events`
3. 规则判断
- 判断是否需要通知、通知谁、使用哪个模板
4. 偏好过滤
- 过滤订阅状态、语言、时区、免打扰时间
5. 生成通知任务
- 写入 `notification_jobs`
6. 分发到 Kafka
- 写入 `notification.jobs`
7. worker 消费并发送邮件
8. 回写状态
- `sent / pending / dead`
9. 失败补偿
- 重试或进入 DLQ
## 常用接口
### 认证
```bash
curl http://localhost:8080/api/health
curl -X POST http://localhost:8080/api/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"email":"demo@example.com","password":"secret123"}'
-d '{"email":"demo@example.com","password":"secret123","auto_login":true}'
```
```bash
curl -X POST http://localhost:8080/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"demo@example.com","password":"secret123"}'
curl -X POST http://localhost:8080/api/v1/tasks \
-H 'Authorization: Bearer demo' \
-H 'Content-Type: application/json' \
-d '{"title":"Learn Gin","priority":1,"tags":["demo"]}'
curl http://localhost:8080/api/v1/tasks \
-H 'Authorization: Bearer demo'
```
## Notes
- Tasks and users are stored in PostgreSQL.
- Auth uses HMAC-signed bearer tokens with TTL (default 24h).
- Redis token cache is optional (`REDIS_ADDR` not set means disabled).
- Kafka task event emit is optional (`KAFKA_BROKERS` not set means disabled).
### 任务
```bash
curl -X POST http://localhost:8080/api/v1/tasks \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"title":"学习通知系统",
"description":"验证 Kafka 通知链路",
"status":"todo",
"due_at":"2026-03-10T15:30:00Z",
"priority":1,
"tags":["demo","notify"],
"notify_group_ids":[1]
}'
```
```bash
curl http://localhost:8080/api/v1/tasks \
-H 'Authorization: Bearer <token>'
```
### 通知偏好
```bash
curl -X PUT http://localhost:8080/api/v1/notifications/prefs \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"subscribed":true,
"dnd_start":"",
"dnd_end":"",
"locale":"zh",
"timezone":"Asia/Shanghai",
"daily_summary_enabled":true,
"daily_summary_time":"09:30"
}'
```
### 通知组
```bash
curl -X POST http://localhost:8080/api/v1/notifications/groups \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name":"项目通知组",
"emails":["a@example.com","b@example.com"]
}'
```
### 通知指标
```bash
curl http://localhost:8080/api/v1/notifications/metrics \
-H 'Authorization: Bearer <token>'
```
## 数据表说明
当前通知系统的关键表包括:
- `tasks`
- 任务主体,包含 `notify_group_ids`
- `user_notification_prefs`
- 用户通知偏好
- `notification_groups`
- 通知组
- `task_notification_groups`
- 任务与通知组关系
- `notification_events`
- 通知事件日志
- `notification_jobs`
- 通知任务队列
- `notification_attempts`
- 每次投递尝试记录
- `notification_dlq`
- 死信记录
## 开发说明
### 关于 `web/dist`
`web/dist/` 已从 Git 移除,并加入 `.gitignore`
前端构建产物不再直接提交到仓库。
### 关于通知重复发送
当前代码已经修复“过期任务在每次扫描中重复重发”的问题。
调度器会基于 `task_id + event_type + due_at` 做去重,同一条到期版本只会生成一次对应事件。
## 相关文档
- [通知系统说明](./doc/notify.md)
- [认证模块说明](./doc/iam.md)