package com.ruoyi.cwgl.service.impl; import com.cmbs.client.header.ClientConfig; import com.cmbs.client.util.OutBoundServiceUtils; import com.google.gson.Gson; import com.ruoyi.cwgl.domain.FundFlow; import com.ruoyi.cwgl.mapper.FundFlowMapper; import com.ruoyi.cwgl.service.ICmbsBankSyncService; import com.ruoyi.cwgl.test.GatewayConfigUtils; import com.ruoyi.cwgl.domain.Q7517Request; import com.ruoyi.cwgl.domain.Q7517Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * CMBS银行流水同步Service业务层处理 * * @author ruoyi * @date 2026-04-15 */ @Service @Transactional(rollbackFor = Exception.class) public class CmbsBankSyncServiceImpl implements ICmbsBankSyncService { protected final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private FundFlowMapper fundFlowMapper; @Override public String syncFromCmbs(String acctNum, String startDate, String endDate) { if (acctNum == null || acctNum.trim().isEmpty()) { throw new RuntimeException("账号不能为空"); } if (startDate == null || endDate == null) { throw new RuntimeException("日期范围不能为空"); } ClientConfig clientConfig = GatewayConfigUtils.getClientConfig(); Gson gson = new Gson(); int totalSynced = 0; int totalSkipped = 0; int currentPage = 1; int pageSize = 100; try { while (true) { // 构建请求参数 Q7517Request request = new Q7517Request(); request.setAcctNum(acctNum); request.setTradeStartDate(startDate); request.setTradeEndDate(endDate); request.setCurrentPage(currentPage); request.setPageSize(pageSize); String queryBody = gson.toJson(request); String reqSeq = generateReqSeq(); String response = OutBoundServiceUtils.execute(clientConfig, queryBody, "", "CMBS_Q7517", reqSeq); logger.info("CMBS Q7517 响应 [页{}]: {}", currentPage, response); Q7517Response resp = gson.fromJson(response, Q7517Response.class); if (resp == null || resp.getBatchInfo() == null || resp.getBatchInfo().isEmpty()) { break; } List batchInfoList = resp.getBatchInfo(); List toInsert = new ArrayList<>(); for (Q7517Response.BatchInfo info : batchInfoList) { // 按URID去重 if (info.getUrid() != null && !info.getUrid().isEmpty()) { FundFlow existing = fundFlowMapper.selectFundFlowByUrid(info.getUrid()); if (existing != null) { totalSkipped++; continue; } } FundFlow fundFlow = convertToFundFlow(info, acctNum); toInsert.add(fundFlow); } if (!toInsert.isEmpty()) { fundFlowMapper.insertFundFlowBatch(toInsert); totalSynced += toInsert.size(); } // 判断是否还有下一页 Number totalNum = resp.getTotalNum(); if (totalNum != null && currentPage * pageSize >= totalNum.intValue()) { break; } currentPage++; } return String.format("同步完成:新增 %d 条,跳过(已存在) %d 条", totalSynced, totalSkipped); } catch (Exception e) { logger.error("CMBS银行流水同步失败", e); throw new RuntimeException("CMBS银行流水同步失败: " + e.getMessage(), e); } } /** * 将 Q7517 响应数据转换为 FundFlow 实体 */ private FundFlow convertToFundFlow(Q7517Response.BatchInfo info, String acctNum) { FundFlow fundFlow = new FundFlow(); // 银行主键 fundFlow.setUrid(info.getUrid()); // 企业编号 fundFlow.setOrgNo(info.getOrgNo()); // 银行流水号 fundFlow.setBankFlowNo(info.getBankSerialNumber()); // 本方账号 fundFlow.setOurAccount(info.getPayerAcctNo()); // 本方户名 fundFlow.setPayerAcctName(info.getPayerAcctName()); // 对方账号 fundFlow.setCounterpartyAccount(info.getPayeeAcctNo()); // 对方户名 fundFlow.setCounterpartyName(info.getPayeeAcctName()); // 对方银行 fundFlow.setCounterpartyBank(info.getPayeeBankNo()); // 交易金额 if (info.getTransAmount() != null) { fundFlow.setTransactionAmount(new BigDecimal(info.getTransAmount().toString())); } // 账户余额 if (info.getCurrentBalance() != null) { fundFlow.setAccountBalance(new BigDecimal(info.getCurrentBalance().toString())); } // 交易币种 fundFlow.setCurrency(info.getCurrName()); // 币种编号 fundFlow.setCurrNo(info.getCurrNo()); // 用途 fundFlow.setPurpose(info.getPurpose()); // 摘要 fundFlow.setSummary(info.getMemo()); // 款项性质 fundFlow.setNatureNames(info.getNatureNames()); // 单据唯一标识号 fundFlow.setSrcNoteCode(info.getSrcNoteCode()); // 是否退汇 fundFlow.setIsReturn(info.getIsReturn()); // 第三方流水号 fundFlow.setCustomizedSerialNum(info.getCustomizedSerialNum()); // 交易方向转换:Q7517(1-支出 2-收入) → FundFlow(1-支 0-收) if ("1".equals(info.getMoneyWay())) { fundFlow.setIncomeExpenseFlag(1); } else if ("2".equals(info.getMoneyWay())) { fundFlow.setIncomeExpenseFlag(0); } // 交易日期:合并 tradeDate + tradeTime try { if (info.getTradeTime() != null && !info.getTradeTime().isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); fundFlow.setTransactionDate(sdf.parse(info.getTradeTime())); } else if (info.getTradeDate() != null && !info.getTradeDate().isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); fundFlow.setTransactionDate(sdf.parse(info.getTradeDate())); } } catch (Exception e) { logger.warn("解析交易日期失败: tradeDate={}, tradeTime={}", info.getTradeDate(), info.getTradeTime()); } // 起息日期 try { if (info.getValueDate() != null && !info.getValueDate().isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); fundFlow.setValueDate(sdf.parse(info.getValueDate())); } } catch (Exception e) { logger.warn("解析起息日期失败: {}", info.getValueDate()); } // 记账日期 try { if (info.getPostdate() != null && !info.getPostdate().isEmpty()) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); fundFlow.setPostdate(sdf.parse(info.getPostdate())); } } catch (Exception e) { logger.warn("解析记账日期失败: {}", info.getPostdate()); } // 状态:默认设置为"0"正常(草稿) fundFlow.setStatus("0"); // 删除标志 fundFlow.setDelFlag("0"); return fundFlow; } /** * 生成请求流水号:10位商户号 + 14位时间 + 8位序号 */ private String generateReqSeq() { String merchantNum = "zjjr"; SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String dateTime = sdf.format(new Date()); String seq = String.format("%08d", (int) (Math.random() * 100000000)); return merchantNum + dateTime + seq; } }