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:
644
specs/002-gorm-postgres-asynq/data-model.md
Normal file
644
specs/002-gorm-postgres-asynq/data-model.md
Normal file
@@ -0,0 +1,644 @@
|
||||
# Data Model: 数据持久化与异步任务处理集成
|
||||
|
||||
**Feature**: 002-gorm-postgres-asynq
|
||||
**Date**: 2025-11-12
|
||||
**Purpose**: 定义数据模型、配置结构和系统实体
|
||||
|
||||
## 概述
|
||||
|
||||
本文档定义了数据持久化和异步任务处理功能的数据模型,包括配置结构、数据库实体示例和任务载荷结构。
|
||||
|
||||
---
|
||||
|
||||
## 1. 配置模型
|
||||
|
||||
### 1.1 数据库配置
|
||||
|
||||
```go
|
||||
// pkg/config/config.go
|
||||
|
||||
// DatabaseConfig 数据库连接配置
|
||||
type DatabaseConfig struct {
|
||||
// 连接参数
|
||||
Host string `mapstructure:"host"` // 数据库主机地址
|
||||
Port int `mapstructure:"port"` // 数据库端口
|
||||
User string `mapstructure:"user"` // 数据库用户名
|
||||
Password string `mapstructure:"password"` // 数据库密码(明文存储)
|
||||
DBName string `mapstructure:"dbname"` // 数据库名称
|
||||
SSLMode string `mapstructure:"sslmode"` // SSL 模式:disable, require, verify-ca, verify-full
|
||||
|
||||
// 连接池配置
|
||||
MaxOpenConns int `mapstructure:"max_open_conns"` // 最大打开连接数(默认:25)
|
||||
MaxIdleConns int `mapstructure:"max_idle_conns"` // 最大空闲连接数(默认:10)
|
||||
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"` // 连接最大生命周期(默认:5m)
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| Host | string | localhost | PostgreSQL 服务器地址 |
|
||||
| Port | int | 5432 | PostgreSQL 服务器端口 |
|
||||
| User | string | postgres | 数据库用户名 |
|
||||
| Password | string | - | 数据库密码(明文存储在配置文件中) |
|
||||
| DBName | string | junhong_cmp | 数据库名称 |
|
||||
| SSLMode | string | disable | SSL 连接模式 |
|
||||
| MaxOpenConns | int | 25 | 最大数据库连接数 |
|
||||
| MaxIdleConns | int | 10 | 最大空闲连接数 |
|
||||
| ConnMaxLifetime | duration | 5m | 连接最大存活时间 |
|
||||
|
||||
### 1.2 任务队列配置
|
||||
|
||||
```go
|
||||
// pkg/config/config.go
|
||||
|
||||
// QueueConfig 任务队列配置
|
||||
type QueueConfig struct {
|
||||
// 并发配置
|
||||
Concurrency int `mapstructure:"concurrency"` // Worker 并发数(默认:10)
|
||||
|
||||
// 队列优先级配置(队列名 -> 权重)
|
||||
Queues map[string]int `mapstructure:"queues"` // 例如:{"critical": 6, "default": 3, "low": 1}
|
||||
|
||||
// 重试配置
|
||||
RetryMax int `mapstructure:"retry_max"` // 最大重试次数(默认:5)
|
||||
Timeout time.Duration `mapstructure:"timeout"` // 任务超时时间(默认:10m)
|
||||
}
|
||||
```
|
||||
|
||||
**队列优先级**:
|
||||
- `critical`: 关键任务(权重 6,约 60% 处理时间)
|
||||
- `default`: 普通任务(权重 3,约 30% 处理时间)
|
||||
- `low`: 低优先级任务(权重 1,约 10% 处理时间)
|
||||
|
||||
### 1.3 完整配置结构
|
||||
|
||||
```go
|
||||
// pkg/config/config.go
|
||||
|
||||
// Config 应用配置
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
Logging LoggingConfig `mapstructure:"logging"`
|
||||
Redis RedisConfig `mapstructure:"redis"`
|
||||
Database DatabaseConfig `mapstructure:"database"` // 新增
|
||||
Queue QueueConfig `mapstructure:"queue"` // 新增
|
||||
Middleware MiddlewareConfig `mapstructure:"middleware"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据库实体模型
|
||||
|
||||
### 2.1 基础模型(Base Model)
|
||||
|
||||
```go
|
||||
// internal/model/base.go
|
||||
|
||||
import (
|
||||
"time"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// BaseModel 基础模型,包含通用字段
|
||||
type BaseModel struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 软删除
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `ID`: 自增主键
|
||||
- `CreatedAt`: 创建时间(GORM 自动管理)
|
||||
- `UpdatedAt`: 更新时间(GORM 自动管理)
|
||||
- `DeletedAt`: 删除时间(软删除,GORM 自动过滤已删除记录)
|
||||
|
||||
### 2.2 示例实体:用户模型
|
||||
|
||||
```go
|
||||
// internal/model/user.go
|
||||
|
||||
// User 用户实体
|
||||
type User struct {
|
||||
BaseModel
|
||||
|
||||
// 基本信息
|
||||
Username string `gorm:"uniqueIndex;not null;size:50" json:"username"`
|
||||
Email string `gorm:"uniqueIndex;not null;size:100" json:"email"`
|
||||
Password string `gorm:"not null;size:255" json:"-"` // 不返回给客户端
|
||||
|
||||
// 状态字段
|
||||
Status string `gorm:"not null;size:20;default:'active';index" json:"status"`
|
||||
|
||||
// 元数据
|
||||
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (User) TableName() string {
|
||||
return "tb_user"
|
||||
}
|
||||
```
|
||||
|
||||
**索引策略**:
|
||||
- `username`: 唯一索引(快速查找和去重)
|
||||
- `email`: 唯一索引(快速查找和去重)
|
||||
- `status`: 普通索引(状态过滤查询)
|
||||
- `deleted_at`: 自动索引(软删除过滤)
|
||||
|
||||
**验证规则**:
|
||||
- `username`: 长度 3-50 字符,字母数字下划线
|
||||
- `email`: 标准邮箱格式
|
||||
- `password`: 长度 >= 8 字符,bcrypt 哈希存储
|
||||
- `status`: 枚举值(active, inactive, suspended)
|
||||
|
||||
### 2.3 示例实体:订单模型(演示手动关联关系)
|
||||
|
||||
```go
|
||||
// internal/model/order.go
|
||||
|
||||
// Order 订单实体
|
||||
type Order struct {
|
||||
BaseModel
|
||||
|
||||
// 业务唯一键
|
||||
OrderID string `gorm:"uniqueIndex;not null;size:50" json:"order_id"`
|
||||
|
||||
// 关联关系(仅存储 ID,不使用 GORM 关联)
|
||||
UserID uint `gorm:"not null;index" json:"user_id"`
|
||||
|
||||
// 订单信息
|
||||
Amount int64 `gorm:"not null" json:"amount"` // 金额(分)
|
||||
Status string `gorm:"not null;size:20;index" json:"status"`
|
||||
Remark string `gorm:"size:500" json:"remark,omitempty"`
|
||||
|
||||
// 时间字段
|
||||
PaidAt *time.Time `json:"paid_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Order) TableName() string {
|
||||
return "tb_order"
|
||||
}
|
||||
```
|
||||
|
||||
**关联关系说明**:
|
||||
- `UserID`: 存储关联用户的 ID(普通字段,无数据库外键约束)
|
||||
- **无 ORM 关联**:遵循 Constitution Principle IX,不使用 `foreignKey`、`belongsTo` 等标签
|
||||
- 关联数据查询在 Service 层手动实现(见下方示例)
|
||||
|
||||
**手动查询关联数据示例**:
|
||||
```go
|
||||
// internal/service/order/service.go
|
||||
|
||||
// GetOrderWithUser 查询订单及关联的用户信息
|
||||
func (s *Service) GetOrderWithUser(ctx context.Context, orderID uint) (*OrderDetail, error) {
|
||||
// 1. 查询订单
|
||||
order, err := s.store.Order.GetByID(ctx, orderID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询订单失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 手动查询关联的用户
|
||||
user, err := s.store.User.GetByID(ctx, order.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询用户失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 组装返回数据
|
||||
return &OrderDetail{
|
||||
Order: order,
|
||||
User: user,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListOrdersByUserID 查询指定用户的订单列表
|
||||
func (s *Service) ListOrdersByUserID(ctx context.Context, userID uint, page, pageSize int) ([]*Order, int64, error) {
|
||||
return s.store.Order.ListByUserID(ctx, userID, page, pageSize)
|
||||
}
|
||||
```
|
||||
|
||||
**状态流转**:
|
||||
```
|
||||
pending → paid → processing → completed
|
||||
↓
|
||||
cancelled
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据传输对象(DTO)
|
||||
|
||||
### 3.1 用户 DTO
|
||||
|
||||
```go
|
||||
// internal/model/user_dto.go
|
||||
|
||||
// CreateUserRequest 创建用户请求
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username" validate:"required,min=3,max=50,alphanum"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=8"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest 更新用户请求
|
||||
type UpdateUserRequest struct {
|
||||
Email *string `json:"email" validate:"omitempty,email"`
|
||||
Status *string `json:"status" validate:"omitempty,oneof=active inactive suspended"`
|
||||
}
|
||||
|
||||
// UserResponse 用户响应
|
||||
type UserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
||||
}
|
||||
|
||||
// ListUsersResponse 用户列表响应
|
||||
type ListUsersResponse struct {
|
||||
Users []UserResponse `json:"users"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Total int64 `json:"total"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 任务载荷模型
|
||||
|
||||
### 4.1 任务类型常量
|
||||
|
||||
```go
|
||||
// pkg/constants/constants.go
|
||||
|
||||
const (
|
||||
// 任务类型
|
||||
TaskTypeEmailSend = "email:send" // 发送邮件
|
||||
TaskTypeDataSync = "data:sync" // 数据同步
|
||||
TaskTypeSIMStatusSync = "sim:status:sync" // SIM 卡状态同步
|
||||
TaskTypeCommission = "commission:calculate" // 分佣计算
|
||||
)
|
||||
```
|
||||
|
||||
### 4.2 邮件任务载荷
|
||||
|
||||
```go
|
||||
// internal/task/email.go
|
||||
|
||||
// EmailPayload 邮件任务载荷
|
||||
type EmailPayload struct {
|
||||
RequestID string `json:"request_id"` // 幂等性标识
|
||||
To string `json:"to"` // 收件人
|
||||
Subject string `json:"subject"` // 主题
|
||||
Body string `json:"body"` // 正文
|
||||
CC []string `json:"cc,omitempty"` // 抄送
|
||||
Attachments []string `json:"attachments,omitempty"` // 附件路径
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 数据同步任务载荷
|
||||
|
||||
```go
|
||||
// internal/task/sync.go
|
||||
|
||||
// DataSyncPayload 数据同步任务载荷
|
||||
type DataSyncPayload struct {
|
||||
RequestID string `json:"request_id"` // 幂等性标识
|
||||
SyncType string `json:"sync_type"` // 同步类型:sim_status, flow_usage, real_name
|
||||
StartDate string `json:"start_date"` // 开始日期(YYYY-MM-DD)
|
||||
EndDate string `json:"end_date"` // 结束日期(YYYY-MM-DD)
|
||||
BatchSize int `json:"batch_size"` // 批量大小(默认:100)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 SIM 卡状态同步载荷
|
||||
|
||||
```go
|
||||
// internal/task/sim.go
|
||||
|
||||
// SIMStatusSyncPayload SIM 卡状态同步任务载荷
|
||||
type SIMStatusSyncPayload struct {
|
||||
RequestID string `json:"request_id"` // 幂等性标识
|
||||
ICCIDs []string `json:"iccids"` // ICCID 列表
|
||||
ForceSync bool `json:"force_sync"` // 强制同步(忽略缓存)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据库 Schema(SQL)
|
||||
|
||||
### 5.1 初始化 Schema
|
||||
|
||||
```sql
|
||||
-- migrations/000001_init_schema.up.sql
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS tb_user (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
|
||||
-- 基本信息
|
||||
username VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
|
||||
-- 状态字段
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||
|
||||
-- 元数据
|
||||
last_login_at TIMESTAMP,
|
||||
|
||||
-- 唯一约束
|
||||
CONSTRAINT uk_user_username UNIQUE (username),
|
||||
CONSTRAINT uk_user_email UNIQUE (email)
|
||||
);
|
||||
|
||||
-- 用户表索引
|
||||
CREATE INDEX idx_user_deleted_at ON tb_user(deleted_at);
|
||||
CREATE INDEX idx_user_status ON tb_user(status);
|
||||
CREATE INDEX idx_user_created_at ON tb_user(created_at);
|
||||
|
||||
-- 订单表
|
||||
CREATE TABLE IF NOT EXISTS tb_order (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP,
|
||||
|
||||
-- 业务唯一键
|
||||
order_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 关联关系(注意:无数据库外键约束,在代码中管理)
|
||||
user_id INTEGER NOT NULL,
|
||||
|
||||
-- 订单信息
|
||||
amount BIGINT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
remark VARCHAR(500),
|
||||
|
||||
-- 时间字段
|
||||
paid_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
|
||||
-- 唯一约束
|
||||
CONSTRAINT uk_order_order_id UNIQUE (order_id)
|
||||
);
|
||||
|
||||
-- 订单表索引
|
||||
CREATE INDEX idx_order_deleted_at ON tb_order(deleted_at);
|
||||
CREATE INDEX idx_order_user_id ON tb_order(user_id);
|
||||
CREATE INDEX idx_order_status ON tb_order(status);
|
||||
CREATE INDEX idx_order_created_at ON tb_order(created_at);
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON TABLE tb_user IS '用户表';
|
||||
COMMENT ON COLUMN tb_user.username IS '用户名(唯一)';
|
||||
COMMENT ON COLUMN tb_user.email IS '邮箱(唯一)';
|
||||
COMMENT ON COLUMN tb_user.password IS '密码(bcrypt 哈希)';
|
||||
COMMENT ON COLUMN tb_user.status IS '用户状态:active, inactive, suspended';
|
||||
COMMENT ON COLUMN tb_user.deleted_at IS '软删除时间';
|
||||
|
||||
COMMENT ON TABLE tb_order IS '订单表';
|
||||
COMMENT ON COLUMN tb_order.order_id IS '订单号(业务唯一键)';
|
||||
COMMENT ON COLUMN tb_order.user_id IS '用户 ID(在代码中维护关联,无数据库外键)';
|
||||
COMMENT ON COLUMN tb_order.amount IS '金额(分)';
|
||||
COMMENT ON COLUMN tb_order.status IS '订单状态:pending, paid, processing, completed, cancelled';
|
||||
COMMENT ON COLUMN tb_order.deleted_at IS '软删除时间';
|
||||
```
|
||||
|
||||
**重要说明**:
|
||||
- ✅ **无外键约束**:`user_id` 仅作为普通字段存储,无 `REFERENCES` 约束
|
||||
- ✅ **无触发器**:`created_at` 和 `updated_at` 由 GORM 自动管理,无需数据库触发器
|
||||
- ✅ **遵循 Constitution Principle IX**:表关系在代码层面手动维护
|
||||
|
||||
### 5.2 回滚 Schema
|
||||
|
||||
```sql
|
||||
-- migrations/000001_init_schema.down.sql
|
||||
|
||||
-- 删除表(按依赖顺序倒序删除)
|
||||
DROP TABLE IF EXISTS tb_order;
|
||||
DROP TABLE IF EXISTS tb_user;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Redis 键结构
|
||||
|
||||
### 6.1 任务锁键
|
||||
|
||||
```go
|
||||
// pkg/constants/redis.go
|
||||
|
||||
// RedisTaskLockKey 生成任务锁键
|
||||
// 格式: task:lock:{request_id}
|
||||
// 用途: 幂等性控制
|
||||
// 过期时间: 24 小时
|
||||
func RedisTaskLockKey(requestID string) string {
|
||||
return fmt.Sprintf("task:lock:%s", requestID)
|
||||
}
|
||||
```
|
||||
|
||||
**使用示例**:
|
||||
```go
|
||||
key := constants.RedisTaskLockKey("req-123456")
|
||||
// 结果: "task:lock:req-123456"
|
||||
```
|
||||
|
||||
### 6.2 任务状态键
|
||||
|
||||
```go
|
||||
// RedisTaskStatusKey 生成任务状态键
|
||||
// 格式: task:status:{task_id}
|
||||
// 用途: 存储任务执行状态
|
||||
// 过期时间: 7 天
|
||||
func RedisTaskStatusKey(taskID string) string {
|
||||
return fmt.Sprintf("task:status:%s", taskID)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 常量定义
|
||||
|
||||
### 7.1 用户状态常量
|
||||
|
||||
```go
|
||||
// pkg/constants/constants.go
|
||||
|
||||
const (
|
||||
// 用户状态
|
||||
UserStatusActive = "active" // 激活
|
||||
UserStatusInactive = "inactive" // 未激活
|
||||
UserStatusSuspended = "suspended" // 暂停
|
||||
)
|
||||
```
|
||||
|
||||
### 7.2 订单状态常量
|
||||
|
||||
```go
|
||||
const (
|
||||
// 订单状态
|
||||
OrderStatusPending = "pending" // 待支付
|
||||
OrderStatusPaid = "paid" // 已支付
|
||||
OrderStatusProcessing = "processing" // 处理中
|
||||
OrderStatusCompleted = "completed" // 已完成
|
||||
OrderStatusCancelled = "cancelled" // 已取消
|
||||
)
|
||||
```
|
||||
|
||||
### 7.3 数据库配置常量
|
||||
|
||||
```go
|
||||
const (
|
||||
// 数据库连接池默认值
|
||||
DefaultMaxOpenConns = 25
|
||||
DefaultMaxIdleConns = 10
|
||||
DefaultConnMaxLifetime = 5 * time.Minute
|
||||
|
||||
// 查询限制
|
||||
DefaultPageSize = 20
|
||||
MaxPageSize = 100
|
||||
|
||||
// 慢查询阈值
|
||||
SlowQueryThreshold = 100 * time.Millisecond
|
||||
)
|
||||
```
|
||||
|
||||
### 7.4 任务队列常量
|
||||
|
||||
```go
|
||||
const (
|
||||
// 队列名称
|
||||
QueueCritical = "critical"
|
||||
QueueDefault = "default"
|
||||
QueueLow = "low"
|
||||
|
||||
// 默认重试配置
|
||||
DefaultRetryMax = 5
|
||||
DefaultTimeout = 10 * time.Minute
|
||||
|
||||
// 默认并发数
|
||||
DefaultConcurrency = 10
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 实体关系图(ER Diagram)
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ tb_user │
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ username (UQ) │
|
||||
│ email (UQ) │
|
||||
│ password │
|
||||
│ status │
|
||||
│ last_login_at │
|
||||
│ created_at │
|
||||
│ updated_at │
|
||||
│ deleted_at │
|
||||
└────────┬────────┘
|
||||
│
|
||||
│ 1:N (代码层面维护)
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ tb_order │
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ order_id (UQ) │
|
||||
│ user_id │ ← 存储关联 ID(无数据库外键)
|
||||
│ amount │
|
||||
│ status │
|
||||
│ remark │
|
||||
│ paid_at │
|
||||
│ completed_at │
|
||||
│ created_at │
|
||||
│ updated_at │
|
||||
│ deleted_at │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
**关系说明**:
|
||||
- 一个用户可以有多个订单(1:N 关系)
|
||||
- 订单通过 `user_id` 字段存储用户 ID,**在代码层面维护关联**
|
||||
- **无数据库外键约束**:遵循 Constitution Principle IX
|
||||
- 关联查询在 Service 层手动实现(参见 2.3 节示例代码)
|
||||
|
||||
---
|
||||
|
||||
## 9. 数据验证规则
|
||||
|
||||
### 9.1 用户字段验证
|
||||
|
||||
| 字段 | 验证规则 | 错误消息 |
|
||||
|------|----------|----------|
|
||||
| username | required, min=3, max=50, alphanum | 用户名必填,3-50 个字母数字字符 |
|
||||
| email | required, email | 邮箱必填且格式正确 |
|
||||
| password | required, min=8 | 密码必填,至少 8 个字符 |
|
||||
| status | oneof=active inactive suspended | 状态必须为 active, inactive, suspended 之一 |
|
||||
|
||||
### 9.2 订单字段验证
|
||||
|
||||
| 字段 | 验证规则 | 错误消息 |
|
||||
|------|----------|----------|
|
||||
| order_id | required, min=10, max=50 | 订单号必填,10-50 个字符 |
|
||||
| user_id | required, gt=0 | 用户 ID 必填且大于 0 |
|
||||
| amount | required, gte=0 | 金额必填且大于等于 0 |
|
||||
| status | oneof=pending paid processing completed cancelled | 状态值无效 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 数据迁移版本
|
||||
|
||||
| 版本 | 文件名 | 描述 | 日期 |
|
||||
|------|--------|------|------|
|
||||
| 1 | 000001_init_schema | 初始化用户表和订单表 | 2025-11-12 |
|
||||
|
||||
**添加新迁移**:
|
||||
```bash
|
||||
# 创建新迁移文件
|
||||
migrate create -ext sql -dir migrations -seq add_sim_table
|
||||
|
||||
# 生成文件:
|
||||
# migrations/000002_add_sim_table.up.sql
|
||||
# migrations/000002_add_sim_table.down.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
本数据模型定义了:
|
||||
|
||||
1. **配置模型**:数据库连接配置、任务队列配置
|
||||
2. **实体模型**:基础模型、用户模型、订单模型(示例)
|
||||
3. **DTO 模型**:请求/响应数据传输对象
|
||||
4. **任务载荷**:各类异步任务的载荷结构
|
||||
5. **数据库 Schema**:SQL 迁移脚本
|
||||
6. **Redis 键结构**:任务锁、任务状态等键生成函数
|
||||
7. **常量定义**:状态枚举、默认配置值
|
||||
8. **验证规则**:字段级别的数据验证规则
|
||||
|
||||
**设计原则**:
|
||||
- 遵循 GORM 约定(BaseModel、软删除)
|
||||
- 遵循 Constitution 命名规范(PascalCase 字段、snake_case 列名)
|
||||
- 统一使用常量定义(避免硬编码)
|
||||
- 支持软删除和审计字段(created_at, updated_at)
|
||||
- 使用数据库约束保证数据完整性
|
||||
Reference in New Issue
Block a user