package model import ( "context" "time" "gorm.io/gorm/clause" "spark-server/internal/module/user/entity" ) // UserDevice maps table user_devices: devices that have logged in. type UserDevice struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement;comment:行 id,表内自增,仅内部用;业务唯一键是 (user_id, device_id)"` UserID int64 `gorm:"column:user_id;comment:所属用户"` DeviceID uint64 `gorm:"column:device_id;comment:客户端设备 id,写入 JWT did"` DeviceType string `gorm:"column:platform;comment:设备类型:ios/android/harmony/windows/macos/web"` DeviceName string `gorm:"column:device_name;comment:设备展示名"` AppVersion string `gorm:"column:app_version;comment:最近一次登录的客户端版本"` Language string `gorm:"column:language;comment:最近一次登录语言,如 zh-CN"` LastIP string `gorm:"column:last_ip;comment:最近一次登录的 PROXY RealIP,禁止用环回地址"` LastLoginAt time.Time `gorm:"column:last_login_at;comment:最近一次登录时间"` CreatedAt time.Time `gorm:"column:created_at;comment:该设备首次登录时间"` } func (UserDevice) TableName() string { return "user_devices" } func (row *UserDevice) entity() *entity.Device { if row == nil { return nil } return &entity.Device{ ID: row.ID, UserID: row.UserID, DeviceID: row.DeviceID, DeviceType: row.DeviceType, DeviceName: row.DeviceName, AppVersion: row.AppVersion, Language: row.Language, LastIP: row.LastIP, LastLoginAt: row.LastLoginAt, CreatedAt: row.CreatedAt, } } func (m *Repo) UpsertDevice(ctx context.Context, d *entity.Device) error { db, err := m.session(ctx) if err != nil { return err } row := UserDevice{ UserID: d.UserID, DeviceID: d.DeviceID, DeviceType: d.DeviceType, DeviceName: d.DeviceName, AppVersion: d.AppVersion, Language: d.Language, LastIP: d.LastIP, LastLoginAt: d.LastLoginAt, CreatedAt: d.CreatedAt, } err = db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "user_id"}, {Name: "device_id"}}, DoUpdates: clause.AssignmentColumns([]string{ "platform", "device_name", "app_version", "language", "last_ip", "last_login_at", }), }).Create(&row).Error return mapDBErr(err) } func (m *Repo) GetDevice(ctx context.Context, userID int64, deviceID uint64) (*entity.Device, error) { db, err := m.session(ctx) if err != nil { return nil, err } var row UserDevice if err := db.Where("user_id = ? AND device_id = ?", userID, deviceID).Take(&row).Error; err != nil { return nil, mapDBErr(err) } return row.entity(), nil }