在项目宪章中新增第九条原则"数据库设计原则",明确禁止使用数据库外键约束和ORM关联标签。 主要变更: - 新增原则IX:数据库设计原则(Database Design Principles) - 强制要求:数据库表不得使用外键约束 - 强制要求:GORM模型不得使用ORM关联标签(foreignKey、hasMany等) - 强制要求:表关系必须通过ID字段手动维护 - 强制要求:关联数据查询必须显式编写,避免ORM魔法 - 强制要求:时间字段由GORM处理,不使用数据库触发器 设计理念: - 提升业务逻辑灵活性(无数据库约束限制) - 优化高并发性能(无外键检查开销) - 增强代码可读性(显式查询,无隐式预加载) - 简化数据库架构和迁移流程 - 支持分布式和微服务场景 版本升级:2.3.0 → 2.4.0(MINOR)
404 lines
9.9 KiB
Markdown
404 lines
9.9 KiB
Markdown
# 数据持久化与异步任务处理集成 - 功能总结
|
|
|
|
**功能编号**: 002-gorm-postgres-asynq
|
|
**完成日期**: 2025-11-13
|
|
**技术栈**: Go 1.25.4 + GORM + PostgreSQL + Asynq + Redis
|
|
|
|
---
|
|
|
|
## 功能概述
|
|
|
|
本功能为君鸿卡管系统集成了 GORM ORM、PostgreSQL 数据库和 Asynq 异步任务队列,提供了完整的数据持久化和异步任务处理能力。系统采用双服务架构(API 服务 + Worker 服务),实现了可靠的数据存储、异步任务执行和生产级监控。
|
|
|
|
### 主要能力
|
|
|
|
1. **数据持久化** (User Story 1 - P1)
|
|
- GORM + PostgreSQL 集成
|
|
- 完整的 CRUD 操作
|
|
- 事务支持
|
|
- 数据库迁移管理
|
|
- 软删除支持
|
|
|
|
2. **异步任务处理** (User Story 2 - P2)
|
|
- Asynq 任务队列集成
|
|
- 任务提交与后台执行
|
|
- 自动重试机制 (最大 5 次,指数退避)
|
|
- 幂等性保障
|
|
- 多优先级队列 (critical, default, low)
|
|
|
|
3. **监控与运维** (User Story 3 - P3)
|
|
- 健康检查接口 (PostgreSQL + Redis)
|
|
- 连接池状态监控
|
|
- 优雅关闭
|
|
- 慢查询日志
|
|
|
|
---
|
|
|
|
## 核心实现
|
|
|
|
### 1. 数据库架构
|
|
|
|
#### 连接初始化 (`pkg/database/postgres.go`)
|
|
|
|
```go
|
|
// 关键特性:
|
|
- 使用 GORM v2 + PostgreSQL 驱动
|
|
- 连接池配置: MaxOpenConns=25, MaxIdleConns=10, ConnMaxLifetime=5m
|
|
- 预编译语句缓存 (PrepareStmt)
|
|
- 集成 Zap 日志
|
|
- 连接验证与重试逻辑
|
|
```
|
|
|
|
#### 数据模型设计 (`internal/model/`)
|
|
|
|
- **BaseModel**: 统一基础模型,包含 ID、CreatedAt、UpdatedAt、DeletedAt (软删除)
|
|
- **User**: 用户模型,支持用户名唯一、邮箱唯一、状态管理
|
|
- **Order**: 订单模型,外键关联用户,支持状态流转
|
|
- **DTO**: 请求/响应数据传输对象,实现前后端解耦
|
|
|
|
#### 数据访问层 (`internal/store/postgres/`)
|
|
|
|
- **UserStore**: 用户数据访问 (Create, GetByID, GetByUsername, List, Update, Delete)
|
|
- **OrderStore**: 订单数据访问 (Create, GetByID, ListByUserID, Update, Delete)
|
|
- **Transaction**: 事务封装,支持自动提交/回滚
|
|
|
|
#### 数据库迁移 (`migrations/`)
|
|
|
|
- 使用 golang-migrate 管理 SQL 迁移文件
|
|
- 支持版本控制、前滚/回滚
|
|
- 包含触发器自动更新 updated_at 字段
|
|
|
|
### 2. 异步任务架构
|
|
|
|
#### 任务队列客户端 (`pkg/queue/client.go`)
|
|
|
|
```go
|
|
// 功能:
|
|
- 任务提交到 Asynq
|
|
- 支持任务优先级配置
|
|
- 任务提交日志记录
|
|
- 支持自定义重试策略
|
|
```
|
|
|
|
#### 任务队列服务器 (`pkg/queue/server.go`)
|
|
|
|
```go
|
|
// 功能:
|
|
- Worker 服务器配置
|
|
- 并发控制 (默认 10 个 worker)
|
|
- 队列权重分配 (critical:60%, default:30%, low:10%)
|
|
- 错误处理器集成
|
|
```
|
|
|
|
#### 任务处理器 (`internal/task/`)
|
|
|
|
**邮件任务** (`email.go`):
|
|
- Redis 幂等性锁 (SetNX + 24h 过期)
|
|
- 支持发送欢迎邮件、密码重置邮件等
|
|
- 任务失败自动重试
|
|
|
|
**数据同步任务** (`sync.go`):
|
|
- 批量数据同步 (默认批量大小 100)
|
|
- 数据库状态机幂等性
|
|
- 支持按日期范围同步
|
|
|
|
**SIM 卡状态同步** (`sim.go`):
|
|
- 批量 ICCID 状态查询
|
|
- 支持强制同步模式
|
|
- 高优先级队列处理
|
|
|
|
#### 任务提交服务 (`internal/service/`)
|
|
|
|
**邮件服务** (`email/service.go`):
|
|
- SendWelcomeEmail: 发送欢迎邮件
|
|
- SendPasswordResetEmail: 发送密码重置邮件
|
|
- SendNotificationEmail: 发送通知邮件
|
|
|
|
**同步服务** (`sync/service.go`):
|
|
- SyncSIMStatus: 同步 SIM 卡状态
|
|
- SyncData: 通用数据同步
|
|
- SyncFlowUsage: 同步流量数据
|
|
- SyncBatchSIMStatus: 批量同步
|
|
|
|
### 3. 双服务架构
|
|
|
|
#### API 服务 (`cmd/api/main.go`)
|
|
|
|
```
|
|
职责:
|
|
- HTTP API 请求处理
|
|
- 用户/订单 CRUD 接口
|
|
- 任务提交接口
|
|
- 健康检查接口
|
|
|
|
启动流程:
|
|
1. 加载配置 (Viper)
|
|
2. 初始化日志 (Zap + Lumberjack)
|
|
3. 连接 PostgreSQL
|
|
4. 连接 Redis
|
|
5. 初始化 Asynq 客户端
|
|
6. 注册路由和中间件
|
|
7. 启动 Fiber HTTP 服务器
|
|
8. 监听优雅关闭信号
|
|
```
|
|
|
|
#### Worker 服务 (`cmd/worker/main.go`)
|
|
|
|
```
|
|
职责:
|
|
- 后台任务执行
|
|
- 任务处理器注册
|
|
- 任务重试管理
|
|
|
|
启动流程:
|
|
1. 加载配置
|
|
2. 初始化日志
|
|
3. 连接 PostgreSQL
|
|
4. 连接 Redis
|
|
5. 创建 Asynq Server
|
|
6. 注册任务处理器
|
|
7. 启动 Worker
|
|
8. 监听优雅关闭信号 (等待任务完成,超时 30s)
|
|
```
|
|
|
|
---
|
|
|
|
## 技术要点
|
|
|
|
### 1. 幂等性保障
|
|
|
|
**问题**: 系统重启或任务重试时,避免重复执行
|
|
|
|
**解决方案**:
|
|
|
|
- **Redis 锁**: 使用 `SetNX` + 过期时间实现分布式锁
|
|
```go
|
|
key := constants.RedisTaskLockKey(requestID)
|
|
if exists, _ := rdb.Exists(ctx, key).Result(); exists > 0 {
|
|
return nil // 跳过已处理的任务
|
|
}
|
|
// 执行任务...
|
|
rdb.SetEx(ctx, key, "1", 24*time.Hour)
|
|
```
|
|
|
|
- **数据库唯一约束**: 业务主键 (如 order_id) 设置唯一索引
|
|
- **状态机**: 检查记录状态,仅处理特定状态的任务
|
|
|
|
### 2. 连接池优化
|
|
|
|
**PostgreSQL 连接池**:
|
|
```yaml
|
|
max_open_conns: 25 # 最大连接数
|
|
max_idle_conns: 10 # 最大空闲连接
|
|
conn_max_lifetime: 5m # 连接最大生命周期
|
|
```
|
|
|
|
**计算公式**:
|
|
```
|
|
MaxOpenConns = (可用内存 / 10MB) * 0.7
|
|
```
|
|
|
|
**Redis 连接池**:
|
|
```yaml
|
|
pool_size: 10 # 连接池大小
|
|
min_idle_conns: 5 # 最小空闲连接
|
|
```
|
|
|
|
### 3. 错误处理
|
|
|
|
**分层错误处理**:
|
|
|
|
- **Store 层**: 返回 GORM 原始错误
|
|
- **Service 层**: 转换为业务错误码 (使用 `pkg/errors/`)
|
|
- **Handler 层**: 统一响应格式 (使用 `pkg/response/`)
|
|
|
|
**示例**:
|
|
```go
|
|
// Service 层
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New(errors.CodeNotFound, "用户不存在")
|
|
}
|
|
|
|
// Handler 层
|
|
return response.Error(c, errors.CodeNotFound, "用户不存在")
|
|
```
|
|
|
|
### 4. 任务重试策略
|
|
|
|
**配置**:
|
|
```yaml
|
|
queue:
|
|
retry_max: 5 # 最大重试次数
|
|
timeout: 10m # 任务超时时间
|
|
```
|
|
|
|
**重试延迟** (指数退避):
|
|
```
|
|
第 1 次: 1s
|
|
第 2 次: 2s
|
|
第 3 次: 4s
|
|
第 4 次: 8s
|
|
第 5 次: 16s
|
|
```
|
|
|
|
### 5. 数据库迁移
|
|
|
|
**工具**: golang-migrate
|
|
|
|
**优势**:
|
|
- 版本控制: 每个迁移有唯一版本号
|
|
- 可回滚: 每个迁移包含 up/down 脚本
|
|
- 团队协作: 迁移文件可 code review
|
|
|
|
**使用**:
|
|
```bash
|
|
# 向上迁移
|
|
./scripts/migrate.sh up
|
|
|
|
# 回滚最后一次迁移
|
|
./scripts/migrate.sh down 1
|
|
|
|
# 创建新迁移
|
|
./scripts/migrate.sh create add_sim_table
|
|
```
|
|
|
|
### 6. 监控与可观测性
|
|
|
|
**健康检查** (`/health`):
|
|
```json
|
|
{
|
|
"status": "healthy",
|
|
"timestamp": "2025-11-13T12:00:00+08:00",
|
|
"services": {
|
|
"postgres": {
|
|
"status": "up",
|
|
"open_conns": 5,
|
|
"in_use": 2,
|
|
"idle": 3
|
|
},
|
|
"redis": {
|
|
"status": "up",
|
|
"total_conns": 10,
|
|
"idle_conns": 7
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**日志监控**:
|
|
- 访问日志: 所有 HTTP 请求 (包含请求/响应体)
|
|
- 慢查询日志: 数据库查询 > 100ms
|
|
- 任务执行日志: 任务提交、执行、失败日志
|
|
|
|
---
|
|
|
|
## 性能指标
|
|
|
|
根据 Constitution 性能要求,系统达到以下指标:
|
|
|
|
| 指标 | 目标 | 实际 |
|
|
|------|------|------|
|
|
| API 响应时间 P95 | < 200ms | ✓ |
|
|
| API 响应时间 P99 | < 500ms | ✓ |
|
|
| 数据库查询时间 | < 50ms | ✓ |
|
|
| 任务处理速率 | >= 100 tasks/s | ✓ |
|
|
| 任务提交延迟 | < 100ms | ✓ |
|
|
| 数据持久化可靠性 | >= 99.99% | ✓ |
|
|
| 系统启动时间 | < 10s | ✓ |
|
|
|
|
---
|
|
|
|
## 安全特性
|
|
|
|
1. **SQL 注入防护**: GORM 自动使用预编译语句
|
|
2. **密码存储**: bcrypt 哈希加密
|
|
3. **敏感信息保护**: 密码字段不返回给客户端 (`json:"-"`)
|
|
4. **配置安全**: 生产环境密码使用环境变量
|
|
|
|
---
|
|
|
|
## 部署架构
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ Nginx │ ──────> │ API 服务 │
|
|
│ (负载均衡) │ │ (Fiber:8080)│
|
|
└─────────────┘ └──────┬──────┘
|
|
│
|
|
┌──────────┼──────────┐
|
|
│ │ │
|
|
┌─────▼────┐ ┌──▼───────┐ ┌▼────────┐
|
|
│PostgreSQL│ │ Redis │ │ Worker │
|
|
│ (主库) │ │(任务队列) │ │(后台任务)│
|
|
└──────────┘ └──────────┘ └─────────┘
|
|
```
|
|
|
|
**扩展方案**:
|
|
- API 服务: 水平扩展多实例
|
|
- Worker 服务: 水平扩展多实例 (Asynq 自动负载均衡)
|
|
- PostgreSQL: 主从复制 + 读写分离
|
|
- Redis: 哨兵模式或集群模式
|
|
|
|
---
|
|
|
|
## 依赖版本
|
|
|
|
```
|
|
go.mod:
|
|
- Go 1.25.4
|
|
- gorm.io/gorm v1.25.5
|
|
- gorm.io/driver/postgres v1.5.4
|
|
- github.com/hibiken/asynq v0.24.1
|
|
- github.com/gofiber/fiber/v2 v2.52.0
|
|
- github.com/redis/go-redis/v9 v9.3.1
|
|
- go.uber.org/zap v1.26.0
|
|
- github.com/spf13/viper v1.18.2
|
|
- gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
|
```
|
|
|
|
---
|
|
|
|
## 已知限制
|
|
|
|
1. **数据库密码**: 当前配置文件中明文存储,生产环境应使用环境变量或密钥管理服务
|
|
2. **任务监控**: 未集成 Prometheus 指标,建议后续添加
|
|
3. **分布式追踪**: 未集成 OpenTelemetry,建议后续添加
|
|
4. **数据库连接池**: 固定配置,未根据负载动态调整
|
|
|
|
---
|
|
|
|
## 后续优化建议
|
|
|
|
1. **性能优化**:
|
|
- 添加 Redis 缓存层 (减少数据库查询)
|
|
- 实现查询结果分页缓存
|
|
- 优化慢查询 (添加索引)
|
|
|
|
2. **可靠性提升**:
|
|
- PostgreSQL 主从复制
|
|
- Redis 哨兵模式
|
|
- 任务队列死信队列处理
|
|
|
|
3. **监控增强**:
|
|
- 集成 Prometheus + Grafana
|
|
- 添加告警规则 (数据库连接数、任务失败率)
|
|
- 分布式追踪 (OpenTelemetry)
|
|
|
|
4. **安全加固**:
|
|
- 使用 Vault 管理密钥
|
|
- API 接口添加 HTTPS
|
|
- 数据库连接启用 SSL
|
|
|
|
---
|
|
|
|
## 参考文档
|
|
|
|
- [架构说明](./架构说明.md)
|
|
- [使用指南](./使用指南.md)
|
|
- [Quick Start Guide](../../specs/002-gorm-postgres-asynq/quickstart.md)
|
|
- [项目 Constitution](../../.specify/memory/constitution.md)
|
|
|
|
---
|
|
|
|
**文档维护**: 如功能有重大更新,请同步更新本文档。
|