关于 go-gorm-model
根据 SQL 迁移生成 Go GORM 持久化模型的技能,面向编写数据库映射结构体的开发者。它规定字段类型、可空值、标签及文件布局的约定。
主要功能
- 01SQL 到 Go 字段映射
- 02可空字段使用指针类型
- 03GORM 索引和默认值标签
- 04JSONB 列映射
- 05模型布局与命名规范
适用场景
- 01根据数据库迁移创建模型
- 02将 SQL 可空列映射为 Go 字段
- 03为索引和默认值添加 GORM 标签
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cristiano-pacheco/ ai-tools --skill go-gorm-model安装后在 Claude Code 中输入/go-gorm-model确认已加载。
详细介绍
Go GORM 模型
在 internal/modules/<module>/model/ 中生成 GORM 持久化模型。
适用场景
- 创建映射数据库表的持久化模型
- 添加包含可空指针类型、索引、JSONB、默认值的结构体
- 将 SQL 迁移映射为 Go 结构体
模式
模型文件必须遵循以下位置和命名规则:
- 路径:
internal/modules/<module>/model/<entity>_model.go - 包:
model - 结构体名称:
<Entity>Model - TableName 方法:
func (*<Entity>Model) TableName() string { return "<table_name>" }
文件结构
按以下顺序编写:
package model- 导入项(仅
"time"— 只有在严格需要时才添加其他导入项) - 带文档注释的结构体定义
- 带文档注释的
TableName()方法
基础模板
package model
import "time"
// EntityModel represents an entity in the database.
type EntityModel struct {
ID uint64 `gorm:"primarykey"`
Name string
CreatedAt time.Time
UpdatedAt time.Time
}
// TableName returns the table name for the entity model.
func (*EntityModel) TableName() string {
return "entities"
}
约定
ID 和主键
ID主键字段使用uint64,并始终标记为gorm:"primarykey"。- 外键字段(引用其他表主键的字段)在 NOT NULL 时也使用
uint64,可空时使用*uint64。
时间字段
TIMESTAMPTZ列使用time.Time(如CreatedAt、UpdatedAt等)。- 不需要显式列标签 — GORM 会按约定将
CreatedAt映射为created_at。
可空字段
可空列使用 Go 指针类型。绝不要使用 database/sql 类型(sql.NullString、sql.NullInt32 等)— 此代码库不使用这些类型。
| SQL 可空类型 | Go 类型 |
|---|---|
TEXT / VARCHAR 可空 | *string |
BIGINT 可空(值) | *int64 |
BIGINT 可空(外键) | *uint64 |
BOOLEAN 可空 | *bool |
TIMESTAMPTZ 可空 | *time.Time |
此代码库中的示例:
Description *string // nullable TEXT
PromotionalPrice *int64 // nullable BIGINT value
CategoryID *uint64 // nullable BIGINT FK
SceneContext *string // nullable TEXT
列标签
不要添加 gorm:"column:..." 标签。GORM 会自动将 Go 字段名映射为 snake_case 列名(ProductID → product_id、CreatedAt → created_at 等)。只有在需要声明特定 GORM 功能时才添加标签。
仅在以下情况添加标签:
gorm:"primarykey"— 主键gorm:"uniqueIndex"/gorm:"uniqueIndex:name"— 唯一约束gorm:"index"— 普通索引gorm:"type:jsonb"— PostgreSQL JSONB 列gorm:"default:'value'"— 列级默认值gorm:"->;column:name"— 计算得出的只读虚拟列
索引和约束标签
在字段上内联声明索引:
Slug string `gorm:"uniqueIndex"`
ProductID uint64 `gorm:"index"`
// Named composite unique index (both fields must share the same name)
CollectionID uint64 `gorm:"uniqueIndex:idx_collection_product"`
ProductID uint64 `gorm:"uniqueIndex:idx_collection_product"`
PostgreSQL 特定类型
JSONB→ 使用[]byte和gorm:"type:jsonb"default值 → 使用gorm:"default:'value'"
Attributes []byte `gorm:"type:jsonb"`
PrimaryColor string `gorm:"default:'#1d4ed8'"`
值类型与外键类型
BIGINT 并非总是 uint64。请根据语义区分:
- 主键:
uint64,带gorm:"primarykey" - NOT NULL 外键:
uint64 - 可空外键:
*uint64 - 数值(价格、重量、尺寸、数量):
int64/*int64 - 小整数(排序顺序、显示顺序):
int - 布尔值:
bool
生成步骤
- 确定模块和实体。
- 阅读迁移 SQL 文件,确认准确的表名和列定义。
- 创建
internal/modules/<module>/model/<entity>_model.go。 - 将每个 SQL 列映射为正确的 Go 字段名和类型。
- 对可空列使用指针类型。
- 仅在需要时添加 GORM 标签(索引、uniqueIndex、类型、默认值)。
- 为结构体和
TableName()添加文档注释。 - 验证结构体字段顺序与迁移中的列顺序一致(便于阅读)。
类型映射指南
| SQL 类型 | NOT NULL | 可空 |
|---|---|---|
BIGSERIAL PRIMARY KEY | uint64 + gorm:"primarykey" | — |
BIGINT 外键 | uint64 | *uint64 |
BIGINT 值 | int64 | *int64 |
INT | int | *int |
VARCHAR / TEXT | string | *string |
BOOLEAN | bool | *bool |
TIMESTAMPTZ | time.Time | *time.Time |
JSONB | []byte + gorm:"type:jsonb" | — |
示例:简单模型
package model
import "time"
// CategoryModel represents a category in the database.
type CategoryModel struct {
ID uint64 `gorm:"primarykey"`
Name string `gorm:"uniqueIndex"`
Slug string `gorm:"uniqueIndex"`
ShowInMenu bool
CreatedAt time.Time
UpdatedAt time.Time
}
// TableName returns the table name for the category model.
func (*CategoryModel) TableName() string {
return "categories"
}
示例:包含可空字段和 JSONB 的模型
package model
import "time"
// ProductModel represents a product in the database.
type ProductModel struct {
ID uint64 `gorm:"primarykey"`
Title string
Description string
Price int64
PromotionalPrice *int64
SKU string `gorm:"uniqueIndex"`
Status string
WeightGrams *int64
WidthCm *int64
LengthCm *int64
HeightCm *int64
StockQuantity int64
CategoryID *uint64
Attributes []byte `gorm:"type:jsonb"`
CreatedAt time.Time
UpdatedAt time.Time
}
// TableName returns the table name for the product model.
func (*ProductModel) TableName() string {
return "products"
}
示例:关联表(复合唯一索引)
package model
import "time"
// CollectionProductModel represents the many-to-many relationship
// between collections and products.
type CollectionProductModel struct {
ID uint64 `gorm:"primarykey"`
CollectionID uint64 `gorm:"uniqueIndex:idx_collection_product"`
ProductID uint64 `gorm:"uniqueIndex:idx_collection_product"`
CreatedAt time.Time
}
// TableName returns the table name for the collection product model.
func (*CollectionProductModel) TableName() string {
return "collection_products"
}
示例:包含默认值的模型
package model
import "time"
// ProfileModel represents the business profile in the database.
type ProfileModel struct {
ID uint64 `gorm:"primarykey"`
BusinessName string
WhatsAppPhone string
LogoPath string
LogoMimeType string
PrimaryColor string `gorm:"default:'#1d4ed8'"`
AccentColor string `gorm:"default:'#16a34a'"`
CreatedAt time.Time
UpdatedAt time.Time
}
// TableName returns the table name for the profile model.
func (*ProfileModel) TableName() string {
return "profiles"
}
关键规则
- 不要定义独立函数:当文件包含带方法的结构体时,不要添加独立函数。请改用结构体上的私有方法。
- 模型仅用于持久化,业务逻辑应放在用例中。
- 不要在 HTTP DTO 响应中直接暴露 GORM 模型。
- 字段名称和类型必须与 SQL 迁移保持一致。
- 对可为空的列使用 Go 指针类型(
*string、*int64、*uint64),绝不要使用database/sql类型。 - 除非 GORM 约定无法处理字段映射,否则不要添加
gorm:"column:..."标签。 - GORM 模型上绝不要使用
json标签。 - 仅使用模块本地的模型包(
internal/modules/<module>/model)。 - 始终为结构体和
TableName()方法添加文档注释。 - 除非同时更新对应的迁移,否则不要更改现有列名或表名。
检查清单
- 文件位于
internal/modules/<module>/model/<entity>_model.go - 结构体命名为
<Entity>Model,并带有文档注释 -
ID uint64带有gorm:"primarykey" - 可为空的列使用指针类型(
*string、*int64、*uint64) - 不导入
database/sql,也不使用 SQL 可空类型 - 除非必要,否则不使用显式的
gorm:"column:..."标签 - 在迁移中定义了索引或约束的地方添加相应标签(
uniqueIndex、index) - PostgreSQL 专用列添加相应标签(
type:jsonb、default:...) -
TableName()带有文档注释,并返回完全匹配的 SQL 表名 - 不使用
json标签
评论
支持 Markdown