You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

104 lines
4.9 KiB

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);
}
}
}