package model import ( "context" "time" "spark-server/internal/module/user/entity" "spark-server/internal/pkg/errs" ) // User maps table users: account row, Snowflake uid, mutable spark_id. type User struct { ID int64 `gorm:"column:id;primaryKey;comment:Snowflake 用户 id(uid),时间有序,非自增"` SparkID string `gorm:"column:spark_id;comment:对外展示的账号,用户可改,全局唯一"` Nickname string `gorm:"column:nickname;comment:昵称"` Avatar string `gorm:"column:avatar;comment:头像对象存储 key 或 URL,不存图片二进制"` QRCode string `gorm:"column:qrcode;comment:个人二维码扫描票据,随机生成,不含 uid 或 spark_id"` Birthday *time.Time `gorm:"column:birthday;type:date;comment:生日,未填为空"` Gender int `gorm:"column:gender;comment:0=未知 1=男 2=女"` Status int `gorm:"column:status;comment:1=正常 0=禁用"` 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:注册时间(create_time)"` UpdatedAt time.Time `gorm:"column:updated_at;comment:资料最近更新时间(update_time)"` } func (User) TableName() string { return "t_users" } func (r *User) entity() *entity.User { if r == nil { return nil } return &entity.User{ ID: r.ID, SparkID: r.SparkID, Nickname: r.Nickname, Avatar: r.Avatar, QRCode: r.QRCode, Birthday: timeOrZero(r.Birthday), Gender: entity.Gender(r.Gender), Status: entity.Status(r.Status), LastLoginAt: timeOrZero(r.LastLoginAt), LastActiveAt: timeOrZero(r.LastActiveAt), CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, } } func fromEntity(u *entity.User) User { row := User{ ID: u.ID, SparkID: u.SparkID, Nickname: u.Nickname, Avatar: u.Avatar, QRCode: u.QRCode, Gender: int(u.Gender), Status: int(u.Status), CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, } row.Birthday = timePtr(u.Birthday) row.LastLoginAt = timePtr(u.LastLoginAt) row.LastActiveAt = timePtr(u.LastActiveAt) return row } func timeOrZero(t *time.Time) time.Time { if t == nil { return time.Time{} } return *t } func timePtr(t time.Time) *time.Time { if t.IsZero() { return nil } return &t } func (m *Repo) GetByID(ctx context.Context, id int64) (*entity.User, error) { db, err := m.session(ctx) if err != nil { return nil, err } var row User if err := db.Where("id = ?", id).Take(&row).Error; err != nil { return nil, mapDBErr(err) } return row.entity(), nil } func (m *Repo) GetBySparkID(ctx context.Context, sparkID string) (*entity.User, error) { db, err := m.session(ctx) if err != nil { return nil, err } var row User if err := db.Where("spark_id = ?", sparkID).Take(&row).Error; err != nil { return nil, mapDBErr(err) } return row.entity(), nil } func (m *Repo) GetByQRCode(ctx context.Context, qrcode string) (*entity.User, error) { db, err := m.session(ctx) if err != nil { return nil, err } var row User if err := db.Where("qrcode = ?", qrcode).Take(&row).Error; err != nil { return nil, mapDBErr(err) } return row.entity(), nil } func (m *Repo) UpdateSparkID(ctx context.Context, userID int64, sparkID string) error { db, err := m.session(ctx) if err != nil { return err } res := db.Model(&User{}).Where("id = ?", userID).Updates(map[string]any{ "spark_id": sparkID, "updated_at": time.Now(), }) if res.Error != nil { return mapDBErr(res.Error) } if res.RowsAffected == 0 { return errs.ErrNotFound } return nil } func (m *Repo) UpdateQRCode(ctx context.Context, userID int64, qrcode string) error { db, err := m.session(ctx) if err != nil { return err } res := db.Model(&User{}).Where("id = ?", userID).Update("qrcode", qrcode) if res.Error != nil { return mapDBErr(res.Error) } if res.RowsAffected == 0 { return errs.ErrNotFound } return nil } func (m *Repo) UpdateProfile(ctx context.Context, u *entity.User) error { if u == nil || u.ID <= 0 { return errs.ErrInvalidArgument } db, err := m.session(ctx) if err != nil { return err } res := db.Model(&User{}).Where("id = ?", u.ID).Select("spark_id", "nickname", "avatar", "gender", "birthday", "updated_at").Updates(map[string]any{ "spark_id": u.SparkID, "nickname": u.Nickname, "avatar": u.Avatar, "gender": int(u.Gender), "birthday": timePtr(u.Birthday), "updated_at": u.UpdatedAt, }) if res.Error != nil { return mapDBErr(res.Error) } if res.RowsAffected == 0 { return errs.ErrNotFound } return nil } func (m *Repo) TouchLogin(ctx context.Context, userID int64, at time.Time) error { db, err := m.session(ctx) if err != nil { return err } res := db.Model(&User{}).Where("id = ?", userID).Updates(map[string]any{ "last_login_at": at, "last_active_at": at, }) if res.Error != nil { return mapDBErr(res.Error) } if res.RowsAffected == 0 { return errs.ErrNotFound } return nil }