spark-server/internal/module/user/logic/service.go

66 lines
2.5 KiB
Go

// Package logic is the user application use-case layer, split by function (profile, login, email code).
package logic
import (
"context"
"time"
smsentity "spark-server/internal/module/sms/entity"
"spark-server/internal/module/user/entity"
)
// Repository is user persistence. Model implements it; do not split this by table.
type Repository interface {
GetByID(ctx context.Context, id int64) (*entity.User, error)
GetByEmail(ctx context.Context, email string) (*entity.User, error)
GetBySparkID(ctx context.Context, sparkID string) (*entity.User, error)
GetByQRCode(ctx context.Context, qrcode string) (*entity.User, error)
CreateWithEmail(ctx context.Context, u *entity.User, email string) error
UpdateSparkID(ctx context.Context, userID int64, sparkID string) error
UpdateQRCode(ctx context.Context, userID int64, qrcode string) error
UpdateProfile(ctx context.Context, u *entity.User) error
TouchLogin(ctx context.Context, userID int64, at time.Time) error
UpsertDevice(ctx context.Context, d *entity.UserDeviceLink) error
GetDevice(ctx context.Context, userID int64, deviceID uint64) (*entity.UserDeviceLink, error)
TouchDeviceActive(ctx context.Context, userID int64, deviceID uint64, at time.Time) error
InsertLoginLog(ctx context.Context, log *entity.LoginLog) error
GetEmail(ctx context.Context, userID int64) (string, error)
GetDeleted(ctx context.Context, id int64) (*entity.Deleted, error)
ArchiveAndDelete(ctx context.Context, d *entity.Deleted) error
}
// IDGen issues time-ordered Snowflake IDs (user PK).
type IDGen interface {
NextID() (int64, error)
}
// Objects stores the generated default avatar. Nil or Put error keeps an SVG data-URI fallback.
type Objects interface {
Put(ctx context.Context, key string, body []byte, contentType string) error
}
// OTP checks and clears verification codes issued by the sms module.
type OTP interface {
CheckAndConsume(ctx context.Context, ch smsentity.Channel, purpose smsentity.Purpose, target, code string) error
ClearTarget(ctx context.Context, ch smsentity.Channel, target string)
}
// Service is the user application use-case (HTTP and TCP share it).
type Service struct {
repo Repository
otp OTP
ids IDGen
objects Objects
tokens TokenRevoker
sessions SessionDropper
cleaners []UserDataCleaner
}
func NewService(repo Repository, otp OTP, ids IDGen, objects Objects) *Service {
return &Service{repo: repo, otp: otp, ids: ids, objects: objects}
}
func (s *Service) ready() bool {
return s != nil && s.repo != nil && s.ids != nil
}