feat: product.

This commit is contained in:
2026-05-01 07:46:48 +08:00
parent 79515007b7
commit 7b43ccf42f
10 changed files with 1070 additions and 1 deletions
+190
View File
@@ -0,0 +1,190 @@
package models
import (
"encoding/json"
"errors"
"strings"
"time"
"gorm.io/gorm"
)
type ProductDefinition struct {
ID uint `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:64;not null;uniqueIndex" json:"code"`
Name string `gorm:"size:255;not null" json:"name"`
Category string `gorm:"size:64;not null;default:'device';index" json:"category"`
Description string `gorm:"size:1024" json:"description"`
ParameterSchema string `gorm:"type:text" json:"parameterSchema"`
TrackSerialNumber bool `gorm:"not null;default:false" json:"trackSerialNumber"`
IsActive bool `gorm:"not null;default:true;index" json:"isActive"`
Sort int `gorm:"not null;default:0;index" json:"sort"`
CreatedAt int64 `gorm:"not null" json:"created_at"`
UpdatedAt int64 `gorm:"not null" json:"updated_at"`
}
func (ProductDefinition) TableName() string {
return "product_definitions"
}
func (p *ProductDefinition) BeforeCreate(tx *gorm.DB) (err error) {
now := time.Now().UnixMilli()
p.Code = normalizeProductCodeValue(p.Code)
p.Name = strings.TrimSpace(p.Name)
p.Category = normalizeProductCategoryValue(p.Category)
p.Description = strings.TrimSpace(p.Description)
p.ParameterSchema = normalizeJSONTextValue(p.ParameterSchema)
p.CreatedAt = now
p.UpdatedAt = now
return nil
}
func (p *ProductDefinition) BeforeUpdate(tx *gorm.DB) (err error) {
p.Code = normalizeProductCodeValue(p.Code)
p.Name = strings.TrimSpace(p.Name)
p.Category = normalizeProductCategoryValue(p.Category)
p.Description = strings.TrimSpace(p.Description)
p.ParameterSchema = normalizeJSONTextValue(p.ParameterSchema)
p.UpdatedAt = time.Now().UnixMilli()
return nil
}
func normalizeProductCodeValue(code string) string {
return strings.TrimSpace(strings.ToLower(code))
}
func normalizeProductCategoryValue(category string) string {
value := strings.TrimSpace(strings.ToLower(category))
if value == "" {
return "device"
}
return value
}
func normalizeJSONTextValue(value string) string {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return ""
}
if !json.Valid([]byte(trimmed)) {
return ""
}
return trimmed
}
type ProductParameterSchemaField struct {
Key string `json:"key"`
Label string `json:"label"`
Type string `json:"type"`
Required bool `json:"required"`
Options []string `json:"options,omitempty"`
}
func mustJSON(value interface{}) string {
bytes, err := json.Marshal(value)
if err != nil {
return "[]"
}
return string(bytes)
}
func EnsureDefaultProductDefinitions(db *gorm.DB) error {
defaults := []ProductDefinition{
{
Code: "charging_case_large",
Name: "充电收纳箱20位",
Category: "container",
Description: "可收纳20个手环的大充电收纳箱",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "capacity", Label: "容量", Type: "number", Required: false}, {Key: "powerAdapter", Label: "适配器型号", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 10,
},
{
Code: "charging_case_small",
Name: "小充电收纳箱10位",
Category: "container",
Description: "可收纳10个手环的小充电收纳箱",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "capacity", Label: "容量", Type: "number", Required: false}, {Key: "powerAdapter", Label: "适配器型号", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 20,
},
{
Code: "collection_band",
Name: "采集手环",
Category: "wearable",
Description: "用于智能心率采集、50米往返跑等项目的采集手环",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "bandSize", Label: "尺码", Type: "string", Required: false}, {Key: "firmwareVersion", Label: "固件版本", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 30,
},
{
Code: "control_tablet",
Name: "控制平板",
Category: "tablet",
Description: "项目控制与操作使用的平板设备",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "screenSize", Label: "屏幕尺寸", Type: "string", Required: false}, {Key: "osVersion", Label: "系统版本", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 40,
},
{
Code: "collection_gateway",
Name: "采集网关",
Category: "gateway",
Description: "用于设备数据采集与上传的网关",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "mac", Label: "MAC地址", Type: "string", Required: true}, {Key: "location", Label: "安装位置", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 50,
},
{
Code: "display_stand_screen",
Name: "闺蜜机",
Category: "display",
Description: "用于投屏展示的移动显示设备",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "screenSize", Label: "屏幕尺寸", Type: "string", Required: false}, {Key: "resolution", Label: "分辨率", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 60,
},
{
Code: "led_light_strip",
Name: "LED灯带",
Category: "accessory",
Description: "心肺耐力测试配套的LED灯带",
ParameterSchema: mustJSON([]ProductParameterSchemaField{{Key: "length", Label: "长度", Type: "string", Required: false}, {Key: "colorMode", Label: "颜色模式", Type: "string", Required: false}}),
TrackSerialNumber: true,
IsActive: true,
Sort: 70,
},
}
for _, item := range defaults {
var existing ProductDefinition
err := db.Where("code = ?", item.Code).First(&existing).Error
switch {
case err == nil:
existing.Name = item.Name
existing.Category = item.Category
existing.Description = item.Description
existing.ParameterSchema = item.ParameterSchema
existing.TrackSerialNumber = item.TrackSerialNumber
existing.IsActive = item.IsActive
existing.Sort = item.Sort
if err := db.Save(&existing).Error; err != nil {
return err
}
case errors.Is(err, gorm.ErrRecordNotFound):
if err := db.Create(&item).Error; err != nil {
return err
}
default:
return err
}
}
return nil
}