package model import ( "context" "time" "gorm.io/gorm/clause" "spark-server/internal/module/user/entity" ) 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:devices.device_id,JWT did"` LastLoginAt time.Time `gorm:"column:last_login_at;comment:该用户在此设备最近登录成功"` LastActiveAt *time.Time `gorm:"column:last_active_at;comment:该用户在此设备最近活跃"` CreatedAt time.Time `gorm:"column:created_at;comment:该用户首次在此设备绑定"` } func (UserDevice) TableName() string { return "t_user_devices" } func (row *UserDevice) entity() *entity.UserDeviceLink { if row == nil { return nil } return &entity.UserDeviceLink{ ID: row.ID, UserID: row.UserID, DeviceID: row.DeviceID, LastLoginAt: row.LastLoginAt, LastActiveAt: row.LastActiveAt, CreatedAt: row.CreatedAt, } } func (m *Repo) UpsertDevice(ctx context.Context, d *entity.UserDeviceLink) error { if d == nil { return nil } db, err := m.session(ctx) if err != nil { return err } row := UserDevice{ UserID: d.UserID, DeviceID: d.DeviceID, LastLoginAt: d.LastLoginAt, LastActiveAt: d.LastActiveAt, CreatedAt: d.CreatedAt, } err = db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "user_id"}, {Name: "device_id"}}, DoUpdates: clause.AssignmentColumns([]string{ "last_login_at", "last_active_at", }), }).Create(&row).Error return mapDBErr(err) } func (m *Repo) GetDevice(ctx context.Context, userID int64, deviceID uint64) (*entity.UserDeviceLink, 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 } func (m *Repo) TouchDeviceActive(ctx context.Context, userID int64, deviceID uint64, at time.Time) error { db, err := m.session(ctx) if err != nil { return err } res := db.Model(&UserDevice{}). Where("user_id = ? AND device_id = ?", userID, deviceID). Update("last_active_at", at) if res.Error != nil { return mapDBErr(res.Error) } return nil }