docs(constitution): 新增数据库设计原则(v2.4.0)

在项目宪章中新增第九条原则"数据库设计原则",明确禁止使用数据库外键约束和ORM关联标签。

主要变更:
- 新增原则IX:数据库设计原则(Database Design Principles)
- 强制要求:数据库表不得使用外键约束
- 强制要求:GORM模型不得使用ORM关联标签(foreignKey、hasMany等)
- 强制要求:表关系必须通过ID字段手动维护
- 强制要求:关联数据查询必须显式编写,避免ORM魔法
- 强制要求:时间字段由GORM处理,不使用数据库触发器

设计理念:
- 提升业务逻辑灵活性(无数据库约束限制)
- 优化高并发性能(无外键检查开销)
- 增强代码可读性(显式查询,无隐式预加载)
- 简化数据库架构和迁移流程
- 支持分布式和微服务场景

版本升级:2.3.0 → 2.4.0(MINOR)
This commit is contained in:
2025-11-13 13:40:19 +08:00
parent ea0c6a8b16
commit 984ccccc63
63 changed files with 12099 additions and 83 deletions

View File

@@ -0,0 +1,733 @@
openapi: 3.0.3
info:
title: 数据持久化与异步任务处理集成 API
description: |
GORM + PostgreSQL + Asynq 集成的数据持久化和异步任务处理功能 API 规范
**Feature**: 002-gorm-postgres-asynq
**Date**: 2025-11-12
## 核心功能
- 数据库连接管理和健康检查
- 异步任务提交和管理
- 数据 CRUD 操作(示例:用户管理)
## 技术栈
- Fiber (HTTP 框架)
- GORM (ORM)
- PostgreSQL (数据库)
- Asynq (任务队列)
- Redis (任务队列存储)
version: 1.0.0
contact:
name: API Support
email: support@example.com
servers:
- url: http://localhost:8080/api/v1
description: 开发环境
- url: http://staging.example.com/api/v1
description: 预发布环境
- url: https://api.example.com/api/v1
description: 生产环境
tags:
- name: Health
description: 健康检查和系统状态
- name: Users
description: 用户管理(数据库操作示例)
- name: Tasks
description: 异步任务管理
paths:
/health:
get:
tags:
- Health
summary: 健康检查
description: |
检查系统健康状态,包括数据库连接和 Redis 连接
**测试用例**:
- FR-011: 系统必须提供健康检查接口
- SC-010: 健康检查应在 1 秒内返回
operationId: healthCheck
responses:
'200':
description: 系统健康
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [ok]
description: 系统整体状态
postgres:
type: string
enum: [up, down]
description: PostgreSQL 连接状态
redis:
type: string
enum: [up, down]
description: Redis 连接状态
example:
status: ok
postgres: up
redis: up
'503':
description: 服务降级或不可用
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [degraded, unavailable]
postgres:
type: string
enum: [up, down]
redis:
type: string
enum: [up, down]
error:
type: string
description: 错误详情
example:
status: degraded
postgres: down
redis: up
error: "数据库连接失败"
/users:
post:
tags:
- Users
summary: 创建用户
description: |
创建新用户(演示数据库 CRUD 操作)
**测试用例**:
- FR-002: 支持标准 CRUD 操作
- FR-003: 支持数据库事务
- User Story 1 - Acceptance 1: 数据持久化
operationId: createUser
security:
- TokenAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'200':
description: 用户创建成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/UserResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
get:
tags:
- Users
summary: 用户列表
description: |
分页查询用户列表
**测试用例**:
- FR-002: 支持分页列表查询
- FR-005: 支持条件查询、分页、排序
- User Story 1 - Acceptance 5: 分页和排序
operationId: listUsers
security:
- TokenAuth: []
parameters:
- name: page
in: query
schema:
type: integer
default: 1
minimum: 1
description: 页码
- name: page_size
in: query
schema:
type: integer
default: 20
minimum: 1
maximum: 100
description: 每页条数(最大 100
- name: status
in: query
schema:
type: string
enum: [active, inactive, suspended]
description: 用户状态过滤
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/ListUsersResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/users/{id}:
get:
tags:
- Users
summary: 获取用户详情
description: |
根据用户 ID 获取详细信息
**测试用例**:
- FR-002: 支持按 ID 查询
- User Story 1 - Acceptance 1: 数据检索
operationId: getUserById
security:
- TokenAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
description: 用户 ID
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/UserResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- Users
summary: 更新用户
description: |
更新用户信息
**测试用例**:
- FR-002: 支持更新操作
- User Story 1 - Acceptance 2: 数据更新
operationId: updateUser
security:
- TokenAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
description: 用户 ID
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: 更新成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/UserResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- Users
summary: 删除用户
description: |
软删除用户(设置 deleted_at 字段)
**测试用例**:
- FR-002: 支持软删除操作
- User Story 1 - Acceptance 3: 数据删除
operationId: deleteUser
security:
- TokenAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
description: 用户 ID
responses:
'200':
description: 删除成功
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
/tasks/email:
post:
tags:
- Tasks
summary: 提交邮件发送任务
description: |
将邮件发送任务提交到异步队列
**测试用例**:
- FR-006: 提交任务到异步队列
- FR-008: 任务重试机制
- User Story 2 - Acceptance 1: 任务提交
operationId: submitEmailTask
security:
- TokenAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EmailTaskRequest'
responses:
'200':
description: 任务已提交
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/TaskResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/tasks/sync:
post:
tags:
- Tasks
summary: 提交数据同步任务
description: |
将数据同步任务提交到异步队列(支持优先级)
**测试用例**:
- FR-006: 提交任务到异步队列
- FR-009: 任务优先级支持
- User Story 2 - Acceptance 1: 任务提交
operationId: submitSyncTask
security:
- TokenAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SyncTaskRequest'
responses:
'200':
description: 任务已提交
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SuccessResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/TaskResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
components:
securitySchemes:
TokenAuth:
type: apiKey
in: header
name: token
description: 认证令牌
schemas:
# 通用响应
SuccessResponse:
type: object
required:
- code
- msg
- timestamp
properties:
code:
type: integer
enum: [0]
description: 响应码0 表示成功)
msg:
type: string
example: success
description: 响应消息
data:
type: object
description: 响应数据(具体结构由各端点定义)
timestamp:
type: string
format: date-time
example: "2025-11-12T16:00:00+08:00"
description: 响应时间戳ISO 8601 格式)
ErrorResponse:
type: object
required:
- code
- msg
- timestamp
properties:
code:
type: integer
description: 错误码(非 0
example: 1001
msg:
type: string
description: 错误消息(中文)
example: "参数验证失败"
data:
type: object
nullable: true
description: 错误详情(可选)
timestamp:
type: string
format: date-time
example: "2025-11-12T16:00:00+08:00"
# 用户相关
CreateUserRequest:
type: object
required:
- username
- email
- password
properties:
username:
type: string
minLength: 3
maxLength: 50
pattern: '^[a-zA-Z0-9_]+$'
description: 用户名3-50 个字母数字下划线)
example: testuser
email:
type: string
format: email
maxLength: 100
description: 邮箱地址
example: test@example.com
password:
type: string
format: password
minLength: 8
description: 密码(至少 8 个字符)
example: password123
UpdateUserRequest:
type: object
properties:
email:
type: string
format: email
maxLength: 100
description: 邮箱地址
example: newemail@example.com
status:
type: string
enum: [active, inactive, suspended]
description: 用户状态
UserResponse:
type: object
required:
- id
- username
- email
- status
- created_at
- updated_at
properties:
id:
type: integer
description: 用户 ID
example: 1
username:
type: string
description: 用户名
example: testuser
email:
type: string
description: 邮箱地址
example: test@example.com
status:
type: string
enum: [active, inactive, suspended]
description: 用户状态
example: active
created_at:
type: string
format: date-time
description: 创建时间
example: "2025-11-12T16:00:00+08:00"
updated_at:
type: string
format: date-time
description: 更新时间
example: "2025-11-12T16:00:00+08:00"
last_login_at:
type: string
format: date-time
nullable: true
description: 最后登录时间
example: "2025-11-12T16:30:00+08:00"
ListUsersResponse:
type: object
required:
- users
- page
- page_size
- total
- total_pages
properties:
users:
type: array
items:
$ref: '#/components/schemas/UserResponse'
description: 用户列表
page:
type: integer
description: 当前页码
example: 1
page_size:
type: integer
description: 每页条数
example: 20
total:
type: integer
format: int64
description: 总记录数
example: 100
total_pages:
type: integer
description: 总页数
example: 5
# 任务相关
EmailTaskRequest:
type: object
required:
- to
- subject
- body
properties:
to:
type: string
format: email
description: 收件人邮箱
example: user@example.com
subject:
type: string
maxLength: 200
description: 邮件主题
example: Welcome to our service
body:
type: string
description: 邮件正文
example: Thank you for signing up!
cc:
type: array
items:
type: string
format: email
description: 抄送列表
example: ["manager@example.com"]
priority:
type: string
enum: [critical, default, low]
default: default
description: 任务优先级
SyncTaskRequest:
type: object
required:
- sync_type
- start_date
- end_date
properties:
sync_type:
type: string
enum: [sim_status, flow_usage, real_name]
description: 同步类型
example: sim_status
start_date:
type: string
format: date
pattern: '^\d{4}-\d{2}-\d{2}$'
description: 开始日期YYYY-MM-DD
example: "2025-11-01"
end_date:
type: string
format: date
pattern: '^\d{4}-\d{2}-\d{2}$'
description: 结束日期YYYY-MM-DD
example: "2025-11-12"
batch_size:
type: integer
minimum: 1
maximum: 1000
default: 100
description: 批量大小
priority:
type: string
enum: [critical, default, low]
default: default
description: 任务优先级
TaskResponse:
type: object
required:
- task_id
- queue
properties:
task_id:
type: string
format: uuid
description: 任务唯一 ID
example: "550e8400-e29b-41d4-a716-446655440000"
queue:
type: string
enum: [critical, default, low]
description: 任务所在队列
example: default
estimated_time:
type: string
description: 预计执行时间
example: "within 5 minutes"
responses:
BadRequest:
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
code: 1001
msg: "参数验证失败"
data: null
timestamp: "2025-11-12T16:00:00+08:00"
Unauthorized:
description: 未授权或令牌无效
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
code: 1002
msg: "缺失认证令牌"
data: null
timestamp: "2025-11-12T16:00:00+08:00"
NotFound:
description: 资源不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
code: 1003
msg: "用户不存在"
data: null
timestamp: "2025-11-12T16:00:00+08:00"
Conflict:
description: 资源冲突(如用户名已存在)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
code: 1004
msg: "用户名已存在"
data: null
timestamp: "2025-11-12T16:00:00+08:00"
InternalServerError:
description: 服务器内部错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
code: 5000
msg: "服务器内部错误"
data: null
timestamp: "2025-11-12T16:00:00+08:00"