// 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, DelFlag: 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, " ") }