From 7289460b2933d55d632ec4022e52be7c077b8fa9 Mon Sep 17 00:00:00 2001 From: wolves Date: Tue, 10 Mar 2026 22:02:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 301 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 277 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 4a7e69d..f93aca6 100644 --- a/README.md +++ b/README.md @@ -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 ' \ + -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 ' +``` + +### 通知偏好 + +```bash +curl -X PUT http://localhost:8080/api/v1/notifications/prefs \ + -H 'Authorization: Bearer ' \ + -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 ' \ + -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 ' +``` + +## 数据表说明 + +当前通知系统的关键表包括: + +- `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) +