213 lines
5.0 KiB
Go
213 lines
5.0 KiB
Go
package models
|
||
|
||
import (
|
||
"database/sql/driver"
|
||
"errors"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"go.uber.org/zap/zapcore"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// Model 用于代替gorm.Model, 使用uint32以节约空间
|
||
type Model struct {
|
||
ID uint32 `gorm:"primarykey"`
|
||
CreatedAt time.Time
|
||
UpdatedAt time.Time
|
||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||
}
|
||
|
||
// GetAllModels 返回一个包含所有数据库模型实例的切片。
|
||
// 这个函数用于在数据库初始化时自动迁移所有的表结构。
|
||
func GetAllModels() []interface{} {
|
||
return []interface{}{
|
||
// Core Models
|
||
&User{},
|
||
&UserActionLog{},
|
||
|
||
// Device Models
|
||
&Device{},
|
||
&AreaController{},
|
||
&DeviceTemplate{},
|
||
&SensorData{},
|
||
&DeviceCommandLog{},
|
||
&DeviceTask{},
|
||
|
||
// Plan & Task Models
|
||
&Plan{},
|
||
&SubPlan{},
|
||
&Task{},
|
||
&PlanExecutionLog{},
|
||
&TaskExecutionLog{},
|
||
&PendingTask{},
|
||
&PendingCollection{},
|
||
|
||
// Farm Asset Models
|
||
&PigHouse{},
|
||
&Pen{},
|
||
|
||
// Pig & Batch Models
|
||
&PigBatch{},
|
||
&PigBatchLog{},
|
||
&WeighingBatch{},
|
||
&WeighingRecord{},
|
||
&PigTransferLog{},
|
||
&PigSickLog{},
|
||
|
||
// Pig Buy & Sell
|
||
&PigPurchase{},
|
||
&PigSale{},
|
||
|
||
// Feed Models
|
||
&RawMaterial{},
|
||
&RawMaterialPurchase{},
|
||
&RawMaterialStockLog{},
|
||
&FeedFormula{},
|
||
&FeedFormulaComponent{},
|
||
&FeedUsageRecord{},
|
||
|
||
// Medication Models
|
||
&Medication{},
|
||
&MedicationLog{},
|
||
|
||
// Alarm Models
|
||
&ActiveAlarm{},
|
||
&HistoricalAlarm{},
|
||
|
||
// Notification Models
|
||
&Notification{},
|
||
}
|
||
}
|
||
|
||
// UintArray 是一个自定义类型,代表 uint32 的切片。
|
||
// 它实现了 gorm.Scanner 和 driver.Valuer 接口,
|
||
// 以便能与数据库的 bigint[] 类型进行原生映射。
|
||
type UintArray []uint32
|
||
|
||
// Value 实现了 driver.Valuer 接口。
|
||
// 它告诉 GORM 如何将 UintArray ([]) 转换为数据库能够理解的格式。
|
||
func (a UintArray) Value() (driver.Value, error) {
|
||
if a == nil {
|
||
return "{}", nil
|
||
}
|
||
|
||
var b strings.Builder
|
||
b.WriteString("{")
|
||
for i, v := range a {
|
||
if i > 0 {
|
||
b.WriteString(",")
|
||
}
|
||
b.WriteString(strconv.FormatUint(uint64(v), 10))
|
||
}
|
||
b.WriteString("}")
|
||
return b.String(), nil
|
||
}
|
||
|
||
// Scan 实现了 gorm.Scanner 接口。
|
||
// 它告诉 GORM 如何将从数据库读取的数据转换为我们的 UintArray ([])。
|
||
func (a *UintArray) Scan(src interface{}) error {
|
||
if src == nil {
|
||
*a = nil
|
||
return nil
|
||
}
|
||
|
||
var srcStr string
|
||
switch v := src.(type) {
|
||
case []byte:
|
||
srcStr = string(v)
|
||
case string:
|
||
srcStr = v
|
||
default:
|
||
return errors.New("无法扫描非字符串或字节类型的源到 UintArray")
|
||
}
|
||
|
||
// 去掉花括号
|
||
srcStr = strings.Trim(srcStr, "{}")
|
||
if srcStr == "" {
|
||
*a = []uint32{}
|
||
return nil
|
||
}
|
||
|
||
// 按逗号分割
|
||
parts := strings.Split(srcStr, ",")
|
||
arr := make([]uint32, len(parts))
|
||
for i, p := range parts {
|
||
val, err := strconv.ParseUint(p, 10, 64)
|
||
if err != nil {
|
||
return fmt.Errorf("解析 UintArray 元素失败: %w", err)
|
||
}
|
||
arr[i] = uint32(val)
|
||
}
|
||
|
||
*a = arr
|
||
return nil
|
||
}
|
||
|
||
// SeverityLevel 定义了系统中告警、通知、日志的统一级别枚举。
|
||
// 它以中文形式存储在数据库中,提高了可读性。
|
||
type SeverityLevel string
|
||
|
||
const (
|
||
// DebugLevel 调试级别,用于开发和诊断问题。
|
||
DebugLevel SeverityLevel = "debug"
|
||
// InfoLevel 信息级别,用于记录常规操作。
|
||
InfoLevel SeverityLevel = "info"
|
||
// WarnLevel 警告级别,表示出现潜在问题,需要关注。
|
||
WarnLevel SeverityLevel = "warn"
|
||
// ErrorLevel 错误级别,表示发生了需要处理的错误。
|
||
ErrorLevel SeverityLevel = "error"
|
||
// DPanicLevel 开发时崩溃级别,在开发模式下会触发 panic。
|
||
DPanicLevel SeverityLevel = "dpanic"
|
||
// PanicLevel 崩溃级别,记录日志后会立即触发 panic。
|
||
PanicLevel SeverityLevel = "panic"
|
||
// FatalLevel 致命级别,记录日志后会调用 os.Exit(1) 退出程序。
|
||
FatalLevel SeverityLevel = "fatal"
|
||
)
|
||
|
||
// ToZapLevel 将我们的自定义级别转换为 zapcore.Level,以便与日志记录器兼容。
|
||
func (al SeverityLevel) ToZapLevel() zapcore.Level {
|
||
switch al {
|
||
case DebugLevel:
|
||
return zapcore.DebugLevel
|
||
case InfoLevel:
|
||
return zapcore.InfoLevel
|
||
case WarnLevel:
|
||
return zapcore.WarnLevel
|
||
case ErrorLevel:
|
||
return zapcore.ErrorLevel
|
||
case DPanicLevel:
|
||
return zapcore.DPanicLevel
|
||
case PanicLevel:
|
||
return zapcore.PanicLevel
|
||
case FatalLevel:
|
||
return zapcore.FatalLevel
|
||
default:
|
||
// 默认情况下返回 Info 级别,保证程序健壮性
|
||
return zapcore.InfoLevel
|
||
}
|
||
}
|
||
|
||
// Scan 实现了 sql.Scanner 接口,GORM 在从数据库读取数据时会调用此方法。
|
||
func (al *SeverityLevel) Scan(value interface{}) error {
|
||
bytes, ok := value.([]byte)
|
||
if !ok {
|
||
// 尝试处理其他可能的类型,例如字符串
|
||
s, ok := value.(string)
|
||
if !ok {
|
||
return fmt.Errorf("无法将值 %v (类型 %T) 扫描为 SeverityLevel", value, value)
|
||
}
|
||
*al = SeverityLevel(s)
|
||
return nil
|
||
}
|
||
*al = SeverityLevel(bytes)
|
||
return nil
|
||
}
|
||
|
||
// Value 实现了 driver.Valuer 接口,GORM 在将数据写入数据库时会调用此方法。
|
||
func (al SeverityLevel) Value() (driver.Value, error) {
|
||
return string(al), nil
|
||
}
|