package model import ( "database/sql/driver" "encoding/json" "fmt" "time" ) // JSON 类型,用于 custom_fields type JSON map[string]interface{} func (j JSON) Value() (driver.Value, error) { if j == nil { return nil, nil } b, err := json.Marshal(j) return string(b), err } func (j *JSON) Scan(value interface{}) error { if value == nil { *j = nil return nil } var bytes []byte switch v := value.(type) { case string: bytes = []byte(v) case []byte: bytes = v default: return fmt.Errorf("cannot scan type %T into JSON", value) } return json.Unmarshal(bytes, j) } // Date 仅含日期(YYYY-MM-DD)的类型,兼容前端只传日期字符串的场景 type Date struct{ time.Time } const dateFmt = "2006-01-02" func (d *Date) UnmarshalJSON(b []byte) error { s := string(b) // 去掉引号 if len(s) >= 2 && s[0] == '"' { s = s[1 : len(s)-1] } if s == "" || s == "null" { d.Time = time.Time{} return nil } // 尝试纯日期格式 YYYY-MM-DD if t, err := time.ParseInLocation(dateFmt, s, time.Local); err == nil { d.Time = t return nil } // 回退:标准 RFC3339 t, err := time.Parse(time.RFC3339, s) if err != nil { return fmt.Errorf("cannot parse date %q", s) } d.Time = t return nil } func (d Date) MarshalJSON() ([]byte, error) { if d.Time.IsZero() { return []byte(`""`), nil } return []byte(`"` + d.Time.Format(dateFmt) + `"`), nil } // Value / Scan 让 GORM 正确读写 MySQL DATE 列 func (d Date) Value() (driver.Value, error) { if d.Time.IsZero() { return nil, nil } return d.Time.Format(dateFmt), nil } func (d *Date) Scan(value interface{}) error { if value == nil { d.Time = time.Time{} return nil } switch v := value.(type) { case time.Time: d.Time = v case []byte: t, err := time.ParseInLocation(dateFmt, string(v), time.Local) if err != nil { return err } d.Time = t case string: t, err := time.ParseInLocation(dateFmt, v, time.Local) if err != nil { return err } d.Time = t default: return fmt.Errorf("cannot scan type %T into Date", value) } return nil } // Base 公共字段 type Base struct { ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt *time.Time `gorm:"index" json:"-"` } // TenantBase 含租户隔离的公共字段 type TenantBase struct { Base ShopID uint64 `gorm:"not null;index" json:"shop_id"` }