250 lines
6.0 KiB
Go
250 lines
6.0 KiB
Go
// Package service 业务逻辑层
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/common"
|
|
"server/config"
|
|
systemEntity "server/modules/system/entity"
|
|
"server/modules/user/dto"
|
|
"server/modules/user/entity"
|
|
"server/modules/user/mapper"
|
|
"server/modules/user/vo"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/datatypes"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type UserService struct {
|
|
mapper *mapper.UserMapper
|
|
platformUserMapper *mapper.PlatformUserMapper
|
|
}
|
|
|
|
func NewUserService() *UserService {
|
|
return &UserService{
|
|
mapper: mapper.NewUserMapper(),
|
|
platformUserMapper: mapper.NewPlatformUserMapper(),
|
|
}
|
|
}
|
|
|
|
// List 获取用户列表
|
|
func (s *UserService) List(page, size int) ([]entity.User, int64, error) {
|
|
return s.mapper.FindAll(page, size)
|
|
}
|
|
|
|
// GetByID 获取用户详情
|
|
func (s *UserService) GetByID(id int64) (*entity.User, error) {
|
|
return s.mapper.FindByID(id)
|
|
}
|
|
|
|
// Create 创建用户
|
|
func (s *UserService) Create(req *dto.CreateUserRequest) (*entity.User, error) {
|
|
phone := normalizeOptionalString(req.Phone)
|
|
user := &entity.User{
|
|
Username: req.Username,
|
|
Nickname: req.Nickname,
|
|
AvatarURL: req.AvatarURL,
|
|
Phone: phone,
|
|
Gender: 0,
|
|
Status: 1,
|
|
Deleted: 0,
|
|
}
|
|
if req.Gender != nil {
|
|
user.Gender = *req.Gender
|
|
}
|
|
if req.Status != nil {
|
|
user.Status = *req.Status
|
|
}
|
|
if err := s.mapper.Create(user); err != nil {
|
|
return nil, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
// Update 更新用户
|
|
func (s *UserService) Update(id int64, req *dto.UpdateUserRequest) (*entity.User, error) {
|
|
fields := make(map[string]interface{})
|
|
if req.Username != nil {
|
|
fields["username"] = *req.Username
|
|
}
|
|
if req.Nickname != nil {
|
|
fields["nickname"] = *req.Nickname
|
|
}
|
|
if req.AvatarURL != nil {
|
|
fields["avatar_url"] = *req.AvatarURL
|
|
}
|
|
if req.Phone != nil {
|
|
fields["phone"] = normalizeOptionalString(req.Phone)
|
|
}
|
|
if req.Gender != nil {
|
|
fields["gender"] = *req.Gender
|
|
}
|
|
if req.Status != nil {
|
|
fields["status"] = *req.Status
|
|
}
|
|
if len(fields) == 0 {
|
|
return s.mapper.FindByID(id)
|
|
}
|
|
fields["update_time"] = time.Now()
|
|
if err := s.mapper.UpdateFields(id, fields); err != nil {
|
|
return nil, err
|
|
}
|
|
return s.mapper.FindByID(id)
|
|
}
|
|
|
|
// UpdateFields 动态字段更新
|
|
func (s *UserService) UpdateFields(id int64, fields map[string]interface{}) error {
|
|
if len(fields) == 0 {
|
|
return nil
|
|
}
|
|
fields["update_time"] = time.Now()
|
|
return s.mapper.UpdateFields(id, fields)
|
|
}
|
|
|
|
// Delete 删除用户
|
|
func (s *UserService) Delete(id int64) error {
|
|
return s.mapper.Delete(id)
|
|
}
|
|
|
|
func normalizeOptionalString(val *string) *string {
|
|
if val == nil {
|
|
return nil
|
|
}
|
|
trimmed := strings.TrimSpace(*val)
|
|
if trimmed == "" {
|
|
return nil
|
|
}
|
|
return &trimmed
|
|
}
|
|
|
|
// LoginByPhonePassword 手机号密码登录
|
|
func (s *UserService) LoginByPhonePassword(phone, password string) (*systemEntity.LoginUser, string, error) {
|
|
phone = strings.TrimSpace(phone)
|
|
if phone == "" || strings.TrimSpace(password) == "" {
|
|
return nil, "", errors.New("手机号或密码不能为空")
|
|
}
|
|
|
|
user, err := s.mapper.FindByPhone(phone)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, "", errors.New("用户不存在")
|
|
}
|
|
return nil, "", err
|
|
}
|
|
if user.Status == 0 {
|
|
return nil, "", errors.New("账号已被禁用")
|
|
}
|
|
if user.Password == nil || strings.TrimSpace(*user.Password) == "" || user.Salt == nil || strings.TrimSpace(*user.Salt) == "" {
|
|
return nil, "", errors.New("用户未设置密码")
|
|
}
|
|
|
|
encrypted, err := common.Encrypt(phone, password, *user.Salt)
|
|
if err != nil {
|
|
return nil, "", errors.New("密码校验失败")
|
|
}
|
|
if encrypted != *user.Password {
|
|
return nil, "", errors.New("手机号或密码错误")
|
|
}
|
|
|
|
token := uuid.NewString()
|
|
loginUser := &systemEntity.LoginUser{
|
|
ID: strconv.FormatInt(user.ID, 10),
|
|
Username: chooseUsernameForLogin(user.Username, phone),
|
|
Realname: user.Nickname,
|
|
Avatar: user.AvatarURL,
|
|
Phone: phone,
|
|
Email: "",
|
|
Token: token,
|
|
}
|
|
if err := s.saveLoginUser(token, loginUser); err != nil {
|
|
return nil, "", errors.New("登录失败,请重试")
|
|
}
|
|
return loginUser, token, nil
|
|
}
|
|
|
|
func chooseUsernameForLogin(username, phone string) string {
|
|
if strings.TrimSpace(username) != "" {
|
|
return username
|
|
}
|
|
return phone
|
|
}
|
|
|
|
func (s *UserService) saveLoginUser(token string, user *systemEntity.LoginUser) error {
|
|
ctx := context.Background()
|
|
data, err := json.Marshal(user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return config.RDB.Set(ctx, common.RedisTokenPrefix+token, data, common.RedisTokenExpire).Err()
|
|
}
|
|
|
|
// GetProfile 获取登录用户信息
|
|
func (s *UserService) GetProfile(userID int64, platformType int8) (*vo.UserProfileVO, error) {
|
|
user, err := s.mapper.FindByID(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var platform *entity.PlatformUser
|
|
platform, err = s.platformUserMapper.FindByUserIDAndPlatformType(userID, platformType)
|
|
if err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
platform = nil
|
|
}
|
|
|
|
profile := &vo.UserProfileVO{
|
|
UserID: user.ID,
|
|
Username: user.Username,
|
|
Nickname: user.Nickname,
|
|
AvatarURL: user.AvatarURL,
|
|
Phone: normalizeValue(user.Phone),
|
|
Gender: user.Gender,
|
|
}
|
|
|
|
if platform != nil {
|
|
profile.PlatformType = platform.PlatformType
|
|
profile.PlatformOpenID = platform.PlatformOpenID
|
|
profile.PlatformUnionID = platform.PlatformUnionID
|
|
profile.PlatformExtra = platform.PlatformExtra
|
|
profile.Region = buildRegion(platform.PlatformExtra)
|
|
}
|
|
|
|
return profile, nil
|
|
}
|
|
|
|
func normalizeValue(val *string) string {
|
|
if val == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(*val)
|
|
}
|
|
|
|
func buildRegion(extra datatypes.JSONMap) string {
|
|
if extra == nil {
|
|
return ""
|
|
}
|
|
if region, ok := extra["region"].(string); ok && strings.TrimSpace(region) != "" {
|
|
return strings.TrimSpace(region)
|
|
}
|
|
province, _ := extra["province"].(string)
|
|
city, _ := extra["city"].(string)
|
|
country, _ := extra["country"].(string)
|
|
|
|
parts := []string{}
|
|
for _, item := range []string{country, province, city} {
|
|
if strings.TrimSpace(item) != "" {
|
|
parts = append(parts, strings.TrimSpace(item))
|
|
}
|
|
}
|
|
return strings.Join(parts, " ")
|
|
}
|