一 webClient 基础配置
1 maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2 application.yaml 配置连接信息
webClient:
api:
base-url: https://api.xxxx.es:5244/v1/
endpoints:
f1: /components/schemas/AltaFacturaRequest
f2: /components/schemas/FacturaSimplificadaRequest
headers:
content-type: application/json; charset=utf-8
accept: application/json
connect-timeout: 2000
response-timeout: 3000
mono-timeout: 5000
3 创建配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "webClient.api")
public class VerifacApiConfig {
private String baseUrl;
private Map<String, String> endpoints;
private Map<String, String> headers;
private int connectTimeout;
private int responseTimeout;
private int monoTimeout;
}
4 创建WebClient工厂
@Component
@RequiredArgsConstructor
public class VerifacWebClientConfig {
private final VerifacApiConfig apiConfig;
public WebClient createClient(){
return WebClient.builder()
.baseUrl(apiConfig.getBaseUrl())
.defaultHeaders(headers -> apiConfig.getHeaders().forEach(headers::add))
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, apiConfig.getConnectTimeout())
.responseTimeout(Duration.ofMillis(apiConfig.getResponseTimeout()))
))
.build();
}
public String getEndpointForInvoiceType(String invoiceType){
return apiConfig.getEndpoints().getOrDefault(
invoiceType.toLowerCase(),
apiConfig.getEndpoints().get("f2")
);
}
}
5 封装webClient
@Service
@RequiredArgsConstructor
@Slf4j
public class VerifacApiClient {
private final TaxInvoiceHandlerFactory handlerFactory;
private final VerifacWebClientConfig webClientConfig;
private final VerifacApiConfig apiConfig;
private final RabbitTemplate rabbitTemplate;
public Mono<TaxUploadResult> uploadInvoiceReactive(VerifacInvoice invoice) {
TaxInvoiceHandler handler = handlerFactory.getHandler(invoice.getInvoiceTipo());
return webClientConfig.createClient()
.post()
.uri(endpoint)
.bodyValue(handler.buildRequest(invoice))
.retrieve()
.onStatus(HttpStatusCode::isError, response ->
response.bodyToMono(ApiErrorResponse.class)
.flatMap(errorBody -> Mono.error(new InvoiceApiException(errorBody.getMensaje(), errorBody.getCodigo())))
)
.bodyToMono(handler.getResponseType())
.timeout(Duration.ofMillis(apiConfig.getMonoTimeout()))
.flatMap(response -> {
return sendToMqAsync(invoice, response)
.map(success -> new TaxUploadResult(
invoice,
true,
response.getFactura().getQr()
));
})
.onErrorResume(e -> handleError(e, invoice));
}
private Mono<Boolean> sendToMqAsync(VerifacInvoice invoice, TaxInvoiceResponse response) {
return Mono.fromCallable(() -> {
long start = System.currentTimeMillis();
log.info("异步处理 发送发票到MQ, 发票信息 : {}",response);
TaxInvoiceHandler handler = handlerFactory.getHandler(invoice.getInvoiceTipo());
handler.handleResponse(response, invoice);
rabbitTemplate.convertAndSend(
OrderDirectMQConfig.ORDER_EXCHANGE,
OrderDirectMQConfig.INVOICE_ROUTER_KEY,
invoice,
message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
}
);
log.info("已发送MQ异步处理,处理耗时: {}ms", System.currentTimeMillis() - start);
return true;
}).subscribeOn(Schedulers.boundedElastic());
}
private Mono<TaxUploadResult> handleError(Throwable e, VerifacInvoice invoice) {
String errorMsg;
if (e instanceof InvoiceApiException) {
errorMsg = e.getMessage();
log.error(errorMsg);
} else if (e instanceof IllegalArgumentException) {
errorMsg = "税务接口参数错误: " + e.getMessage();
log.error(errorMsg);
} else if (e instanceof IllegalStateException) {
errorMsg = "税务接口状态错误: " + e.getMessage();
log.error(errorMsg);
} else if (e instanceof NullPointerException) {
errorMsg = "税务接口返回结果为空";
} else if (e instanceof ConnectTimeoutException) {
errorMsg = "税务接口网络或服务器问题导致连接超时";
log.error(errorMsg);
} else if (e instanceof ReadTimeoutException) {
errorMsg = "税务接口网络或服务器问题导致响应超时";
log.error(errorMsg);
} else if (e instanceof TimeoutException) {
errorMsg = "税务接口网络或服务器问题导致处理超时";
log.error(errorMsg);
} else {
errorMsg = "税务响应非规定的JSON结果异常!: " + e.getMessage();
log.error(errorMsg, e);
}
return Mono.just(new TaxUploadResult(
invoice,
false,
errorMsg
));
}
@Data
@AllArgsConstructor
public static class TaxUploadResult {
private VerifacInvoice invoice;
private boolean success;
private String errorMessage;
}
}
二 请求/响应 处理工厂
1 发票处理器工厂
@Component
@RequiredArgsConstructor
public class TaxInvoiceHandlerFactory {
private final List<TaxInvoiceHandler> handlers;
public TaxInvoiceHandler getHandler(String invoiceType) {
return handlers.stream()
.filter(handler -> handler.getSupportedType().equalsIgnoreCase(invoiceType))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
String.format("No tax invoice handler found for invoice type %s", invoiceType)
));
}
}
2 定义处理器接口
public interface TaxInvoiceHandler {
public String getSupportedType();
public TaxInvoiceRequest buildRequest(VerifacInvoice invoice);
public void handleResponse(TaxInvoiceResponse response, VerifacInvoice invoice);
public Class<? extends TaxInvoiceResponse> getResponseType();
}
3 接口实现A
@Component
@Slf4j
@RequiredArgsConstructor
public class AltaFacturaHandler implements TaxInvoiceHandler {
private final ITenantService tenantService;
@Override
public String getSupportedType() {
return FacturaConstants.FACTURA_TIPO_COMPLETA;
}
@Override
public TaxInvoiceRequest buildRequest(VerifacInvoice invoice) {
AltaFacturaRequest request = new AltaFacturaRequest();
Tenant tenant = tenantService.selectTenantByTenantId(invoice.getTenantId());
Emisor emisor = new Emisor();
emisor.setNif(tenant.getInvoiceTaxNumber());
emisor.setCodigo(tenant.getApiKey());
request.setEmisor(emisor);
request.setOperacion(invoice.getInvoiceOperacion());
request.setIdTransaccion(invoice.getOrderNo());
List<DesgloseNormal> desgloses = invoice.getVerifacInvoiceDetailList().stream()
.map(invoiceDetail -> {
DesgloseNormal desglose = new DesgloseNormal();
desglose.setRegimen(invoiceDetail.getDetailRegimen());
desglose.setCalificacion(invoiceDetail.getDetailCalificacion());
desglose.setBase(invoiceDetail.getDetailBase());
desglose.setTipoIva(invoiceDetail.getDetailTipoIva());
desglose.setCuotaIva(invoiceDetail.getDetailCuotaIva());
desglose.setTipoRe(invoiceDetail.getDetailTipoRe());
desglose.setCuotaRe(invoiceDetail.getDetailCuotaRe());
return desglose;
}).toList();
request.setDesglose(desgloses);
return request;
}
@Override
public void handleResponse(TaxInvoiceResponse response, VerifacInvoice invoice) {
if (response instanceof AltaFacturaResponse altaFacturaResponse) {
invoice.setEmisorNombre(altaFacturaResponse.getEmisor().getNombre());
invoice.setInvoiceOperacion(altaFacturaResponse.getOperacion());
invoice.setInvoiceIdFactura(altaFacturaResponse.getFactura().getIdFactura());
invoice.setInvoiceTipo(altaFacturaResponse.getFactura().getTipo());
invoice.setInvoiceSerie(altaFacturaResponse.getFactura().getSerie());
invoice.setInvoiceNumero(altaFacturaResponse.getFactura().getNumero());
invoice.setInvoiceFecha(altaFacturaResponse.getFactura().getFecha());
invoice.setInvoiceHora(altaFacturaResponse.getFactura().getHora());
invoice.setInvoiceImporteImpuestos(altaFacturaResponse.getFactura().getImporteImpuestos());
invoice.setInvoiceImporteTotal(altaFacturaResponse.getFactura().getImporteTotal());
invoice.setInvoiceDescripcion(altaFacturaResponse.getFactura().getDescripcion());
invoice.setInvoiceEstado(altaFacturaResponse.getFactura().getEstado());
invoice.setInvoiceQr(altaFacturaResponse.getFactura().getQr());
invoice.setInvoiceMillisegundos(altaFacturaResponse.getFactura().getMillisegundos());
if (altaFacturaResponse.getFactura().getQr() == null) {
invoice.setInvoiceStatus(FacturaConstants.INVOICE_STATUS_RESPONSE_EXCEPTION);
invoice.setApiResponse("API返回的QR码为空");
}
}
}
@Override
public Class<? extends TaxInvoiceResponse> getResponseType() {
return AltaFacturaResponse.class;
}
}
4 接口实现B
@Component
@Slf4j
@RequiredArgsConstructor
public class FacturaSimplificadaHandler implements TaxInvoiceHandler {
private final ITenantService tenantService;
@Override
public String getSupportedType() {
return FacturaConstants.FACTURA_TIPO_SIMPLIFICADA;
}
@Override
public TaxInvoiceRequest buildRequest(VerifacInvoice invoice) {
FacturaSimplificadaRequest request = new FacturaSimplificadaRequest();
Tenant tenant = tenantService.selectTenantByTenantId(invoice.getTenantId());
Emisor emisor = new Emisor();
emisor.setNif(tenant.getInvoiceTaxNumber());
emisor.setCodigo(tenant.getApiKey());
request.setEmisor(emisor);
System.out.println("########################### 发行者信息");
System.out.println("tenant:" + tenant);
System.out.println("发行者:" + emisor);
System.out.println("########################### 发行者信息");
request.setOperacion(invoice.getInvoiceOperacion());
request.setIdTransaccion(invoice.getOrderNo());
List<DesgloseSimplificado> desgloses = invoice.getVerifacInvoiceDetailList().stream()
.map(detail -> {
DesgloseSimplificado desglose = new DesgloseSimplificado();
desglose.setTipoIva(detail.getDetailTipoIva());
desglose.setTotal(detail.getDetailSumaTotal());
return desglose;
}).toList();
request.setDesglose(desgloses);
System.out.println("########################### 请求信息");
System.out.println(request);
System.out.println("########################### 请求信息");
return request;
}
@Override
public void handleResponse(TaxInvoiceResponse response, VerifacInvoice invoice) {
if (response instanceof FacturaSimplificadaResponse simplificadaResponse) {
invoice.setEmisorNombre(simplificadaResponse.getEmisor().getNombre());
invoice.setInvoiceOperacion(simplificadaResponse.getOperacion());
invoice.setInvoiceIdFactura(simplificadaResponse.getFactura().getIdFactura());
invoice.setInvoiceTipo(simplificadaResponse.getFactura().getTipo());
invoice.setInvoiceSerie(simplificadaResponse.getFactura().getSerie());
invoice.setInvoiceNumero(simplificadaResponse.getFactura().getNumero());
invoice.setInvoiceFecha(simplificadaResponse.getFactura().getFecha());
invoice.setInvoiceHora(simplificadaResponse.getFactura().getHora());
invoice.setInvoiceImporteImpuestos(simplificadaResponse.getFactura().getImporteImpuestos());
invoice.setInvoiceImporteTotal(simplificadaResponse.getFactura().getImporteTotal());
invoice.setInvoiceDescripcion(simplificadaResponse.getFactura().getDescripcion());
invoice.setInvoiceEstado(simplificadaResponse.getFactura().getEstado());
invoice.setInvoiceQr(simplificadaResponse.getFactura().getQr());
invoice.setInvoiceMillisegundos(simplificadaResponse.getFactura().getMillisegundos());
simplificadaResponse.getDesglose().forEach(desglose -> {
invoice.getVerifacInvoiceDetailList().add(
VerifacInvoiceDetail.builder()
.detailBase(desglose.getBase())
.detailTipoIva(desglose.getTipoIva())
.detailCuotaIva(desglose.getCuotaIva())
.build());
});
if (simplificadaResponse.getFactura().getQr() == null) {
invoice.setInvoiceStatus(FacturaConstants.INVOICE_STATUS_RESPONSE_EXCEPTION);
invoice.setApiResponse("API返回的QR码为空");
}
}
}
@Override
public Class<? extends TaxInvoiceResponse> getResponseType() {
return FacturaSimplificadaResponse.class;
}
}
三 业务使用
@Slf4j
@Service
@RequiredArgsConstructor
public class VerifacApiServiceImpl implements IVerifacApiService {
private final VerifacApiClient verifacApiClient;
@Override
public VerifacApiClient.TaxUploadResult uploadInvoiceBySalessOrder(VerifacInvoice invoice) {
try {
VerifacApiClient.TaxUploadResult apiResult = verifacApiClient.uploadInvoiceReactive(invoice)
.block(Duration.ofSeconds(5));
if (apiResult == null) {
apiResult = new VerifacApiClient.TaxUploadResult(invoice, false, "API税务接口无响应!");
}
if (apiResult.isSuccess() && apiResult.getInvoice() == null){
apiResult.setSuccess(false);
apiResult.setErrorMessage("API税务接口没有返回响应数据!");
log.error("API税务接口返回数据异常!异常原因:{}", apiResult.getErrorMessage());
}
return apiResult;
} catch (Exception e) {
log.error("API税务接口请求异常!异常原因:{}", e.getMessage());
return new VerifacApiClient.TaxUploadResult(invoice, false, "API税务接口请求异常!");
}
}
@Override
public VerifacApiClient.TaxUploadResult uploadInvoiceByDeadLetterInvoiceResend(VerifacInvoice invoice) {
try {
VerifacApiClient.TaxUploadResult apiResult = verifacApiClient.uploadInvoiceReactive(invoice)
.block(Duration.ofSeconds(5));
if (apiResult == null) {
apiResult = new VerifacApiClient.TaxUploadResult(invoice, false, "API税务接口无响应!");
}
if (apiResult.isSuccess() && apiResult.getInvoice() == null){
apiResult.setSuccess(false);
apiResult.setErrorMessage("API税务接口没有返回响应数据!");
log.error("API税务接口返回数据异常!异常原因:{}", apiResult.getErrorMessage());
}
return apiResult;
} catch (Exception e) {
log.error("API税务接口请求异常!异常原因:{}", e.getMessage());
return new VerifacApiClient.TaxUploadResult(invoice, false, "API税务接口请求异常!");
}
}
}