feat(backend): 新增商品属性字典表(产地/保质期/储存方式/描述文档)

- model/product_attr.go: 新增 4 张字典表 struct(TenantBase 范式)
- model/product.go: Product 增加 4 个可空外键 + belongs-to 关联
- handler/product_attr.go: 4 组 16 个 CRUD handler(shop_id 隔离)
- handler/product.go: FindOrCreate/Update 支持写入 4 个属性 ID
- handler/public.go: Preload 4 表 + 三级回退介绍 + 默认话术常量
- router/router.go: /product-options/{origins,shelf-lives,storages,description-docs} 路由
- main.go: AutoMigrate 注册 4 个新 model
- testutil/setup.go: SQLite 测试库补齐新列与新表

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-08 06:56:16 +08:00
parent 0e2a4086e1
commit ba127826b2
8 changed files with 543 additions and 44 deletions
+16 -6
View File
@@ -22,11 +22,21 @@ type Product struct {
SalePrice float64 `gorm:"type:decimal(12,2)" json:"sale_price"`
MinStock int `gorm:"default:0" json:"min_stock"`
Description string `gorm:"type:text" json:"description"`
CustomFields JSON `gorm:"type:json" json:"custom_fields,omitempty"`
Remark string `gorm:"size:500" json:"remark"`
NamePinyin string `gorm:"size:400;index" json:"-"`
NameInitials string `gorm:"size:100;index" json:"-"`
CustomFields JSON `gorm:"type:json" json:"custom_fields,omitempty"`
Remark string `gorm:"size:500" json:"remark"`
NamePinyin string `gorm:"size:400;index" json:"-"`
NameInitials string `gorm:"size:100;index" json:"-"`
Category *ProductCategory `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
Images []ProductImage `gorm:"foreignKey:ProductID" json:"images,omitempty"`
// 商品属性字典外键(可空,公开页展示用)
OriginID *uint64 `json:"origin_id"`
ShelfLifeID *uint64 `json:"shelf_life_id"`
StorageID *uint64 `json:"storage_id"`
DescriptionDocID *uint64 `json:"description_doc_id"`
Category *ProductCategory `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
Images []ProductImage `gorm:"foreignKey:ProductID" json:"images,omitempty"`
Origin *ProductOriginOption `gorm:"foreignKey:OriginID" json:"origin,omitempty"`
ShelfLife *ProductShelfLifeOption `gorm:"foreignKey:ShelfLifeID" json:"shelf_life,omitempty"`
Storage *ProductStorageOption `gorm:"foreignKey:StorageID" json:"storage,omitempty"`
DescriptionDoc *ProductDescriptionDoc `gorm:"foreignKey:DescriptionDocID" json:"description_doc,omitempty"`
}
+34
View File
@@ -0,0 +1,34 @@
package model
// ProductOriginOption 产地字典(门店级)
type ProductOriginOption struct {
TenantBase
Code string `gorm:"size:50" json:"code"`
Name string `gorm:"size:200;not null" json:"name"`
Remark string `gorm:"size:500" json:"remark"`
}
// ProductShelfLifeOption 保质期字典(门店级)
type ProductShelfLifeOption struct {
TenantBase
Code string `gorm:"size:50" json:"code"`
Name string `gorm:"size:200;not null" json:"name"` // 如「无限期(适饮)」「24个月」
Remark string `gorm:"size:500" json:"remark"`
}
// ProductStorageOption 储存方式字典(门店级)
type ProductStorageOption struct {
TenantBase
Code string `gorm:"size:50" json:"code"`
Name string `gorm:"size:200;not null" json:"name"` // 如「阴凉干燥、避光保存」
Remark string `gorm:"size:500" json:"remark"`
}
// ProductDescriptionDoc 商品描述文档字典(门店级)
// Title = 酒类型(如「酱香型白酒」),Content = 商品介绍正文
type ProductDescriptionDoc struct {
TenantBase
Title string `gorm:"size:200;not null" json:"title"`
Content string `gorm:"type:text" json:"content"`
Remark string `gorm:"size:500" json:"remark"`
}