Files
baiyue-firstleg/auth/src/main/java/com/baiyue/auth/service/MerchantRegistrationService.java
T
2026-09-11 16:17:16 +08:00

184 lines
9.4 KiB
Java

package com.baiyue.auth.service;
import com.baiyue.auth.client.IamOnboardingClient;
import com.baiyue.auth.dto.MerchantRegisterRequest;
import com.baiyue.auth.dto.MerchantRegisterResponse;
import com.baiyue.auth.entity.AuthAccount;
import com.baiyue.auth.repository.AuthAccountRepository;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.audit.BusinessAudit;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import feign.FeignException;
import jakarta.annotation.Resource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicBoolean;
@Service
public class MerchantRegistrationService {
private static final Logger LOGGER = LoggerFactory.getLogger(MerchantRegistrationService.class);
@Resource private AuthAccountRepository accountRepository;
@Resource private PasswordEncoder passwordEncoder;
@Resource private IamOnboardingClient onboardingClient;
@Value("${baiyue.security.internal-service-token}") private String internalToken;
@Transactional
@BusinessAudit(event = "MERCHANT_REGISTER", resourceType = "ACCOUNT",
resourceId = "#request.username", description = "商户注册", captureAfter = false)
public MerchantRegisterResponse register(MerchantRegisterRequest request) {
AuthAccount usernameAccount = accountRepository.findByUsername(request.username());
AuthAccount phoneAccount = accountRepository.findByPhone(request.phone());
if (usernameAccount != null && phoneAccount != null
&& usernameAccount.getId().equals(phoneAccount.getId())
&& "MERCHANT".equals(usernameAccount.getDefaultAppCode())
&& "ACTIVE".equals(usernameAccount.getAccountStatus())
&& passwordEncoder.matches(request.password(), usernameAccount.getPasswordHash())) {
return existingRegistration(usernameAccount);
}
if (usernameAccount != null) {
throw new BizException(ResultCode.CONFLICT, "用户名已存在");
}
if (phoneAccount != null) {
throw new BizException(ResultCode.CONFLICT, "该手机号已注册,请直接登录后继续完善企业资质");
}
long accountId = IdWorker.getId();
String passwordHash = passwordEncoder.encode(request.password());
AtomicBoolean compensationCompleted = new AtomicBoolean(false);
registerRollbackCompensation(accountId, compensationCompleted);
IamOnboardingClient.OnboardingResponse onboarding;
try {
var response = onboardingClient.register(internalToken,
new IamOnboardingClient.OnboardingRequest(accountId, request.displayName(),
request.companyName(), request.legalPerson(), request.phone(),
request.contactEmail(), request.countryCode(), request.address()));
if (response == null) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "IAM registration failed");
}
if (!response.successful()) {
ResultCode code = ResultCode.of(response.code());
String message = response.message() == null || response.message().isBlank()
? code.message() : response.message();
throw new BizException(code, message);
}
if (response.data() == null) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "IAM registration failed");
}
onboarding = response.data();
} catch (FeignException exception) {
BizException failure = new BizException(ResultCode.SERVICE_UNAVAILABLE,
"IAM registration service is unavailable");
failure.addSuppressed(exception);
compensateOnboarding(accountId, failure, compensationCompleted);
throw failure;
} catch (BizException exception) {
compensateOnboarding(accountId, exception, compensationCompleted);
throw exception;
} catch (RuntimeException exception) {
BizException failure = new BizException(ResultCode.SERVICE_UNAVAILABLE,
"IAM registration service is unavailable");
failure.addSuppressed(exception);
compensateOnboarding(accountId, failure, compensationCompleted);
throw failure;
}
AuthAccount account = new AuthAccount();
account.setId(accountId);
account.setUsername(request.username());
account.setPhone(request.phone());
account.setPasswordHash(passwordHash);
account.setDisplayName(request.displayName());
account.setAccountStatus("ACTIVE");
account.setFailedLoginCount(0);
account.setTokenVersion(1);
account.setDefaultTenantId(onboarding.tenantId());
account.setOperatorTenantId(onboarding.tenantId());
account.setDefaultMembershipId(onboarding.membershipId());
account.setDefaultAppCode("MERCHANT");
account.setDeleted(0);
try {
accountRepository.insert(account);
} catch (DuplicateKeyException exception) {
BizException failure = new BizException(ResultCode.CONFLICT, "用户名或手机号已存在");
failure.addSuppressed(exception);
compensateOnboarding(accountId, failure, compensationCompleted);
throw failure;
} catch (RuntimeException exception) {
compensateOnboarding(accountId, exception, compensationCompleted);
throw exception;
}
return new MerchantRegisterResponse(accountId, onboarding.tenantId(), onboarding.membershipId(),
onboarding.tenantStatus(), onboarding.dimensionInitializationStatus(),
"LOGIN_AND_COMPLETE_QUALIFICATIONS");
}
private MerchantRegisterResponse existingRegistration(AuthAccount account) {
try {
var response = onboardingClient.registration(internalToken, account.getId());
if (response == null || !response.successful() || response.data() == null) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "注册状态暂时无法确认,请直接登录后继续完善企业资质");
}
var onboarding = response.data();
if (!java.util.Set.of("PROFILE_REQUIRED", "REJECTED").contains(onboarding.tenantStatus())) {
throw new BizException(ResultCode.CONFLICT, "该账号已注册,请直接登录");
}
return new MerchantRegisterResponse(account.getId(), onboarding.tenantId(), onboarding.membershipId(),
onboarding.tenantStatus(), onboarding.dimensionInitializationStatus(),
"LOGIN_AND_COMPLETE_QUALIFICATIONS");
} catch (FeignException exception) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "注册状态暂时无法确认,请直接登录后继续完善企业资质");
}
}
private void registerRollbackCompensation(long accountId, AtomicBoolean compensationCompleted) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return;
}
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_COMMITTED || compensationCompleted.get()) {
return;
}
IllegalStateException rollbackFailure = new IllegalStateException(
"Auth transaction rolled back after IAM onboarding");
if (!compensateOnboarding(accountId, rollbackFailure, compensationCompleted)) {
LOGGER.error("IAM registration compensation failed after Auth rollback, authUserId={}",
accountId, rollbackFailure);
}
}
});
}
private boolean compensateOnboarding(long accountId, RuntimeException registrationFailure,
AtomicBoolean compensationCompleted) {
if (compensationCompleted.get()) {
return true;
}
try {
var response = onboardingClient.compensate(internalToken, accountId);
if (response != null && response.successful()) {
compensationCompleted.set(true);
return true;
}
{
String code = response == null ? "empty-response" : response.code();
String message = response == null ? "IAM compensation returned no response" : response.message();
registrationFailure.addSuppressed(new IllegalStateException(
"IAM registration compensation failed: code=" + code + ", message=" + message));
}
} catch (RuntimeException compensationFailure) {
registrationFailure.addSuppressed(compensationFailure);
}
return false;
}
}