package com.mydreamjob.auth.company; import com.mydreamjob.audit.AuditContext; import com.mydreamjob.audit.AuditService; import com.mydreamjob.auth.AuthService; import com.mydreamjob.auth.AuthSessionService; import com.mydreamjob.auth.User; import com.mydreamjob.auth.UserRepository; import com.mydreamjob.auth.SmsVerificationService; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import com.mydreamjob.company.Company; import com.mydreamjob.company.CompanyRepository; import com.mydreamjob.deletion.AccountDeletionPhoneHasher; import com.mydreamjob.deletion.AccountDeletionRepository; import com.mydreamjob.auth.password.PasswordPolicy; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataAccessException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.Clock; import java.time.Instant; import java.time.LocalDateTime; import java.util.Map; import java.util.Set; @Service @RequiredArgsConstructor public class CompanyRegistrationService { private static final String PURPOSE = "company-register"; private final CompanyRegistrationProperties properties; private final SmsVerificationService smsVerificationService; private final UserRepository userRepository; private final CompanyRepository companyRepository; private final PasswordPolicy passwordPolicy; private final PasswordEncoder passwordEncoder; private final AccountDeletionRepository accountDeletionRepository; private final AccountDeletionPhoneHasher accountDeletionPhoneHasher; private final AuthSessionService authSessionService; private final AuditService auditService; private final Clock clock; @Transactional public CompanyRegistrationResponse register(CompanyRegistrationRequest request, AuditContext ctx) { if (!properties.isEnabled()) { throw new BusinessException(ErrorCode.COMPANY_REGISTRATION_DISABLED); } String phone = request.getPhone().trim(); try { smsVerificationService.verifyCode(phone, request.getCode(), PURPOSE); String phoneHash = accountDeletionPhoneHasher.hash(phone); if (accountDeletionRepository.findByDeletedPhoneHash(phoneHash).isPresent()) { throw new BusinessException(ErrorCode.COMPANY_REGISTRATION_CONFLICT); } passwordPolicy.validateNewPassword(request.getPassword(), phone, null, passwordEncoder); if (userRepository.findByPhoneForUpdate(phone).isPresent()) { throw new BusinessException(ErrorCode.COMPANY_REGISTRATION_CONFLICT); } User user = User.builder() .phone(phone) .nickname("企业_" + phone.substring(7)) .passwordHash(passwordEncoder.encode(passwordPolicy.normalize(request.getPassword()))) .passwordChangedAt(LocalDateTime.ofInstant(clock.instant(), AuthSessionService.BUSINESS_ZONE)) .role(User.Role.COMPANY) .roles(Set.of(User.Role.COMPANY)) .active(true) .verified(false) .build(); user = userRepository.saveAndFlush(user); CompanyRegistrationRequest.CompanyDetails d = request.getCompany(); Company company = Company.builder() .ownerUserId(user.getId()) .name(d.getName()) .logoUrl(d.getLogoUrl()) .description(d.getDescription()) .industry(d.getIndustry()) .website(d.getWebsite()) .address(d.getAddress()) .verified(false) .build(); company = companyRepository.saveAndFlush(company); Instant now = clock.instant(); AuthSessionService.SessionTokens tokens = authSessionService.createSession(user, now, ctx); auditService.logRequired(user.getId(), "COMPANY_REGISTERED", "COMPANY", company.getId(), "{\"result\":\"SUCCESS\"}", ctx, LocalDateTime.ofInstant(now, AuthSessionService.BUSINESS_ZONE)); return new CompanyRegistrationResponse(user.getId(), company.getId(), User.Role.COMPANY.name(), "UNVERIFIED", tokens.accessToken(), tokens.refreshToken(), AuthService.EXPIRES_IN_SECONDS); } catch (DataIntegrityViolationException e) { throw new BusinessException(ErrorCode.COMPANY_REGISTRATION_CONFLICT); } catch (DataAccessException e) { // 数据库、Redis 或会话持久化故障统一失败关闭,不把 SQL/连接信息返回给客户端。 throw new BusinessException(ErrorCode.COMPANY_REGISTRATION_DEPENDENCY_UNAVAILABLE); } } }