Files
todo-vibe-coding/README.md
2026-03-10 22:02:46 +08:00

304 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SuperTodo 任务与通知系统
`SuperTodo` 是一个基于 `Gin + PostgreSQL + Redis + Kafka + Vue` 的任务管理与定时通知系统。
当前系统不仅支持基础的任务创建、编辑、完成,还支持通知偏好、通知组、到期提醒、过期提醒、每日摘要、重试与死信队列。
## 项目概览
系统当前由以下几部分组成:
- 后端 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
```
常用环境变量:
```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
npm install
npm run dev
```
默认访问地址:
- 前端开发服务:通常为 `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 -X POST http://localhost:8080/api/v1/auth/register \
-H 'Content-Type: application/json' \
-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"}'
```
### 任务
```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)