在 fast-forward 到 origin/main 后解决 music logic/repo 冲突,SQL 与模型统一 t_* 表名;Makefile 修正 golangci-lint 变量并令 test/deploy 依赖 lint;补齐 admin/transport 等 lint 修复与 music logic 单测以满足覆盖率门禁。 Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
2.4 KiB
Go
86 lines
2.4 KiB
Go
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
|
||
}
|