# Feature Specification: 数据持久化与异步任务处理集成

**Feature Branch**: `002-gorm-postgres-asynq`  
**Created**: 2025-11-12  
**Status**: Draft  
**Input**: User description: "集成gorm、Postgresql数据库和asynq任务队列系统"

## Clarifications

### Session 2025-11-12

- Q: PostgreSQL连接应如何处理凭证管理和传输安全? → A: 凭证直接写在配置文件(config.yaml)中,明文存储
- Q: 任务失败后应该如何重试? → A: 最大重试5次,指数退避策略(1s、2s、4s、8s、16s)
- Q: 数据库表结构的创建和变更应该如何执行? → A: 完全不使用GORM迁移,使用外部迁移工具(如golang-migrate)管理SQL迁移文件
- Q: Worker进程应该如何配置并发任务处理? → A: 支持多个worker进程,每个进程可配置并发数(默认10);不同任务类型可配置独立的队列优先级
- Q: 系统重启时,正在执行中或排队中的任务应该如何处理? → A: 所有未完成任务(包括执行中)自动重新排队,重启后继续执行;任务处理逻辑需保证幂等性

### Session 2025-11-13

- Q: 数据库慢查询(>100ms)和任务执行状态应该如何进行监控和指标收集? → A: 仅记录日志文件,不收集指标
- Q: 当数据库连接池耗尽时,新的数据库请求应该如何处理? → A: 请求排队等待直到获得连接(带超时,如5秒)
- Q: 当数据库执行慢查询时,系统应该如何避免请求超时? → A: 使用context超时控制(如3秒),超时后取消查询
- Q: 当PostgreSQL主从切换时,系统应该如何感知并重新连接? → A: 依赖GORM的自动重连机制,连接失败时重试
- Q: 当并发事务产生死锁时,系统应该如何检测和恢复? → A: 依赖PostgreSQL自动检测,捕获死锁错误并重试(最多3次)

## User Scenarios & Testing *(mandatory)*

### User Story 1 - 可靠的数据存储与检索 (Priority: P1)

作为系统,需要能够可靠地持久化存储业务数据(如用户信息、业务记录等),并支持高效的数据查询和修改操作,确保数据的一致性和完整性。

**Why this priority**: 这是系统的核心基础能力,没有数据持久化就无法提供任何有意义的业务功能。所有后续功能都依赖于数据存储能力。

**Independent Test**: 可以通过创建、读取、更新、删除(CRUD)测试数据来独立验证。测试应包括基本的数据操作、事务提交、数据一致性验证等场景。

**Acceptance Scenarios**:

1. **Given** 系统接收到新的业务数据, **When** 执行数据保存操作, **Then** 数据应成功持久化到数据库,并可以被后续查询检索到
2. **Given** 需要修改已存在的数据, **When** 执行更新操作, **Then** 数据应被正确更新,且旧数据被新数据替换
3. **Given** 需要删除数据, **When** 执行删除操作, **Then** 数据应从数据库中移除,后续查询不应返回该数据
4. **Given** 多个数据操作需要原子性执行, **When** 在事务中执行这些操作, **Then** 要么全部成功提交,要么全部回滚,保证数据一致性
5. **Given** 执行数据查询, **When** 查询条件匹配多条记录, **Then** 系统应返回所有匹配的记录,支持分页和排序

---

### User Story 2 - 异步任务处理能力 (Priority: P2)

作为系统,需要能够将耗时的操作(如发送邮件、生成报表、数据同步等)放到后台异步执行,避免阻塞用户请求,提升用户体验和系统响应速度。

**Why this priority**: 许多业务操作需要较长时间完成,如果在用户请求中同步执行会导致超时和糟糕的用户体验。异步任务处理是提升系统性能和用户体验的关键。

**Independent Test**: 可以通过提交一个耗时任务(如模拟发送邮件),验证任务被成功加入队列,然后在后台完成执行,用户请求立即返回而不等待任务完成。

**Acceptance Scenarios**:

1. **Given** 系统需要执行一个耗时操作, **When** 将任务提交到任务队列, **Then** 任务应被成功加入队列,用户请求立即返回,不阻塞等待
2. **Given** 任务队列中有待处理的任务, **When** 后台工作进程运行, **Then** 任务应按顺序被取出并执行
3. **Given** 任务执行过程中发生错误, **When** 任务失败, **Then** 系统应记录错误信息,并根据配置进行重试
4. **Given** 任务需要定时执行, **When** 到达指定时间, **Then** 任务应自动触发执行
5. **Given** 需要查看任务执行状态, **When** 查询任务信息, **Then** 应能获取任务的当前状态(等待、执行中、成功、失败)和执行历史

---

### User Story 3 - 数据库连接管理与监控 (Priority: P3)

作为系统管理员,需要能够监控数据库连接状态、查询性能和任务队列健康度,及时发现和解决潜在问题,确保系统稳定运行。

**Why this priority**: 虽然不是核心业务功能,但对系统的稳定性和可维护性至关重要。良好的监控能力可以预防故障和提升运维效率。

**Independent Test**: 可以通过健康检查接口验证数据库连接状态和任务队列状态,模拟连接失败场景验证系统的容错能力。

**Acceptance Scenarios**:

1. **Given** 系统启动时, **When** 初始化数据库连接池, **Then** 应成功建立连接,并验证数据库可访问性
2. **Given** 数据库连接出现问题, **When** 检测到连接失败, **Then** 系统应记录错误日志,并尝试重新建立连接
3. **Given** 需要监控系统健康状态, **When** 调用健康检查接口, **Then** 应返回数据库和任务队列的当前状态(正常/异常)
4. **Given** 系统关闭时, **When** 执行清理操作, **Then** 应优雅地关闭数据库连接和任务队列,等待正在执行的任务完成

---

### Edge Cases

- 当数据库连接池耗尽时,新的数据库请求会排队等待可用连接,等待超时时间为5秒。超时后返回503 Service Unavailable错误,错误消息提示"数据库连接池繁忙,请稍后重试"
- 当任务队列积压过多任务(超过 10,000 个待处理任务或 Redis 内存使用超过 80%)时,系统应触发告警,并考虑暂停低优先级任务提交或扩展 Worker 进程数量
- 当数据库执行慢查询时,系统使用context.WithTimeout为每个数据库操作设置超时时间(默认3秒)。超时后自动取消查询并返回504 Gateway Timeout错误,错误消息提示"数据库查询超时,请优化查询条件或联系管理员"
- 当任务重复执行5次后仍然失败时,任务应被标记为"最终失败"状态,记录完整错误历史,并可选择发送告警通知或进入死信队列等待人工处理
- 当PostgreSQL主从切换时,系统依赖GORM的自动重连机制。当检测到连接失败或不可用时,GORM会自动尝试重新建立连接。失败的查询会返回数据库连接错误,应用层应在合理范围内进行重试(建议重试1-3次,每次间隔100ms)
- 当并发事务产生死锁时,PostgreSQL会自动检测并中止其中一个事务(返回SQLSTATE 40P01错误)。应用层捕获死锁错误后,应自动重试该事务(建议最多重试3次,每次间隔50-100ms随机延迟)。超过重试次数后,返回409 Conflict错误,提示"数据库操作冲突,请稍后重试"
- 当系统重启时,所有未完成的任务(包括排队中和执行中的任务)会利用Asynq的Redis持久化机制自动重新排队,重启后Worker进程会继续处理这些任务。所有任务处理逻辑必须设计为幂等操作,确保任务重复执行不会产生副作用或数据不一致

## Requirements *(mandatory)*

### Functional Requirements

- **FR-001**: 系统必须能够建立和管理与PostgreSQL数据库的连接池,支持配置最大连接数、空闲连接数等参数。数据库连接配置(包括主机地址、端口、用户名、密码、数据库名)存储在配置文件(config.yaml)中,明文形式保存。当连接池耗尽时,新请求排队等待可用连接(默认超时5秒),超时后返回503错误。系统依赖GORM的自动重连机制处理数据库连接失败或主从切换场景
- **FR-002**: 系统必须支持标准的CRUD操作(创建、读取、更新、删除),并提供统一的数据访问接口。接口应包括但不限于: Create(创建记录)、GetByID(按ID查询)、Update(更新记录)、Delete(软删除)、List(分页列表查询)等基础方法,所有 Store 层接口遵循一致的命名和参数约定(详见 data-model.md)
- **FR-003**: 系统必须支持数据库事务,包括事务的开始、提交、回滚操作,确保数据一致性。当发生死锁时(SQLSTATE 40P01),系统应捕获错误并自动重试事务(最多3次,每次间隔50-100ms随机延迟),超过重试次数后返回409错误
- **FR-004**: 系统必须支持数据库迁移,使用外部迁移工具(如golang-migrate)通过版本化的SQL迁移文件管理表结构的创建和变更,不使用GORM AutoMigrate功能。迁移文件应包含up/down脚本以支持正向迁移和回滚
- **FR-005**: 系统必须提供查询构建能力,支持条件查询、分页、排序、关联查询等常见操作。所有数据库查询必须使用context.WithTimeout设置超时时间(默认3秒),超时后自动取消查询并返回504错误
- **FR-006**: 系统必须能够将任务提交到异步任务队列,任务应包含任务类型、参数、优先级等信息
- **FR-007**: 系统必须提供后台工作进程,从任务队列中获取任务并执行。支持启动多个worker进程实例,每个进程可独立配置并发处理数(默认10个并发goroutine)。不同任务类型可配置到不同的队列,并设置队列优先级,实现资源隔离和灵活扩展。Worker 进程异常退出时,Asynq 会自动将执行中的任务标记为失败并重新排队;建议使用进程管理工具(如 systemd, supervisord)实现 Worker 自动重启
- **FR-008**: 系统必须支持任务重试机制,当任务执行失败时能够按配置的策略自动重试。默认最大重试5次,采用指数退避策略(重试间隔为1s、2s、4s、8s、16s),每个任务类型可独立配置重试参数
- **FR-009**: 系统必须支持任务优先级,高优先级任务应优先被处理
- **FR-010**: 系统必须能够记录任务执行历史和状态,包括开始时间、结束时间、执行结果、错误信息等。任务执行状态通过日志文件记录,不使用外部指标收集系统
- **FR-011**: 系统必须提供健康检查接口,能够验证数据库连接和任务队列的可用性
- **FR-012**: 系统必须支持定时任务,能够按照cron表达式或固定间隔调度任务执行
- **FR-013**: 系统必须记录慢查询日志,当数据库查询超过阈值(100ms)时记录详细信息用于优化。日志应包含 SQL 语句、执行时间、参数和上下文信息。监控采用日志文件方式,不使用 Prometheus 或其他指标收集系统
- **FR-014**: 系统必须支持配置化的数据库和任务队列参数,如连接字符串、最大重试次数、任务超时时间等
- **FR-015**: 系统必须在关闭时优雅地清理资源,关闭数据库连接并等待正在执行的任务完成
- **FR-016**: 系统必须支持任务持久化和故障恢复。利用Asynq基于Redis的持久化机制,确保系统重启或崩溃时未完成的任务不会丢失。所有任务处理函数必须设计为幂等操作,支持任务重新执行而不产生副作用

### Technical Requirements (Constitution-Driven)

**Tech Stack Compliance**:
- [x] 所有数据库操作使用GORM (不直接使用 `database/sql`)
- [x] 数据库迁移使用golang-migrate (不使用GORM AutoMigrate)
- [x] 所有异步任务使用Asynq
- [x] 所有HTTP操作使用Fiber框架 (不使用 `net/http`)
- [x] 所有JSON操作使用sonic (不使用 `encoding/json`)
- [x] 所有日志使用Zap + Lumberjack.v2
- [x] 所有配置使用Viper
- [x] 使用Go官方工具链: `go fmt`, `go vet`, `golangci-lint`

**Architecture Requirements**:
- [x] 实现遵循 Handler → Service → Store → Model 分层架构
- [x] 依赖通过结构体字段注入(不使用构造函数模式)
- [x] 统一错误码定义在 `pkg/errors/`
- [x] 统一API响应通过 `pkg/response/`
- [x] 所有常量定义在 `pkg/constants/` (不使用魔法数字/字符串)
- [x] **不允许硬编码值: 3次及以上相同字面量必须定义为常量**
- [x] **已定义的常量必须使用(不允许重复硬编码)**
- [x] **代码注释优先使用中文(实现注释用中文)**
- [x] **日志消息使用中文(logger.Info/Warn/Error/Debug用中文)**
- [x] **错误消息支持中文(面向用户的错误有中文文本)**
- [x] 所有Redis键通过 `pkg/constants/` 键生成函数管理
- [x] 包结构扁平化,按功能组织(不按层级)

**Go Idiomatic Design Requirements**:
- [x] 不使用Java风格模式: 无getter/setter方法、无I-前缀接口、无Impl-后缀
- [x] 接口应小型化(1-3个方法),在使用处定义
- [x] 错误处理显式化(返回错误,不使用panic)
- [x] 使用组合(结构体嵌入)而非继承
- [x] 使用goroutines和channels处理并发
- [x] 命名遵循Go约定: `UserID` 不是 `userId`, `HTTPServer` 不是 `HttpServer`
- [x] 不使用匈牙利命名法或类型前缀
- [x] 代码结构简单直接

**API Design Requirements**:
- [x] 所有API遵循RESTful原则
- [x] 所有响应使用统一JSON格式,包含code/message/data/timestamp
- [x] 所有错误消息包含错误码和双语描述
- [x] 所有分页使用标准参数(page, page_size, total)
- [x] 所有时间字段使用ISO 8601格式(RFC3339)
- [x] 所有货币金额使用整数(分)

**Performance Requirements**:
- [x] API响应时间(P95) < 200ms
- [x] 数据库查询 < 50ms
- [x] 批量操作使用bulk查询
- [x] 列表查询实现分页(默认20条,最大100条)
- [x] 非实时操作委托给异步任务
- [x] 使用 `context.Context` 进行超时和取消控制

**Testing Requirements**:
- [x] Service层业务逻辑必须有单元测试
- [x] 所有API端点必须有集成测试
- [x] 所有异步任务处理函数必须有幂等性测试,验证重复执行的正确性
- [x] 测试使用Go标准testing框架,文件名为 `*_test.go`
- [x] 多测试用例使用表驱动测试
- [x] 测试相互独立,使用mocks/testcontainers
- [x] 目标覆盖率: 总体70%+, 核心业务逻辑90%+

### Key Entities

- **DatabaseConnection**: 代表与PostgreSQL数据库的连接,包含连接池配置、连接状态、健康检查等属性
- **DataModel**: 代表业务数据模型,通过ORM映射到数据库表,包含数据验证规则和关联关系
- **Task**: 代表异步任务,包含任务类型、任务参数、优先级、重试次数、执行状态等属性
- **TaskQueue**: 代表任务队列,管理任务的提交、调度、执行和状态跟踪
- **Worker**: 代表后台工作进程,从任务队列中获取任务并执行。每个Worker进程支持可配置的并发数(通过goroutine池实现),可以部署多个Worker进程实例实现水平扩展。不同Worker可订阅不同的任务队列,实现任务类型的资源隔离

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: 数据库基本CRUD操作响应时间(P95)应小于50毫秒
- **SC-002**: 系统应支持至少1000个并发数据库连接而不出现连接池耗尽
- **SC-003**: 任务队列应能够处理每秒至少100个任务的提交速率
- **SC-004**: 异步任务从提交到开始执行的延迟(空闲情况下)应小于100毫秒
- **SC-005**: 数据持久化的可靠性应达到99.99%,即每10000次操作中失败不超过1次
- **SC-006**: 失败任务的自动重试成功率应达到90%以上
- **SC-007**: 系统启动时应在10秒内完成数据库连接和任务队列初始化
- **SC-008**: 数据库查询慢查询(超过100ms)的占比应小于1%
- **SC-009**: 系统关闭时应在30秒内优雅完成所有资源清理,不丢失正在执行的任务
- **SC-010**: 健康检查接口应在1秒内返回系统健康状态
