Nesta etapa do desenvolvimento da integração com o ERP do Banco Agrícola da China (ABC), o foco é a estruturação da lógica central de processamento. A arquitetura foi remodelada para centralizar o tratamento de mensagens Socket, conversão de XML e manipulação de arquivos de lote.
Estrutura de Serviços
| Componente | Descrição |
|---|---|
IBankIntegrationService |
Interface que define o contrato principal de requisição. |
BaseBankRequestProcessor |
Classe abstrata que contém o motor de processamento (Core). |
BankIntegrationService |
Implemantação concreta que expõe o serviço para a aplicação. |
1. Interface de Comunicação
Define o método padrão que todas as transações ERP devem seguir. O parâmetro de entrada é uma abstração de requisição e o retorno é um DTO contendo o status e os dados da resposta.
import com.example.demo.dto.request.BaseRequest;
import com.example.demo.dto.response.BankResponseDTO;
public interface IBankIntegrationService {
BankResponseDTO executeTransaction(BaseRequest requestData);
}
2. Processador Base (Lógica Central)
Esta classse abstrata gerencia o ciclo de vida de uma chamada ao banco, desde a formatação dos dados até o fechamento da conexão Socket.
- Obtém configurações de rede (IP e Porta) via
BankSettings. executeSocketFlow: Método principal que coordena as etapas.- Geração de arquivos temporários para operações de lote (MFS).
- Conversão de objetos Java para XML e adição de cabeçalhos de protocolo.
- Tratamento de codificação de caracteres (Charset) para garantir a integridade dos dados chineses.
import com.alibaba.fastjson.JSONObject;
import com.example.demo.config.BankSettings;
import com.example.demo.constant.InterfaceTypeEnum;
import com.example.demo.dto.request.BaseRequest;
import com.example.demo.dto.response.*;
import com.example.demo.utils.SocketHandler;
import com.example.demo.utils.XmlParser;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.*;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
public abstract class BaseBankRequestProcessor implements IBankIntegrationService {
private static final Logger logger = LoggerFactory.getLogger(BaseBankRequestProcessor.class);
protected BankSettings settings;
protected BankResponseDTO executeSocketFlow(BaseRequest request) {
BankResponseDTO responseWrapper = new BankResponseDTO();
if (request.getInterfaceType() == null) {
String error = "Tipo de transação inválido: " + request.getTransactionCode();
logger.error(error);
responseWrapper.setMessage(error);
return responseWrapper;
}
try {
// Inicializa metadados da requisição
this.prepareHeaderMetadata(request);
// Gerencia arquivos se necessário
this.processAttachmentFile(request);
// Serialização XML e montagem do pacote Socket
String xmlPayload = XmlParser.toXml(request);
String fullPacket = this.wrapWithProtocolHeader(xmlPayload);
responseWrapper.setRawRequest(fullPacket);
// Comunicação TCP/IP
byte[] responseBytes = SocketHandler.send(
settings.getHost(),
settings.getPort(),
fullPacket.getBytes(settings.getEncoding())
);
if (responseBytes != null) {
String responseStr = new String(responseBytes, settings.getEncoding()).trim();
responseWrapper.setRawResponse(responseStr);
// Parse da resposta XML para Objeto
BaseResponse responseObj = this.parseXmlResponse(request.getOutputClass(), responseStr);
responseWrapper.setData(responseObj);
// Processa arquivos de retorno (extratos/lotes)
this.handleResponseFile(request, responseObj);
}
} catch (Exception ex) {
logger.error("Falha na execução da transação bancária", ex);
responseWrapper.setMessage("Erro interno no processamento");
}
return responseWrapper;
}
private void prepareHeaderMetadata(BaseRequest req) {
req.setTransCode(req.getInterfaceType().getCode());
req.setChannel("ERP");
if (StringUtils.isBlank(req.getSerialNo())) {
req.setSerialNo(UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
LocalDateTime now = LocalDateTime.now();
req.setDate(now.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
req.setTime(now.format(DateTimeFormatter.ofPattern("HHmmss")));
}
private String wrapWithProtocolHeader(String xml) throws UnsupportedEncodingException {
int length = xml.getBytes(settings.getEncoding()).length;
// Protocolo: 1 byte (Cripto) + 6 bytes (Tamanho) + XML
return String.format("0%06d%s", length, xml);
}
protected <t baseresponse="" extends=""> T parseXmlResponse(Class<t> clazz, String rawXml) throws Exception {
int start = rawXml.indexOf("<ap>");
int end = rawXml.lastIndexOf("</ap>") + 5;
String cleanXml = rawXml.substring(start, end);
return XmlParser.fromXml(clazz, cleanXml);
}
@Autowired
public void setSettings(BankSettings settings) {
this.settings = settings;
}
}</t></t>
3. Implementação do Serviço
A classe de serviço expõe a funcionalidade de forma simplificada, herdando a complexidade do processador base.
import com.example.demo.dto.request.BaseRequest;
import com.example.demo.dto.response.BankResponseDTO;
import org.springframework.stereotype.Service;
@Service
public class BankIntegrationService extends BaseBankRequestProcessor {
@Override
public BankResponseDTO executeTransaction(BaseRequest requestData) {
return this.executeSocketFlow(requestData);
}
}
Exemplos de Uso e Testes
Abaixo, demonstramos como realizar operações comuns utilizando o serviço refatorado. É essencial configurar corretamente os parâmetros de agência e conta conforme o ambiente (Produção ou Homologação).
@SpringBootTest
public class BankIntegrationTest {
@Autowired
private IBankIntegrationService bankService;
/**
* Consulta de Saldo de Conta Corrente
*/
@Test
public void testBalanceInquiry() {
BalanceRequestDTO request = new BalanceRequestDTO();
request.setAccountNo("6228480010000000000");
request.setCurrency("CNY");
request.setProvinceCode("44"); // Ex: Guangdong
BankResponseDTO result = bankService.executeTransaction(request);
if (result.getData() != null) {
BalanceResponseDTO balance = (BalanceResponseDTO) result.getData();
System.out.println("Saldo Disponível: " + balance.getAvailableBalance());
}
}
/**
* Transferência Única (Pagamento Eletrônico)
*/
@Test
public void testSingleTransfer() {
TransferRequestDTO transfer = new TransferRequestDTO();
transfer.setAmount(new BigDecimal("100.00"));
transfer.setBeneficiaryName("Empresa de Tecnologia LTDA");
transfer.setBeneficiaryAccount("1234567890");
transfer.setRemitterAccount("0987654321");
transfer.setPurpose("Pagamento de Fornecedor");
BankResponseDTO response = bankService.executeTransaction(transfer);
System.out.println("ID da Transação: " + transfer.getSerialNo());
System.out.println("Status: " + response.getMessage());
}
}
A refatoração permite que novos tipos de transação sejam adicionados apenas criando novas classes DTO que herdam de BaseRequest e BaseResponse, mantendo a lógica de comunicação Socket e tratamento de erros isolada no processador abstrato.