Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
82 lines
2.6 KiB
Go
82 lines
2.6 KiB
Go
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
|
||
}
|