暂存提交

This commit is contained in:
2026-07-31 18:10:32 +08:00
parent e834482717
commit 9a37657148
35 changed files with 435 additions and 44 deletions
@@ -11,9 +11,12 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class OrderMasterDataGuard {
private static final Logger log = LoggerFactory.getLogger(OrderMasterDataGuard.class);
private final WarehouseClient warehouses;
private final PartnerClient partners;
private final String token;
@@ -37,13 +40,15 @@ public class OrderMasterDataGuard {
}
return new Snapshots(partner,receiving,destination);
} catch (FeignException.NotFound exception) {
throw new BizException(ResultCode.NOT_FOUND,"往来单位或仓库不存在、已停用或不属于当前企业");
throw new BizException(ResultCode.NOT_FOUND,"往来单位或仓库不存在或不属于当前企业");
} catch (FeignException.Conflict exception) {
throw new BizException(ResultCode.CONFLICT,"往来单位或仓库已停用");
} catch (FeignException exception) {
throw unavailable("主数据");
throw downstreamFailure(exception);
} catch (BizException exception) {
throw exception;
} catch (RuntimeException exception) {
throw unavailable("主数据");
throw downstreamFailure(exception);
}
}
public void validatePersisted(long tenantId, Long partnerId, Long receivingWarehouseId, Long destinationWarehouseId) {
@@ -54,13 +59,15 @@ public class OrderMasterDataGuard {
loadWarehouse(tenantId,receivingWarehouseId,"收货仓库");
if (destinationWarehouseId!=null) loadWarehouse(tenantId,destinationWarehouseId,"目的仓库");
} catch (FeignException.NotFound exception) {
throw new BizException(ResultCode.NOT_FOUND,"往来单位或仓库已停用、越权或不存在");
throw new BizException(ResultCode.NOT_FOUND,"往来单位或仓库不存在或不属于当前企业");
} catch (FeignException.Conflict exception) {
throw new BizException(ResultCode.CONFLICT,"往来单位或仓库已停用");
} catch (FeignException exception) {
throw unavailable("主数据");
throw downstreamFailure(exception);
} catch (BizException exception) {
throw exception;
} catch (RuntimeException exception) {
throw unavailable("主数据");
throw downstreamFailure(exception);
}
}
private PartnerClient.Partner loadPartner(long tenantId,long id) {
@@ -68,8 +75,10 @@ public class OrderMasterDataGuard {
if (response==null || !response.successful() || response.data()==null)
throw unavailable("往来单位");
var value=response.data();
if (!Long.valueOf(tenantId).equals(value.tenantId()) || !"ACTIVE".equals(value.status()))
throw new BizException(ResultCode.FORBIDDEN,"往来单位不属于当前企业或已停用");
if (!Long.valueOf(tenantId).equals(value.tenantId()))
throw new BizException(ResultCode.FORBIDDEN,"往来单位不属于当前企业");
if (!"ACTIVE".equals(value.status()))
throw new BizException(ResultCode.CONFLICT,"往来单位已停用");
if (!StringUtils.hasText(value.partnerName())) throw unavailable("往来单位");
return value;
}
@@ -78,8 +87,10 @@ public class OrderMasterDataGuard {
if (response==null || !response.successful() || response.data()==null || response.data().warehouse()==null)
throw unavailable(name);
var value=response.data();
if (!Long.valueOf(tenantId).equals(value.warehouse().tenantId()) || !"ACTIVE".equals(value.warehouse().status()))
throw new BizException(ResultCode.FORBIDDEN,name+"不属于当前企业或已停用");
if (!Long.valueOf(tenantId).equals(value.warehouse().tenantId()))
throw new BizException(ResultCode.FORBIDDEN,name+"不属于当前企业");
if (!"ACTIVE".equals(value.warehouse().status()))
throw new BizException(ResultCode.CONFLICT,name+"已停用");
if (!Long.valueOf(id).equals(value.warehouse().id()) || !StringUtils.hasText(value.warehouse().warehouseName())) {
throw unavailable(name);
}
@@ -88,6 +99,27 @@ public class OrderMasterDataGuard {
private BizException unavailable(String name) {
return new BizException(ResultCode.SERVICE_UNAVAILABLE,name+"服务暂时不可用");
}
private BizException downstreamFailure(RuntimeException exception) {
FeignException feignException = findFeignException(exception);
if (feignException != null) {
if (feignException.status() == 404)
return new BizException(ResultCode.NOT_FOUND,"往来单位或仓库不存在或不属于当前企业");
if (feignException.status() == 409)
return new BizException(ResultCode.CONFLICT,"往来单位或仓库已停用");
if (feignException.status() == 403)
return new BizException(ResultCode.SERVICE_UNAVAILABLE,
"主数据内部服务凭证不一致,请检查 INTERNAL_SERVICE_TOKEN");
}
log.warn("订单主数据内部调用失败,已转换为 S0503", exception);
return unavailable("主数据");
}
private FeignException findFeignException(Throwable exception) {
Throwable current = exception;
for (int depth = 0; current != null && depth < 10; depth++, current = current.getCause()) {
if (current instanceof FeignException feignException) return feignException;
}
return null;
}
public record Snapshots(PartnerClient.Partner partner, WarehouseClient.Detail receiving,
WarehouseClient.Detail destination) { }
}
+11
View File
@@ -31,3 +31,14 @@ baiyue:
url: ${IAM_SERVICE_URL:}
warehouse:
url: ${WAREHOUSE_SERVICE_URL:}
logging:
file:
name: "${LOGGING_FILE_NAME:${LOG_PATH:./logs}/${spring.application.name}.log}"
logback:
rollingpolicy:
file-name-pattern: "${LOGGING_ROLLING_FILE_NAME_PATTERN:${LOG_PATH:./logs}/${spring.application.name}.%d{yyyy-MM-dd}.%i.log.gz}"
max-file-size: "${LOGGING_ROLLING_MAX_FILE_SIZE:100MB}"
max-history: "${LOGGING_ROLLING_MAX_HISTORY:30}"
total-size-cap: "${LOGGING_ROLLING_TOTAL_SIZE_CAP:5GB}"
clean-history-on-start: "${LOGGING_ROLLING_CLEAN_HISTORY_ON_START:false}"
@@ -6,11 +6,16 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.order.client.PartnerClient;
import com.baiyue.order.client.WarehouseClient;
import com.baiyue.order.dto.OrderRequests;
import feign.FeignException;
import feign.Request;
import feign.Response;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@@ -71,6 +76,67 @@ class OrderMasterDataGuardTest {
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void downstreamForbiddenExplainsInternalCredentialMismatch() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(403));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.satisfies(exception -> {
BizException businessException = (BizException) exception;
assertThat(businessException.getResultCode()).isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
assertThat(businessException.getMessage()).contains("内部服务凭证");
});
}
@Test
void downstreamNotFoundMapsToBusinessNotFound() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(404));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.NOT_FOUND);
}
@Test
void wrappedDownstreamConflictMapsToBusinessConflict() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(new IllegalStateException("wrapped", feign(409)));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.CONFLICT);
}
@Test
void downstreamServerErrorMapsToServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(500));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void inactiveMasterDataInSuccessfulResponseMapsToConflict() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7))
.thenReturn(ApiResponse.success(new PartnerClient.Partner(7L,10001L,"客户","INACTIVE")));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.CONFLICT);
}
private FeignException feign(int status) {
Request request = Request.create(Request.HttpMethod.GET, "http://warehouse-service/internal/test",
Map.of(), null, StandardCharsets.UTF_8, null);
Response response = Response.builder().status(status).reason("test").request(request)
.headers(Map.of()).build();
return FeignException.errorStatus("masterData", response);
}
private OrderRequests.SaveRequest request(Long partner,Long warehouse) {
var party=new OrderRequests.Party("公司","联系人","13800138000",null,"CN",null,"深圳",null,"地址",null);
var cargo=new OrderRequests.Cargo("货物",null,"CARTON",1,BigDecimal.ONE,BigDecimal.ONE,