接口不能定义公共字段,因为go语言规定接口只能声明方法而不能包含字段或结构体标签;复用code、reason等字段必须通过结构体嵌入实现字段提升和标签继承。

直接用结构体嵌入(embedding)是 GORM 中复用公共字段的唯一可行方式,接口不能定义字段,也不能带 json 或 gorm 标签。
为什么不能用接口定义公共字段?
Go 语言不允许接口包含字段或结构体标签——以下写法编译直接报错:
type APIResult interface {
Code string `json:"code"` // ❌ 编译错误:interface cannot contain fields
Reason string `json:"reason"`
}
接口只描述行为(方法),不承载数据。想复用 Code、Reason 这类字段,必须靠结构体嵌入实现字段提升(field promotion)和标签继承。
匿名嵌入 vs gorm:"embedded" 标签
两者都把子结构体字段“摊平”进主表,但语义和控制粒度不同:
- 匿名嵌入(如
APIResult):字段自动提升,可直接访问obj.Code;GORM 默认按字段名映射,不加前缀 -
gorm:"embedded":显式声明嵌入意图,配合embeddedPrefix控制列名前缀,避免冲突 - 若两个嵌入结构体都有
Name字段,不加前缀会导致 GORM 映射失败或字段覆盖 - 推荐对业务逻辑强耦合的公共结构(如审计字段)用匿名嵌入;对可能重名或需隔离来源的结构(如
Address、Contact)用gorm:"embedded;embeddedPrefix:addr_"
gorm.Model 嵌入的隐含行为与陷阱
嵌入 gorm.Model 确实省事,但它自带四字段:ID、CreatedAt、UpdatedAt、DeletedAt,且默认启用软删除:
-
DeletedAt是*time.Time类型,调用Delete()实际执行 UPDATE,不是物理删除 - 若数据库字段名不是
created_at而是create_time,必须手动覆盖:CreatedAt time.Time `gorm:"column:create_time"` - 主键非
ID(比如user_id)时,gorm.Model的ID uint会冲突,此时应放弃嵌入,改用自定义主键字段 +gorm:"primaryKey" -
gorm.Model不处理零值问题:Age int为 0 时不会写入数据库,需改用*int或sql.NullInt64
嵌入字段名冲突的实际修复路径
当两个嵌入结构体含同名字段(如都含 Name),GORM 无法自动区分,必须人工干预:
- 加前缀是最稳妥方案:
Profile Profile `gorm:"embedded;embeddedPrefix:profile_"`→ 生成列profile_name - 若已上线表结构不可改,可用
column标签单独指定每个冲突字段:Name string `gorm:"column:user_name"` - 禁止依赖 GORM 自动推导:字段名大小写、下划线规则(如
user_name→UserName)仅单向生效,反向不成立 - 运行
db.Migrator().CreateTable(&YourModel{})前务必检查生成的 SQL,确认列名符合预期
嵌入本身不难,难的是字段命名空间管理——一旦多个模块共用同一套嵌入结构,embeddedPrefix 就不是可选项,而是必选项。











