Integração Java: Refatoração da Comunicação ERP com o Banco Agrícola da China (ABC)

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.

Tags: java ERP Socket-Programming Banking-API XML-Serialization

Publicado em 6-15 04:08 por Thomas