2025-07-13 16:36:20 +08:00
|
|
|
package user
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
|
|
"tyapi-server/internal/application/user/dto/commands"
|
|
|
|
|
"tyapi-server/internal/application/user/dto/queries"
|
|
|
|
|
"tyapi-server/internal/application/user/dto/responses"
|
|
|
|
|
"tyapi-server/internal/domains/user/entities"
|
|
|
|
|
"tyapi-server/internal/domains/user/events"
|
|
|
|
|
"tyapi-server/internal/domains/user/repositories"
|
|
|
|
|
user_service "tyapi-server/internal/domains/user/services"
|
|
|
|
|
"tyapi-server/internal/shared/interfaces"
|
|
|
|
|
"tyapi-server/internal/shared/middleware"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// UserApplicationServiceImpl 用户应用服务实现
|
|
|
|
|
type UserApplicationServiceImpl struct {
|
|
|
|
|
userRepo repositories.UserRepository
|
|
|
|
|
enterpriseInfoRepo repositories.EnterpriseInfoRepository
|
|
|
|
|
smsCodeService *user_service.SMSCodeService
|
|
|
|
|
eventBus interfaces.EventBus
|
|
|
|
|
jwtAuth *middleware.JWTAuthMiddleware
|
|
|
|
|
logger *zap.Logger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewUserApplicationService 创建用户应用服务
|
|
|
|
|
func NewUserApplicationService(
|
|
|
|
|
userRepo repositories.UserRepository,
|
|
|
|
|
enterpriseInfoRepo repositories.EnterpriseInfoRepository,
|
|
|
|
|
smsCodeService *user_service.SMSCodeService,
|
|
|
|
|
eventBus interfaces.EventBus,
|
|
|
|
|
jwtAuth *middleware.JWTAuthMiddleware,
|
|
|
|
|
logger *zap.Logger,
|
|
|
|
|
) UserApplicationService {
|
|
|
|
|
return &UserApplicationServiceImpl{
|
|
|
|
|
userRepo: userRepo,
|
|
|
|
|
enterpriseInfoRepo: enterpriseInfoRepo,
|
|
|
|
|
smsCodeService: smsCodeService,
|
|
|
|
|
eventBus: eventBus,
|
|
|
|
|
jwtAuth: jwtAuth,
|
|
|
|
|
logger: logger,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Register 用户注册
|
|
|
|
|
func (s *UserApplicationServiceImpl) Register(ctx context.Context, cmd *commands.RegisterUserCommand) (*responses.RegisterUserResponse, error) {
|
|
|
|
|
if err := s.smsCodeService.VerifyCode(ctx, cmd.Phone, cmd.Code, entities.SMSSceneRegister); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("验证码错误或已过期")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := s.userRepo.GetByPhone(ctx, cmd.Phone); err == nil {
|
|
|
|
|
return nil, fmt.Errorf("手机号已存在")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user, err := entities.NewUser(cmd.Phone, cmd.Password)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("创建用户失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createdUser, err := s.userRepo.Create(ctx, *user)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("创建用户失败", zap.Error(err))
|
|
|
|
|
return nil, fmt.Errorf("创建用户失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event := events.NewUserRegisteredEvent(user, "")
|
|
|
|
|
if err := s.eventBus.Publish(ctx, event); err != nil {
|
|
|
|
|
s.logger.Warn("发布用户注册事件失败", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.logger.Info("用户注册成功", zap.String("user_id", user.ID), zap.String("phone", user.Phone))
|
|
|
|
|
|
|
|
|
|
return &responses.RegisterUserResponse{
|
|
|
|
|
ID: createdUser.ID,
|
|
|
|
|
Phone: user.Phone,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoginWithPassword 密码登录
|
|
|
|
|
func (s *UserApplicationServiceImpl) LoginWithPassword(ctx context.Context, cmd *commands.LoginWithPasswordCommand) (*responses.LoginUserResponse, error) {
|
|
|
|
|
user, err := s.userRepo.GetByPhone(ctx, cmd.Phone)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("用户名或密码错误")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !user.CanLogin() {
|
|
|
|
|
return nil, fmt.Errorf("用户状态异常,无法登录")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !user.CheckPassword(cmd.Password) {
|
|
|
|
|
return nil, fmt.Errorf("用户名或密码错误")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
accessToken, err := s.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("生成令牌失败", zap.Error(err))
|
|
|
|
|
return nil, fmt.Errorf("生成访问令牌失败")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userProfile, err := s.GetUserProfile(ctx, user.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("获取用户信息失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &responses.LoginUserResponse{
|
|
|
|
|
User: userProfile,
|
|
|
|
|
AccessToken: accessToken,
|
|
|
|
|
TokenType: "Bearer",
|
|
|
|
|
ExpiresIn: 86400, // 24h
|
|
|
|
|
LoginMethod: "password",
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoginWithSMS 短信验证码登录
|
|
|
|
|
func (s *UserApplicationServiceImpl) LoginWithSMS(ctx context.Context, cmd *commands.LoginWithSMSCommand) (*responses.LoginUserResponse, error) {
|
|
|
|
|
if err := s.smsCodeService.VerifyCode(ctx, cmd.Phone, cmd.Code, entities.SMSSceneLogin); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("验证码错误或已过期")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user, err := s.userRepo.GetByPhone(ctx, cmd.Phone)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("用户不存在")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !user.CanLogin() {
|
|
|
|
|
return nil, fmt.Errorf("用户状态异常,无法登录")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
accessToken, err := s.jwtAuth.GenerateToken(user.ID, user.Phone, user.Phone)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("生成令牌失败", zap.Error(err))
|
|
|
|
|
return nil, fmt.Errorf("生成访问令牌失败")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userProfile, err := s.GetUserProfile(ctx, user.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("获取用户信息失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &responses.LoginUserResponse{
|
|
|
|
|
User: userProfile,
|
|
|
|
|
AccessToken: accessToken,
|
|
|
|
|
TokenType: "Bearer",
|
|
|
|
|
ExpiresIn: 86400, // 24h
|
|
|
|
|
LoginMethod: "sms",
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ChangePassword 修改密码
|
|
|
|
|
func (s *UserApplicationServiceImpl) ChangePassword(ctx context.Context, cmd *commands.ChangePasswordCommand) error {
|
|
|
|
|
user, err := s.userRepo.GetByID(ctx, cmd.UserID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("用户不存在: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.smsCodeService.VerifyCode(ctx, user.Phone, cmd.Code, entities.SMSSceneChangePassword); err != nil {
|
|
|
|
|
return fmt.Errorf("验证码错误或已过期")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := user.ChangePassword(cmd.OldPassword, cmd.NewPassword, cmd.ConfirmNewPassword); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.userRepo.Update(ctx, user); err != nil {
|
|
|
|
|
return fmt.Errorf("密码更新失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event := events.NewUserPasswordChangedEvent(user.ID, user.Phone, "")
|
|
|
|
|
if err := s.eventBus.Publish(ctx, event); err != nil {
|
|
|
|
|
s.logger.Warn("发布密码修改事件失败", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.logger.Info("密码修改成功", zap.String("user_id", cmd.UserID))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 13:21:34 +08:00
|
|
|
// ResetPassword 重置密码
|
|
|
|
|
func (s *UserApplicationServiceImpl) ResetPassword(ctx context.Context, cmd *commands.ResetPasswordCommand) error {
|
|
|
|
|
user, err := s.userRepo.GetByPhone(ctx, cmd.Phone)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("用户不存在")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.smsCodeService.VerifyCode(ctx, cmd.Phone, cmd.Code, entities.SMSSceneResetPassword); err != nil {
|
|
|
|
|
return fmt.Errorf("验证码错误或已过期")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := user.ResetPassword(cmd.NewPassword, cmd.ConfirmNewPassword); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.userRepo.Update(ctx, *user); err != nil {
|
|
|
|
|
return fmt.Errorf("密码更新失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event := events.NewUserPasswordChangedEvent(user.ID, user.Phone, "")
|
|
|
|
|
if err := s.eventBus.Publish(ctx, event); err != nil {
|
|
|
|
|
s.logger.Warn("发布密码重置事件失败", zap.Error(err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.logger.Info("密码重置成功", zap.String("user_id", user.ID), zap.String("phone", user.Phone))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-13 16:36:20 +08:00
|
|
|
// GetUserProfile 获取用户信息
|
|
|
|
|
func (s *UserApplicationServiceImpl) GetUserProfile(ctx context.Context, userID string) (*responses.UserProfileResponse, error) {
|
|
|
|
|
if userID == "" {
|
|
|
|
|
return nil, fmt.Errorf("用户ID不能为空")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user, err := s.userRepo.GetByID(ctx, userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("用户不存在: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response := &responses.UserProfileResponse{
|
|
|
|
|
ID: user.ID,
|
|
|
|
|
Phone: user.Phone,
|
|
|
|
|
CreatedAt: user.CreatedAt,
|
|
|
|
|
UpdatedAt: user.UpdatedAt,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取企业信息(如果存在)
|
|
|
|
|
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Debug("用户暂无企业信息", zap.String("user_id", userID))
|
2025-07-15 13:21:34 +08:00
|
|
|
response.IsCertified = false
|
2025-07-13 16:36:20 +08:00
|
|
|
} else {
|
|
|
|
|
response.EnterpriseInfo = &responses.EnterpriseInfoResponse{
|
|
|
|
|
ID: enterpriseInfo.ID,
|
|
|
|
|
CompanyName: enterpriseInfo.CompanyName,
|
|
|
|
|
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
|
|
|
|
LegalPersonName: enterpriseInfo.LegalPersonName,
|
|
|
|
|
LegalPersonID: enterpriseInfo.LegalPersonID,
|
|
|
|
|
IsOCRVerified: enterpriseInfo.IsOCRVerified,
|
|
|
|
|
IsFaceVerified: enterpriseInfo.IsFaceVerified,
|
|
|
|
|
IsCertified: enterpriseInfo.IsCertified,
|
|
|
|
|
CertifiedAt: enterpriseInfo.CertifiedAt,
|
|
|
|
|
CreatedAt: enterpriseInfo.CreatedAt,
|
|
|
|
|
UpdatedAt: enterpriseInfo.UpdatedAt,
|
|
|
|
|
}
|
2025-07-15 13:21:34 +08:00
|
|
|
response.IsCertified = enterpriseInfo.IsCertified
|
2025-07-13 16:36:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *UserApplicationServiceImpl) GetUser(ctx context.Context, query *queries.GetUserQuery) (*responses.UserProfileResponse, error) {
|
|
|
|
|
// ... implementation
|
|
|
|
|
return nil, fmt.Errorf("not implemented")
|
|
|
|
|
}
|