优化
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.baiyue.auth.client;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -14,6 +16,11 @@ public interface IamOnboardingClient {
|
||||
@RequestHeader("X-Internal-Service-Token") String internalToken,
|
||||
@RequestBody OnboardingRequest request);
|
||||
|
||||
@DeleteMapping("/merchant-registrations/{authUserId}/compensation")
|
||||
ApiResponse<Void> compensate(
|
||||
@RequestHeader("X-Internal-Service-Token") String internalToken,
|
||||
@PathVariable("authUserId") Long authUserId);
|
||||
|
||||
record OnboardingRequest(Long authUserId, String displayName, String companyName,
|
||||
String legalPerson, String legalPhone, String contactEmail,
|
||||
String countryCode, String address) { }
|
||||
|
||||
@@ -47,7 +47,7 @@ public class AuthController {
|
||||
@SecurityRequirements
|
||||
@PostMapping("/register")
|
||||
@Operation(summary = "Merchant registration / 客户注册",
|
||||
description = "Create a login account and an enterprise profile awaiting one business-license upload.")
|
||||
description = "Create a login account and an enterprise profile awaiting four required qualification files.")
|
||||
public ApiResponse<MerchantRegisterResponse> register(@Valid @RequestBody MerchantRegisterRequest request) {
|
||||
return ApiResponse.success(registrationService.register(request));
|
||||
}
|
||||
|
||||
@@ -11,13 +11,21 @@ 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;
|
||||
@@ -31,6 +39,9 @@ public class MerchantRegistrationService {
|
||||
throw new BizException(ResultCode.CONFLICT, "Username already exists");
|
||||
}
|
||||
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,
|
||||
@@ -51,17 +62,26 @@ public class MerchantRegistrationService {
|
||||
}
|
||||
onboarding = response.data();
|
||||
} catch (FeignException exception) {
|
||||
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "IAM registration service is unavailable");
|
||||
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) {
|
||||
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "IAM registration service is unavailable");
|
||||
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.setPasswordHash(passwordEncoder.encode(request.password()));
|
||||
account.setPasswordHash(passwordHash);
|
||||
account.setDisplayName(request.displayName());
|
||||
account.setAccountStatus("ACTIVE");
|
||||
account.setFailedLoginCount(0);
|
||||
@@ -71,8 +91,61 @@ public class MerchantRegistrationService {
|
||||
account.setDefaultMembershipId(onboarding.membershipId());
|
||||
account.setDefaultAppCode("MERCHANT");
|
||||
account.setDeleted(0);
|
||||
accountRepository.insert(account);
|
||||
try {
|
||||
accountRepository.insert(account);
|
||||
} catch (DuplicateKeyException exception) {
|
||||
BizException failure = new BizException(ResultCode.CONFLICT, "Username already exists");
|
||||
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(), "LOGIN_AND_UPLOAD_BUSINESS_LICENSE");
|
||||
onboarding.tenantStatus(), "LOGIN_AND_COMPLETE_QUALIFICATIONS");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user