184 lines
9.4 KiB
Java
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;
|
|
}
|
|
}
|