mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 14:06:36 +08:00
211 lines
5.3 KiB
Go
211 lines
5.3 KiB
Go
// Package user provides user profiles, credentials, role management, and access token domain services.
|
|
package user
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/Rain-kl/Wavelet/core/contracts"
|
|
"github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
|
"github.com/Rain-kl/Wavelet/internal/infra/persistence/idgen"
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
"github.com/Rain-kl/Wavelet/internal/repository"
|
|
pkgu "github.com/Rain-kl/Wavelet/pkg/util"
|
|
)
|
|
|
|
func toUserDTO(u *model.User) *contracts.UserDTO {
|
|
if u == nil {
|
|
return nil
|
|
}
|
|
return &contracts.UserDTO{
|
|
ID: u.ID,
|
|
Username: u.Username,
|
|
Nickname: u.Nickname,
|
|
Email: u.Email,
|
|
AvatarURL: u.AvatarURL,
|
|
IsActive: u.IsActive,
|
|
IsAdmin: u.IsAdmin,
|
|
Bio: u.Bio,
|
|
Phone: u.Phone,
|
|
Gender: u.Gender,
|
|
Website: u.Website,
|
|
Location: u.Location,
|
|
LastLoginAt: u.LastLoginAt,
|
|
CreatedAt: u.CreatedAt,
|
|
UpdatedAt: u.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
type userServiceImpl struct{}
|
|
|
|
func newUserService() contracts.UserService {
|
|
return &userServiceImpl{}
|
|
}
|
|
|
|
func (s *userServiceImpl) GetUserByID(ctx context.Context, id uint64) (*contracts.UserDTO, error) {
|
|
u, err := repository.GetUserByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return toUserDTO(&u), nil
|
|
}
|
|
|
|
func (s *userServiceImpl) GetUserByUsername(ctx context.Context, username string) (*contracts.UserDTO, error) {
|
|
u, err := repository.GetUserByUsername(ctx, username)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return toUserDTO(&u), nil
|
|
}
|
|
|
|
func (s *userServiceImpl) GetUserByEmail(ctx context.Context, email string) (*contracts.UserDTO, error) {
|
|
var u model.User
|
|
if err := db.DB(ctx).Where("email = ?", email).First(&u).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return toUserDTO(&u), nil
|
|
}
|
|
|
|
func (s *userServiceImpl) CreateUser(ctx context.Context, req contracts.CreateUserRequest) (*contracts.UserDTO, error) {
|
|
if req.Username == "" {
|
|
return nil, errors.New("user: username cannot be empty")
|
|
}
|
|
|
|
user := model.User{
|
|
ID: idgen.NextUint64ID(),
|
|
Username: req.Username,
|
|
Nickname: req.Nickname,
|
|
Email: req.Email,
|
|
IsActive: true,
|
|
IsAdmin: req.IsAdmin,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
LastLoginAt: time.Now(),
|
|
}
|
|
|
|
if user.Nickname == "" {
|
|
user.Nickname = req.Username
|
|
}
|
|
|
|
if req.Password != "" {
|
|
if err := user.SetEncryptedPassword(req.Password); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := repository.CreateUser(ctx, &user); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toUserDTO(&user), nil
|
|
}
|
|
|
|
func (s *userServiceImpl) UpdateProfile(ctx context.Context, id uint64, req contracts.UpdateUserProfileRequest) (*contracts.UserDTO, error) {
|
|
updates := make(map[string]any)
|
|
if req.Nickname != nil {
|
|
updates["nickname"] = *req.Nickname
|
|
}
|
|
if req.Email != nil {
|
|
updates["email"] = *req.Email
|
|
}
|
|
if req.AvatarURL != nil {
|
|
updates["avatar_url"] = *req.AvatarURL
|
|
}
|
|
if req.Bio != nil {
|
|
updates["bio"] = *req.Bio
|
|
}
|
|
if req.Phone != nil {
|
|
updates["phone"] = *req.Phone
|
|
}
|
|
if req.Gender != nil {
|
|
updates["gender"] = *req.Gender
|
|
}
|
|
if req.Website != nil {
|
|
updates["website"] = *req.Website
|
|
}
|
|
if req.Location != nil {
|
|
updates["location"] = *req.Location
|
|
}
|
|
updates["updated_at"] = time.Now()
|
|
|
|
if err := db.DB(ctx).Model(&model.User{}).Where("id = ?", id).Updates(updates).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s.GetUserByID(ctx, id)
|
|
}
|
|
|
|
func (s *userServiceImpl) UpdatePassword(ctx context.Context, id uint64, oldPassword, newPassword string) error {
|
|
var user model.User
|
|
if err := db.DB(ctx).Where("id = ?", id).First(&user).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if !user.CheckPassword(oldPassword) {
|
|
return errors.New("user: incorrect old password")
|
|
}
|
|
|
|
if err := user.SetEncryptedPassword(newPassword); err != nil {
|
|
return err
|
|
}
|
|
|
|
return db.DB(ctx).Model(&model.User{}).Where("id = ?", id).
|
|
Updates(map[string]any{
|
|
"password": user.Password,
|
|
"updated_at": time.Now(),
|
|
}).Error
|
|
}
|
|
|
|
func (s *userServiceImpl) VerifyPassword(ctx context.Context, id uint64, password string) bool {
|
|
var user model.User
|
|
if err := db.DB(ctx).Where("id = ?", id).First(&user).Error; err != nil {
|
|
pkgu.DummyCheckPassword(password)
|
|
return false
|
|
}
|
|
return user.CheckPassword(password)
|
|
}
|
|
|
|
func (s *userServiceImpl) UpdateLastLogin(ctx context.Context, id uint64, _ string) error {
|
|
return db.DB(ctx).Model(&model.User{}).Where("id = ?", id).
|
|
Updates(map[string]any{
|
|
"last_login_at": time.Now(),
|
|
"updated_at": time.Now(),
|
|
}).Error
|
|
}
|
|
|
|
func (s *userServiceImpl) ListUsers(ctx context.Context, page, pageSize int, keyword string) ([]*contracts.UserDTO, int64, error) {
|
|
if page <= 0 {
|
|
page = 1
|
|
}
|
|
if pageSize <= 0 {
|
|
pageSize = 20
|
|
}
|
|
|
|
filter := repository.AdminUserListFilter{
|
|
Username: keyword,
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
}
|
|
|
|
total, users, err := repository.ListAdminUsers(ctx, filter)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
dtos := make([]*contracts.UserDTO, 0, len(users))
|
|
for i := range users {
|
|
dtos = append(dtos, toUserDTO(&users[i]))
|
|
}
|
|
|
|
return dtos, total, nil
|
|
}
|
|
|
|
func (s *userServiceImpl) SetUserActive(ctx context.Context, id uint64, active bool) error {
|
|
return repository.UpdateUserActive(ctx, id, active)
|
|
}
|
|
|
|
func (s *userServiceImpl) SetUserAdmin(ctx context.Context, id uint64, admin bool) error {
|
|
return db.DB(ctx).Model(&model.User{}).Where("id = ?", id).Update("is_admin", admin).Error
|
|
}
|