package model import ( "context" "errors" "time" mysqldriver "github.com/go-sql-driver/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" devicelogic "spark-server/internal/module/device/logic" "spark-server/internal/pkg/errs" ) type DeviceRow struct { DeviceID uint64 `gorm:"column:device_id;primaryKey;comment:JWT did"` FingerprintHash string `gorm:"column:fingerprint_hash;uniqueIndex:uk_fingerprint;comment:物理指纹"` DeviceType uint8 `gorm:"column:device_type;comment:device_type 枚举"` DeviceName string `gorm:"column:device_name;comment:设备展示名"` IDFV string `gorm:"column:idfv;comment:iOS IDFV"` AndroidID string `gorm:"column:android_id;comment:Android ID"` LastClientIP string `gorm:"column:last_client_ip;comment:最近 RealIP"` CreatedAt time.Time `gorm:"column:created_at;comment:首次注册"` UpdatedAt time.Time `gorm:"column:updated_at;comment:最近 touch"` } func (DeviceRow) TableName() string { return "t_devices" } type RegistryRepo struct { gdb *gorm.DB } func NewRegistryRepo(gdb *gorm.DB) *RegistryRepo { return &RegistryRepo{gdb: gdb} } func (r *RegistryRepo) Upsert(ctx context.Context, row *devicelogic.RegistryRow) error { if r == nil || r.gdb == nil || row == nil { return errs.ErrAvailable } now := time.Now() if row.UpdatedAt.IsZero() { row.UpdatedAt = now } dr := DeviceRow{ DeviceID: row.DeviceID, FingerprintHash: row.FingerprintHash, DeviceType: uint8(row.DeviceType), DeviceName: row.DeviceName, IDFV: row.IDFV, AndroidID: row.AndroidID, LastClientIP: row.LastClientIP, CreatedAt: now, UpdatedAt: row.UpdatedAt, } err := r.gdb.WithContext(ctx).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "fingerprint_hash"}}, DoUpdates: clause.AssignmentColumns([]string{ "device_name", "idfv", "android_id", "last_client_ip", "updated_at", }), }).Create(&dr).Error return mapDBErr(err) } func mapDBErr(err error) error { if err == nil { return nil } if errors.Is(err, gorm.ErrRecordNotFound) { return errs.ErrNotFound } var me *mysqldriver.MySQLError if errors.As(err, &me) && me.Number == 1062 { return errs.ErrDuplicate } return errs.ErrAvailable.WithError("device-db", err) }