feat: product.
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user