Files
dzpt/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/BankGetDataMethodByJHLZF.java

1481 lines
77 KiB
Java
Raw Normal View History

2025-07-23 09:55:50 +08:00
package com.saye.hospitalgd.scheduler.jobMethod;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.saye.hospitalgd.commons.date.DateDUtil;
import com.saye.hospitalgd.commons.getBean.GetBeanUtil;
import com.saye.hospitalgd.commons.log.LogUtil;
import com.saye.hospitalgd.commons.string.StringDUtil;
2025-07-31 15:27:47 +08:00
import com.saye.hospitalgd.entity.BankbillHistory;
2025-07-23 09:55:50 +08:00
import com.saye.hospitalgd.service.BankbillGetinfoService;
2025-07-31 15:27:47 +08:00
import com.saye.hospitalgd.service.BankbillHistoryService;
2025-07-23 09:55:50 +08:00
import com.saye.hospitalgd.service.ThirdSftpConfigService;
import com.saye.hospitalgd.service.impl.BankbillGetinfoServiceImpl;
2025-07-31 15:27:47 +08:00
import com.saye.hospitalgd.service.impl.BankbillHistoryServiceImpl;
2025-07-23 09:55:50 +08:00
import com.saye.hospitalgd.service.impl.ThirdSftpConfigServiceImpl;
import com.saye.hospitalgd.service.system.ServiceParamsService;
import com.saye.hospitalgd.service.system.impl.ServiceParamsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
2025-10-30 17:21:43 +08:00
import org.apache.http.HttpEntity;
2025-07-23 09:55:50 +08:00
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.CollectionUtils;
2025-10-30 17:21:43 +08:00
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
2025-07-23 09:55:50 +08:00
import java.io.*;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* @author thuang
* @version 1.0
* @description: 获取商户POS通对账单 调用dmz服务器部署的接口
* @date 2021/9/13 14:46
*/
@Slf4j
2025-07-31 15:27:47 +08:00
public class BankGetDataMethodByJHLZF {
2025-07-23 09:55:50 +08:00
public HashMap<Object, Object> getDate(String id, String name, String trade_date, HashMap<Object, Object> map) {
HashMap<Object, Object> resultMap = new HashMap<>();
String errCode = "0";
String errMsg = "";
2025-07-31 15:27:47 +08:00
BankbillHistoryService bankbillHistoryService = GetBeanUtil.getBean(BankbillHistoryServiceImpl.class);
2025-07-23 09:55:50 +08:00
BankbillGetinfoService bankbillGetinfoService = GetBeanUtil.getBean(BankbillGetinfoServiceImpl.class);
ServiceParamsService serviceParamsService = GetBeanUtil.getBean(ServiceParamsServiceImpl.class);
ThirdSftpConfigService thirdSftpConfigService = GetBeanUtil.getBean(ThirdSftpConfigServiceImpl.class);
boolean isOk = false;
//重新执行最多10次 还没获取到肯定有问题 不执行了
//如果有传入时间 那就只执行一次 获取不到就还是获取不到
int num = "".equals(trade_date) ? 0 : 9;
while (!isOk && num < 10) {
num++;
String dateStr = "";
//如果有传入时间 那就使用传入时间
if ("".equals(trade_date)) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE, -1);
Date time = calendar.getTime();
trade_date = DateDUtil.DateToStr(DateDUtil.yyyy_MM_dd, time);
dateStr = DateDUtil.DateToStr(DateDUtil.yyyyMMdd, time);
} else {
Date date = DateDUtil.strToDate(DateDUtil.yyyy_MM_dd, trade_date);
dateStr = DateDUtil.DateToStr(DateDUtil.yyyyMMdd, date);
}
//开始请求海南外联接口,生成账单文件
String cust_id = StringDUtil.changeNullToEmpty(map.get("CUST_ID"));
String user_id = StringDUtil.changeNullToEmpty(map.get("USER_ID"));
String tx_code = StringDUtil.changeNullToEmpty(map.get("TX_CODE"));
String password = StringDUtil.changeNullToEmpty(map.get("PASSWORD"));
String language = StringDUtil.changeNullToEmpty(map.get("LANGUAGE"));
String wlip = StringDUtil.changeNullToEmpty(map.get("WLIP"));
String wlport = StringDUtil.changeNullToEmpty(map.get("WLPORT"));
2025-10-30 17:21:43 +08:00
// 验证和清理XML字段防止XML解析错误
if (language == null || language.trim().isEmpty()) {
language = "CN"; // 默认值
} else {
// 清理可能导致XML格式错误的字符
language = language.replaceAll("[<>&\"']", "").trim();
if (language.isEmpty()) {
language = "CN";
}
}
// 清理其他可能包含特殊字符的字段
cust_id = cust_id.replaceAll("[<>&\"']", "");
user_id = user_id.replaceAll("[<>&\"']", "");
tx_code = tx_code.replaceAll("[<>&\"']", "");
password = password.replaceAll("[<>&\"']", "");
2025-07-23 09:55:50 +08:00
String requset_sn_sc = String.valueOf(System.currentTimeMillis());
log.info("传去的时间是dateStr" + dateStr);
String requestXml = "<?xml version=\"1.0\" encoding=\"GB2312\" standalone=\"yes\" ?> \n" +
"<TX> \n" +
" <REQUEST_SN>" + requset_sn_sc + "</REQUEST_SN> \n" +
" <CUST_ID>" + cust_id + "</CUST_ID> \n" +
" <USER_ID>" + user_id + "</USER_ID> \n" +
" <PASSWORD>" + password + "</PASSWORD> \n" +
" <TX_CODE>" + tx_code + "</TX_CODE> \n" +
" <LANGUAGE>" + language + "</LANGUAGE> \n" +
" <TX_INFO> \n" +
" <DATE>" + dateStr + "</DATE> \n" +
" <KIND>1</KIND> \n" +
" <FILETYPE>1</FILETYPE> \n" +
" <TYPE></TYPE> \n" +
" <NORDERBY></NORDERBY> \n" +
" <POS_CODE></POS_CODE> \n" +
" <ORDER></ORDER> \n" +
" <STATUS></STATUS> \n" +
" <BILL_FLAG></BILL_FLAG> \n" +
" <Mrch_No></Mrch_No> \n" +
" <GROUP_FLAG></GROUP_FLAG> \n" +
" <TXN_TPCD></TXN_TPCD> \n" +
" </TX_INFO> \n" +
"</TX> ";
String params = "requestXml=" + requestXml;
String ipAdress = wlip + ":" + wlport;
log.info("拼接的ip是" + ipAdress);
String fileName = "";
try {
URI uri = new URI(ipAdress.trim());
CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(uri);
httpPost.setHeader("content-type", "application/x-www-form-urlencoded");
httpPost.setHeader("Connection", "close");
StringEntity entity = new StringEntity(params, "GB18030");
log.info("发送的数据是entity" + entity);
httpPost.setEntity(entity);
CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
2025-10-30 17:21:43 +08:00
// 增强响应读取,防止截断
String s;
try {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
long contentLength = responseEntity.getContentLength();
log.info("主接口响应内容长度: " + contentLength);
// 使用流式读取,避免截断
try (InputStream inputStream = responseEntity.getContent();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024]; // 1KB缓冲区更小更精确
int bytesRead;
int totalBytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
// 获取原始字节数组
byte[] responseBytes = outputStream.toByteArray();
log.info("主接口流式读取完成,总字节数: " + totalBytesRead + ", 字节数组长度: " + responseBytes.length);
// 使用ISO-8859-1编码确保字节数和字符数一致
s = new String(responseBytes, "ISO-8859-1");
log.info("主接口使用ISO-8859-1编码字符串长度: " + s.length());
// 检查是否可能被截断
if (contentLength > 0 && totalBytesRead < contentLength) {
log.warn("主接口响应可能被截断,期望长度: " + contentLength + ", 实际读取: " + totalBytesRead);
}
}
} else {
s = "";
log.warn("主接口响应实体为空");
}
} finally {
// 确保响应被完全消费
EntityUtils.consumeQuietly(response.getEntity());
}
2025-07-23 09:55:50 +08:00
log.info("接受到的数据是s" + s);
2025-10-30 17:21:43 +08:00
// 增强XML格式修复和验证
Document document;
try {
// 首先清理响应数据移除可能的非XML内容
s = cleanXmlResponse(s);
// 尝试解析XML
document = DocumentHelper.parseText(s);
} catch (Exception e) {
log.error("XML解析错误原始响应: " + s, e);
try {
// 尝试修复常见的XML格式问题
s = fixXmlFormat(s);
log.info("修复后的XML: " + s);
document = DocumentHelper.parseText(s);
} catch (Exception e2) {
log.error("XML修复后仍然解析失败", e2);
throw new RuntimeException("XML解析失败无法处理响应数据: " + s, e2);
}
}
2025-07-23 09:55:50 +08:00
Iterator iterator = document.nodeIterator();
Element tx = (Element) iterator.next();
Element return_code_F = tx.element("RETURN_CODE");
Element return_msg_F = tx.element("RETURN_MSG");
if (return_code_F != null) {
if (!return_code_F.getText().equals("000000")) {
errCode = "999";
errMsg = return_msg_F.getText();
log.info("获取到的返回code是" + return_code_F);
} else {
Iterator tx_info = tx.elementIterator("TX_INFO");
Element txInfo = (Element) tx_info.next();
Element file_name = txInfo.element("FILE_NAME");
fileName = file_name.getText();
boolean exist = FileUtil.exist("C:" + File.separator + "CCB_EBSClient" + File.separator + "download" + File.separator + fileName);
Boolean fileIsExist = false;
if (!exist) { //文件不存在开始下载
2025-10-30 17:21:43 +08:00
log.info("文件不存在,开始下载: " + fileName);
2025-07-23 09:55:50 +08:00
HashMap<Object, Object> searchMap = new HashMap<>();
searchMap.put("FUBS", "2");
List<HashMap<Object, Object>> wlConfigList = thirdSftpConfigService.findWLIF(searchMap);
HashMap<Object, Object> configMap = wlConfigList.get(0);
String txCode = StringDUtil.changeNullToEmpty(configMap.get("TX_CODE"));
String filepath = StringDUtil.changeNullToEmpty(configMap.get("FILEPATH"));
String requset_sn_xz = String.valueOf(System.currentTimeMillis());
String re = "<?xml version=\"1.0\" encoding=\"GB2312\" standalone=\"yes\" ?> \n" +
"<TX> \n" +
" <REQUEST_SN>" + requset_sn_xz + "</REQUEST_SN> \n" +
" <CUST_ID>" + cust_id + "</CUST_ID> \n" +
" <USER_ID>" + user_id + "</USER_ID> \n" +
" <PASSWORD>" + password + "</PASSWORD> \n" +
" <TX_CODE>" + txCode + "</TX_CODE> \n" +
" <LANGUAGE>CN</LANGUAGE> \n" +
" <TX_INFO> \n" +
" <SOURCE>" + fileName + "</SOURCE> \n" +
" <FILEPATH>" + filepath + "</FILEPATH> \n" +
" <LOCAL_REMOTE>0</LOCAL_REMOTE> \n" +
" </TX_INFO> \n" +
"</TX> \n";
String ps = "requestXml=" + re;
Element return_code = null;
Element return_msg = null;
URI uri_d = new URI(ipAdress.trim());
CloseableHttpClient closeableHttpClientD = HttpClients.createDefault();
HttpPost httpPostD = new HttpPost(uri_d);
httpPostD.setHeader("content-type", "application/x-www-form-urlencoded");
httpPostD.setHeader("Connection", "close");
StringEntity entityD = new StringEntity(ps, "GB18030");
2025-10-30 17:21:43 +08:00
httpPostD.setEntity(entityD);
CloseableHttpResponse responseD = closeableHttpClientD.execute(httpPostD);
// 增强响应读取,防止截断
String s_d;
try {
HttpEntity responseEntityD = responseD.getEntity();
if (responseEntityD != null) {
long contentLength = responseEntityD.getContentLength();
log.info("下载接口响应内容长度: " + contentLength);
// 使用流式读取,避免截断
try (InputStream inputStream = responseEntityD.getContent();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024]; // 1KB缓冲区更小更精确
int bytesRead;
int totalBytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
log.debug("读取了 " + bytesRead + " 字节,累计: " + totalBytesRead);
}
// 获取原始字节数组
byte[] responseBytes = outputStream.toByteArray();
log.info("流式读取完成,总字节数: " + totalBytesRead + ", 字节数组长度: " + responseBytes.length);
// 首先检查十六进制模式,确定是否需要字节级修复
StringBuilder hexCheck = new StringBuilder();
for (int i = Math.max(0, responseBytes.length - 20); i < responseBytes.length; i++) {
hexCheck.append(String.format("%02X ", responseBytes[i]));
}
String hexStr = hexCheck.toString();
log.info("响应尾部20字节(HEX): " + hexStr);
// 检查是否存在LANGUAGE标签截断的十六进制模式
// </LANG = 3C 2F 4C 41 4E 47
// 完整的</LANGUAGE> = 3C 2F 4C 41 4E 47 55 41 47 45 3E
if (hexStr.contains("3C 2F 4C 41 4E 47") && !hexStr.contains("3C 2F 4C 41 4E 47 55 41 47 45 3E")) {
log.warn("通过十六进制检测到LANGUAGE标签截断进行字节级修复");
s_d = fixLanguageTagFromBytes(responseBytes);
log.info("字节级修复完成,新字符串长度: " + s_d.length());
} else {
// 没有截断直接使用ISO-8859-1编码字节数和字符数一致
s_d = new String(responseBytes, "ISO-8859-1");
log.info("使用ISO-8859-1编码字符串长度: " + s_d.length());
// 再次检查字符串内容是否有截断
if (s_d.contains("<LANGUAGE>CN</LANG") && !s_d.contains("</LANGUAGE>")) {
log.warn("字符串检测到LANGUAGE标签截断进行字节级修复");
s_d = fixLanguageTagFromBytes(responseBytes);
log.info("字符串检测修复完成,新字符串长度: " + s_d.length());
}
}
log.info("最终字符串长度: " + s_d.length());
// 验证修复结果
if (s_d.contains("<LANGUAGE>CN</LANGUAGE>") && s_d.contains("</TX>")) {
log.info("XML修复成功包含完整的LANGUAGE标签和TX根元素");
} else if (s_d.contains("<LANGUAGE>CN</LANG") && !s_d.contains("</LANGUAGE>")) {
log.error("XML修复失败LANGUAGE标签仍然截断");
// 最后尝试字符串级别的修复
s_d = s_d.replace("<LANGUAGE>CN</LANG", "<LANGUAGE>CN</LANGUAGE>");
if (!s_d.contains("</TX>")) {
s_d = s_d + "</TX>";
}
log.info("应急字符串修复完成");
}
// 检查是否可能被截断
if (contentLength > 0 && totalBytesRead < contentLength) {
log.warn("响应可能被截断,期望长度: " + contentLength + ", 实际读取: " + totalBytesRead);
}
}
} else {
s_d = "";
log.warn("下载接口响应实体为空");
}
} finally {
// 确保响应被完全消费
EntityUtils.consumeQuietly(responseD.getEntity());
}
log.info("下载接口响应: " + s_d);
// 增强XML格式修复和验证
Document documentD;
try {
// 首先清理响应数据移除可能的非XML内容
s_d = cleanXmlResponse(s_d);
2025-07-23 09:55:50 +08:00
2025-10-30 17:21:43 +08:00
// 尝试解析XML
documentD = DocumentHelper.parseText(s_d);
} catch (Exception e) {
log.error("下载XML解析错误原始响应: " + s_d, e);
try {
// 尝试修复常见的XML格式问题
s_d = fixXmlFormat(s_d);
log.info("修复后的下载XML: " + s_d);
documentD = DocumentHelper.parseText(s_d);
} catch (Exception e2) {
log.error("下载XML修复后仍然解析失败", e2);
throw new RuntimeException("下载XML解析失败无法处理响应数据: " + s_d, e2);
}
}
2025-07-23 09:55:50 +08:00
Iterator iteratorD = documentD.nodeIterator();
Element txD = (Element) iteratorD.next();
return_code = txD.element("RETURN_CODE");
return_msg = txD.element("RETURN_MSG");
if (return_code != null) {
if (!return_code.getText().equals("000000")) {
errCode = "999";
2025-10-30 17:21:43 +08:00
errMsg = "文件下载失败: " + return_msg.getText();
log.error("文件下载失败,返回码: " + return_code.getText() + ", 错误信息: " + return_msg.getText());
} else {
log.info("下载接口返回成功准备从中专服务器下载ZIP文件...");
// 调用外联平台下载接口成功后从中专服务器下载ZIP文件到本地
try {
log.info("准备调用中专服务器下载接口获取ZIP文件: " + fileName);
boolean downloadSuccess = downloadZipFromIntermediaryServer(fileName, serviceParamsService);
if (downloadSuccess) {
log.info("从中专服务器下载ZIP文件成功: " + fileName);
fileIsExist = true; // 标记文件已存在
} else {
log.warn("从中专服务器下载ZIP文件失败等待本地文件生成: " + fileName);
// 如果中专服务器下载失败,等待本地文件生成
fileIsExist = waitForFileGeneration(fileName, 60);
if (!fileIsExist) {
errCode = "999";
errMsg = "文件下载超时,文件未生成: " + fileName;
log.error("文件下载超时: " + fileName);
}
}
} catch (Exception downloadEx) {
log.error("调用中专服务器下载接口异常,等待本地文件生成: " + fileName, downloadEx);
// 如果中专服务器下载异常,等待本地文件生成
fileIsExist = waitForFileGeneration(fileName, 60);
if (!fileIsExist) {
errCode = "999";
errMsg = "文件下载超时,文件未生成: " + fileName;
log.error("文件下载超时: " + fileName);
}
}
2025-07-23 09:55:50 +08:00
}
}
}
if (fileIsExist || exist) { //确定文件存在,开始读取数据
String filePath = "C:" + File.separator + "CCB_EBSClient" + File.separator + "download" + File.separator + fileName;
2025-10-30 17:21:43 +08:00
log.info("开始读取文件: " + filePath);
// 再次验证文件是否真实存在且可读
File file = new File(filePath);
if (!file.exists()) {
errCode = "999";
errMsg = "文件不存在: " + filePath;
log.error("文件不存在: " + filePath);
continue; // 继续下一次循环重试
}
if (!file.canRead()) {
errCode = "999";
errMsg = "文件无法读取: " + filePath;
log.error("文件无法读取: " + filePath);
continue; // 继续下一次循环重试
}
if (file.length() == 0) {
errCode = "999";
errMsg = "文件为空: " + filePath;
log.error("文件为空: " + filePath);
continue; // 继续下一次循环重试
}
2025-07-23 09:55:50 +08:00
2025-10-30 17:21:43 +08:00
log.info("文件验证通过,大小: " + file.length() + " 字节");
2025-07-23 09:55:50 +08:00
2025-10-30 17:21:43 +08:00
List<String> txtList = null;
FileInputStream stream = null;
ZipInputStream zipInputStream = null;
BufferedReader br = null;
try {
stream = new FileInputStream(filePath);
zipInputStream = new ZipInputStream(new BufferedInputStream(stream), Charset.forName("GBK"));
2025-07-23 09:55:50 +08:00
ZipEntry nextEntry = zipInputStream.getNextEntry();
2025-10-30 17:21:43 +08:00
if (nextEntry == null) {
throw new RuntimeException("ZIP文件中没有找到任何条目");
}
String entryName = nextEntry.getName();
log.info("读取的ZIP条目: " + entryName + ", 大小: " + nextEntry.getSize());
2025-07-23 09:55:50 +08:00
2025-10-30 17:21:43 +08:00
// 判断文件类型
if (entryName.toLowerCase().endsWith(".xlsx") || entryName.toLowerCase().endsWith(".xls")) {
log.info("检测到Excel文件使用POI解析: " + entryName);
txtList = parseExcelFromZip(zipInputStream, entryName);
log.info("成功从Excel解析 " + (txtList != null ? txtList.size() : 0) + " 行数据");
} else {
// 原来的文本文件解析逻辑
log.info("检测到文本文件,使用文本解析: " + entryName);
br = new BufferedReader(new InputStreamReader(zipInputStream, StandardCharsets.UTF_8));
2025-07-23 09:55:50 +08:00
String line;
txtList = new ArrayList<>();
2025-10-30 17:21:43 +08:00
int lineCount = 0;
2025-07-23 09:55:50 +08:00
//内容不为空,输出
while ((line = br.readLine()) != null) {
txtList.add(line);
2025-10-30 17:21:43 +08:00
lineCount++;
}
log.info("成功读取 " + lineCount + " 行数据");
}
} catch (Exception e) {
log.error("文件读取失败: " + filePath, e);
errCode = "999";
errMsg = "文件读取失败: " + e.getMessage();
continue; // 继续下一次循环重试
} finally {
// 确保资源正确关闭
try {
if (br != null) br.close();
if (zipInputStream != null) zipInputStream.close();
if (stream != null) stream.close();
} catch (Exception e) {
log.warn("关闭文件流时出错", e);
}
2025-07-23 09:55:50 +08:00
}
log.info("继续执行程序3");
2025-07-31 15:27:47 +08:00
List<BankbillHistory> bankbillHistoryList = new ArrayList<>();
2025-10-30 17:21:43 +08:00
// 跳过前24行Excel的前24行是标题和说明第24行是表头从第25行开始是数据
for (int i = 24; i < txtList.size(); i++) {
try {
2025-07-23 09:55:50 +08:00
String d = txtList.get(i);
2025-10-30 17:21:43 +08:00
// 跳过空行
if (d == null || d.trim().isEmpty()) {
continue;
}
2025-07-23 09:55:50 +08:00
String[] s1 = d.split("\t");
2025-10-30 17:21:43 +08:00
// 跳过列数不足的行至少需要10列才能有交易金额
if (s1.length < 10) {
log.debug("" + (i + 1) + " 行列数不足,跳过");
2025-07-23 09:55:50 +08:00
continue;
}
2025-10-30 17:21:43 +08:00
// 检查是否是汇总行(包含"小计"、"合计"、"终端小计"等关键字)
String rowText = d.toLowerCase();
if (rowText.contains("小计") || rowText.contains("合计") ||
rowText.contains("终端小计") || rowText.contains("pos编号") ||
rowText.contains("笔数") ||
s1.length > 6 && (s1[6].contains("小计") || s1[6].contains("合计"))) {
log.debug("" + (i + 1) + " 行是汇总行,跳过: " + (s1.length > 6 ? s1[6] : ""));
continue;
}
// 检查第13列交易日期Excel的N列是否为空
if (s1.length <= 13 || s1[13] == null || s1[13].trim().isEmpty()) {
log.debug("" + (i + 1) + " 行交易日期为空,跳过");
continue;
}
String jyrq = s1[13].trim();
// 检查第13列是否包含日期简单检查是否包含"-"
if (!jyrq.contains("-")) {
log.debug("" + (i + 1) + " 行交易日期不包含日期分隔符,跳过");
continue;
}
// 检查交易金额列第20列对应Excel的U列是否有效
String amountStr = s1.length > 20 ? s1[20].trim() : "";
if (amountStr.isEmpty() || amountStr.equals("null") || amountStr.equals("0") || amountStr.equals("0.00")) {
log.debug("" + (i + 1) + " 行交易金额无效: [" + amountStr + "],跳过");
continue;
}
// 尝试解析金额,确保是有效数字
try {
double amount = Double.parseDouble(amountStr);
if (Math.abs(amount) < 0.01) {
log.debug("" + (i + 1) + " 行交易金额太小: " + amount + ",跳过");
continue;
}
} catch (NumberFormatException e) {
log.debug("" + (i + 1) + " 行交易金额不是有效数字: [" + amountStr + "],跳过");
continue;
}
log.info("正在处理第 " + (i + 1) + " 行数据");
2025-07-23 09:55:50 +08:00
2025-07-31 15:27:47 +08:00
BankbillHistory bankbillHistory = new BankbillHistory();
2025-07-23 09:55:50 +08:00
2025-10-30 17:21:43 +08:00
// 根据你提供的Excel列位置映射
// 终端号: CD列(索引2-3), 发卡行: EFGH列(索引4-7), 卡种: IJ列(索引8-9),
// 卡号: KLM列(索引10-12), 交易日期: N列(索引13), 交易时间: P列(索引15),
// 交易类型: QR列(索引16-17), 授权号: ST列(索引18-19), 交易金额: U列(索引20)⭐,
// 小费: VW列(索引21-22), 分期期数: X列(索引23), 银行手续费: Y列(索引24),
// DCC返还手续费: Z列(索引25), 划账金额: AA列(索引26), 凭证号: AB列(索引27),
// 批次号: AC列(索引28), POS交易序号: AD列(索引29)⭐, 结算账号: AE列(索引30),
// 订单号: AF列(索引31), 柜台编号: AG列(索引32), 系统参考号: AH列(索引33),
// 持卡人姓名: AI列(索引34), 付款凭证号: AJ列(索引35), 备注1: AK列(索引36), 备注2: AL列(索引37)
// 交易日期和时间
jyrq = s1.length > 13 ? s1[13] : "";
String jysj = s1.length > 15 ? s1[15] : "";
// 如果交易时间为空,尝试从交易日期中提取时间部分
if (jysj == null || jysj.trim().isEmpty()) {
// 检查交易日期是否包含时间格式如2025-10-23 14:30:15
if (jyrq != null && jyrq.contains(" ")) {
String[] dateTimeParts = jyrq.split(" ", 2);
if (dateTimeParts.length == 2) {
jyrq = dateTimeParts[0]; // 日期部分
jysj = dateTimeParts[1]; // 时间部分
}
} else {
// 如果还是空,设置默认值
jysj = "00:00:00";
}
}
bankbillHistory.setCJyrq(jyrq); // N列(索引13): 交易日期
bankbillHistory.setCJysj(jysj); // P列(索引15): 交易时间
bankbillHistory.setCJyje(s1.length > 20 ? s1[20] : "0"); // U列(索引20): 交易金额
// QR列(索引16): 支付方式Excel中就是微信支付、支付宝等
String zffs = s1.length > 16 ? s1[16].trim() : "建行龙支付";
if (zffs.isEmpty()) {
zffs = "建行龙支付"; // 默认值
}
bankbillHistory.setCZffs(zffs); // 支付方式直接从Excel读取
// 交易类型:根据交易金额正负判断
String jylx = "消费"; // 默认交易类型
try {
double amount = Double.parseDouble(s1.length > 20 ? s1[20] : "0");
if (amount < 0) {
jylx = "退款";
}
} catch (Exception e) {
// 解析失败,使用默认值
}
bankbillHistory.setCJylx(jylx); // 交易类型(根据金额正负判断)
// AD列(索引29): POS交易序号 (作为流水号和银商订单号)
bankbillHistory.setCLsh(s1.length > 29 ? s1[29] : ""); // 流水号
bankbillHistory.setCYsddh(s1.length > 29 ? s1[29] : ""); // 银商订单号
// AF列(索引31): 订单号 (作为商户订单号)
bankbillHistory.setCShddh(s1.length > 31 ? s1[31] : ""); // 商户订单号
// AH列(索引33): 系统参考号
bankbillHistory.setCCkh(s1.length > 33 ? s1[33] : ""); // 参考号
// KLM列(索引10): 卡号-序列号
bankbillHistory.setCCard(s1.length > 10 ? s1[10] : ""); // 卡号
// IJ列(索引8): 卡种
bankbillHistory.setCKlx(s1.length > 8 ? s1[8] : ""); // 卡类型
// EFGH列(索引4): 发卡行
bankbillHistory.setCFkh(s1.length > 4 ? s1[4] : ""); // 发卡行
2025-11-03 11:42:55 +08:00
// 支付方式已经在前面设置从第16列读取这里不再覆盖
2025-10-30 17:21:43 +08:00
// VW列(索引21): 小费
bankbillHistory.setCSxf(s1.length > 21 ? s1[21] : "0"); // 小费
// X列(索引23): 分期期数
bankbillHistory.setCFqqs(s1.length > 23 ? s1[23] : ""); // 分期期数
// Y列(索引24): 银行手续费
bankbillHistory.setCFqsxf(s1.length > 24 ? s1[24] : "0"); // 分期手续费
// AA列(索引26): 划账金额 (作为清算金额和实际支付金额)
String haje = s1.length > 26 ? s1[26] : (s1.length > 20 ? s1[20] : "0");
bankbillHistory.setCQsje(haje); // 清算金额
bankbillHistory.setCSjzfje(haje); // 实际支付金额
// 清算日期 = 交易日期
bankbillHistory.setCQsrq(s1.length > 13 ? s1[13] : ""); // 清算日期
// 备注字段 (AK列:备注1, AL列:备注2)
String bz = "";
if (s1.length > 36 && s1[36] != null && !s1[36].trim().isEmpty()) {
bz = s1[36];
}
if (s1.length > 37 && s1[37] != null && !s1[37].trim().isEmpty()) {
bz += (bz.isEmpty() ? "" : ";") + s1[37];
}
bankbillHistory.setCBzzd(bz); // 备注字段
// 其他固定字段
bankbillHistory.setCQbyhje("0"); // 钱包优惠金额
bankbillHistory.setCShyhje("0"); // 商户优惠金额
bankbillHistory.setCYjylsh(""); // 原交易流水号(空)
bankbillHistory.setCFqfwf(""); // 分期服务方(空)
bankbillHistory.setCFqfxf(""); // 分期付息方(空)
bankbillHistory.setCQtyhje("0"); // 其他优惠金额
bankbillHistory.setCThddh(""); // 退货订单号(空)
bankbillHistory.setCFkfy(""); // 付款附言(空)
bankbillHistory.setCFdjc(""); // 分店简称(空)
bankbillHistory.setCZddh(""); // 子订单号(空)
bankbillHistory.setBillTableName("建行龙支付对账单"); // 对账表名
2025-07-31 15:27:47 +08:00
bankbillHistoryList.add(bankbillHistory);
2025-10-30 17:21:43 +08:00
log.info("成功解析第 " + (i + 1) + " 行数据");
} catch (Exception e) {
log.error("处理第 " + (i + 1) + " 行数据时出错,跳过该行", e);
continue;
}
2025-07-23 09:55:50 +08:00
}
2025-07-31 15:27:47 +08:00
if (!CollectionUtils.isEmpty(bankbillHistoryList)) {
2025-10-30 17:21:43 +08:00
log.info("成功解析 " + bankbillHistoryList.size() + " 条建行龙支付对账数据");
2025-07-31 15:27:47 +08:00
// 先存一份原始的 这份只是留底查询
bankbillHistoryService.insertBankBillOriginal(bankbillHistoryList, trade_date, "建行龙支付对账单");
2025-10-30 17:21:43 +08:00
log.info("已保存原始对账数据到数据库");
2025-07-31 15:27:47 +08:00
// 再存一份修改的用于对账
bankbillHistoryService.insertAllBankHistory(bankbillHistoryList, trade_date, "建行龙支付对账单");
2025-10-30 17:21:43 +08:00
log.info("已保存对账数据到对账表");
2025-07-23 09:55:50 +08:00
isOk = true;
} else {
errCode = "999";
2025-10-30 17:21:43 +08:00
errMsg = "未查询到有效数据(可能是文件格式不正确)";
log.error("解析建行龙支付对账单失败: " + errMsg);
2025-07-23 09:55:50 +08:00
isOk = false;
}
}
}
}
} catch (Exception e) {
errCode = "999";
if (StringUtils.isEmpty(errMsg)) {
errMsg = "外联平台接口调用失败!!!";
}
e.printStackTrace();
}
//海南外联接口调用成功后会生成账单文件,在请求一次外联接口下载到服务器。
log.info("继续执行程序4");
//记录数据获取记录
HashMap<Object, Object> addMap = new HashMap<>();
addMap.put("trade_date", trade_date);
addMap.put("quartz_id", id);
addMap.put("quartz_name", name);
2025-10-30 17:21:43 +08:00
addMap.put("bill_table_name", "建行龙支付对账单");
2025-07-31 15:27:47 +08:00
addMap.put("thirdConfigId", "999");
2025-07-23 09:55:50 +08:00
if (isOk) {
addMap.put("is_ok", "1");
} else {
addMap.put("is_ok", "0");
}
addMap.put("modify_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
bankbillGetinfoService.insertBankbillGetinfo(addMap);
//判断是否成功 如果失败 执行休眠一小时
if (!isOk && num < 10) {
try {
//记录日志
LogUtil.error(this.getClass(), errMsg);
System.out.println("获取对账记录没有成功1小时后重新执行");
Thread.sleep(1000 * 60 * 60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
resultMap.put("errCode", errCode);
resultMap.put("errMsg", errMsg);
return resultMap;
}
2025-10-30 17:21:43 +08:00
/**
* 清理XML响应数据移除可能的非XML内容
*/
private String cleanXmlResponse(String response) {
if (response == null || response.trim().isEmpty()) {
return response;
}
// 移除BOM标记
if (response.startsWith("\uFEFF")) {
response = response.substring(1);
}
// 查找XML声明的开始位置
int xmlStart = response.indexOf("<?xml");
if (xmlStart > 0) {
response = response.substring(xmlStart);
}
// 如果没有XML声明查找根元素开始位置
if (xmlStart == -1) {
int rootStart = response.indexOf("<TX>");
if (rootStart > 0) {
response = response.substring(rootStart);
}
}
// 移除可能的尾部非XML内容
int lastTagEnd = response.lastIndexOf(">");
if (lastTagEnd > 0 && lastTagEnd < response.length() - 1) {
String afterLastTag = response.substring(lastTagEnd + 1).trim();
// 如果最后一个标签后还有非空白字符,可能是垃圾数据
if (!afterLastTag.isEmpty() && !afterLastTag.matches("\\s*")) {
response = response.substring(0, lastTagEnd + 1);
}
}
return response.trim();
}
/**
* 修复常见的XML格式问题
*/
private String fixXmlFormat(String xml) {
if (xml == null || xml.trim().isEmpty()) {
return xml;
}
log.info("开始修复XML原始长度: " + xml.length());
// 确保有XML声明
if (!xml.trim().startsWith("<?xml")) {
xml = "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n" + xml;
}
// 处理严重截断的情况 - 检查XML是否在标签中间被截断
if (xml.endsWith("<") || xml.matches(".*<[^>]*$")) {
log.warn("检测到XML在标签中被截断尝试修复");
// 移除最后一个不完整的标签
int lastLessThan = xml.lastIndexOf('<');
if (lastLessThan > 0) {
xml = xml.substring(0, lastLessThan);
log.info("移除不完整标签后的XML: " + xml);
}
}
// 特殊处理:检测<LANGUAGE>CN</LANG这种特定截断模式
if (xml.contains("<LANGUAGE>CN</LANG") && !xml.contains("</LANGUAGE>")) {
log.warn("检测到LANGUAGE标签特定截断模式");
// 直接替换为完整的标签
xml = xml.replaceAll("<LANGUAGE>CN</LANG[^>]*$", "<LANGUAGE>CN</LANGUAGE>");
xml = xml.replaceAll("<LANGUAGE>CN</LANG(?![A-Z])", "<LANGUAGE>CN</LANGUAGE>");
log.info("修复LANGUAGE标签截断");
}
// 处理XML结尾被截断的情况
if (xml.endsWith("<LANGUAGE>CN</LANG")) {
log.warn("检测到XML结尾LANGUAGE标签截断");
xml = xml.replace("<LANGUAGE>CN</LANG", "<LANGUAGE>CN</LANGUAGE>");
log.info("修复XML结尾LANGUAGE标签");
}
// 处理XML在LANGUAGE标签中间被截断的情况
if (xml.endsWith("<LANGUAGE>") || xml.endsWith("<LANGUAGE>CN") || xml.endsWith("<LANGUAGE>CN<")) {
log.warn("检测到LANGUAGE标签内容截断");
if (xml.endsWith("<LANGUAGE>")) {
xml = xml.replace("<LANGUAGE>", "<LANGUAGE>CN</LANGUAGE>");
} else if (xml.endsWith("<LANGUAGE>CN")) {
xml = xml.replace("<LANGUAGE>CN", "<LANGUAGE>CN</LANGUAGE>");
} else if (xml.endsWith("<LANGUAGE>CN<")) {
xml = xml.replace("<LANGUAGE>CN<", "<LANGUAGE>CN</LANGUAGE>");
}
log.info("修复LANGUAGE标签内容截断");
}
// 处理标签名被截断的情况,如 </LANG 应该是 </LANGUAGE>
xml = xml.replaceAll("</LANG(?!UAGE)", "</LANGUAGE>");
xml = xml.replaceAll("<LANGUAGE([^>]*)>([^<]*)</LANG(?!UAGE)", "<LANGUAGE$1>$2</LANGUAGE>");
// 修复LANGUAGE标签问题 - 处理各种截断情况
// 特殊处理如果发现CN</LANG模式说明LANGUAGE标签被截断
if (xml.contains("CN</LANG") && !xml.contains("CN</LANGUAGE>")) {
xml = xml.replaceAll("CN</LANG[^>]*", "CN</LANGUAGE>");
log.info("修复CN</LANG截断问题");
}
// 处理<LANGUAGE>CN</LANG这种截断
xml = xml.replaceAll("<LANGUAGE([^>]*)>CN</LANG[^>]*", "<LANGUAGE$1>CN</LANGUAGE>");
// 处理<LANGUAGE>...内容被截断的情况,保留已有内容
xml = xml.replaceAll("<LANGUAGE([^>]*)>([^<]*?)(?=<(?!/LANGUAGE))", "<LANGUAGE$1>$2</LANGUAGE>");
xml = xml.replaceAll("<LANGUAGE([^>]*)>([^<]*?)$", "<LANGUAGE$1>$2</LANGUAGE>");
// 特殊处理如果LANGUAGE标签内容为空但原始响应中有CN尝试恢复
if (xml.contains("<LANGUAGE></LANGUAGE>") && xml.contains("CN")) {
// 查找CN在原始XML中的位置如果在LANGUAGE标签附近则恢复
String originalPart = xml.substring(Math.max(0, xml.indexOf("<LANGUAGE>") - 50),
Math.min(xml.length(), xml.indexOf("</LANGUAGE>") + 20));
if (originalPart.contains("CN")) {
xml = xml.replace("<LANGUAGE></LANGUAGE>", "<LANGUAGE>CN</LANGUAGE>");
log.info("恢复LANGUAGE标签中的CN内容");
}
}
// 修复其他可能未关闭的标签
String[] commonTags = {"REQUEST_SN", "CUST_ID", "USER_ID", "PASSWORD", "TX_CODE",
"RETURN_CODE", "RETURN_MSG", "FILE_NAME", "SOURCE", "FILEPATH", "TX_INFO"};
for (String tag : commonTags) {
// 修复未关闭的标签
xml = xml.replaceAll("<" + tag + "([^>]*)>([^<]*?)(?=<(?!/" + tag + "))",
"<" + tag + "$1>$2</" + tag + ">");
xml = xml.replaceAll("<" + tag + "([^>]*)>([^<]*?)$",
"<" + tag + "$1>$2</" + tag + ">");
}
// 移除重复的XML声明
String[] parts = xml.split("\\<\\?xml[^\\>]*\\?\\>");
if (parts.length > 2) {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");
for (int i = 1; i < parts.length; i++) {
sb.append(parts[i]);
}
xml = sb.toString();
}
// 确保根元素完整
if (!xml.contains("</TX>") && xml.contains("<TX>")) {
xml = xml + "</TX>";
}
// 最后的激进修复如果仍然有问题尝试重构XML
if (!xml.contains("</TX>") || xml.contains("<LANGUAGE>CN</LANG")) {
log.warn("尝试激进修复重构XML结构");
xml = reconstructXml(xml);
}
// 最后检查:如果仍然有未关闭的标签,尝试智能修复
if (xml.matches(".*<[^/>][^>]*$")) {
log.warn("仍有未关闭的标签,进行最终修复");
// 查找最后一个未关闭的标签
int lastOpenTag = xml.lastIndexOf('<');
if (lastOpenTag > 0) {
String beforeTag = xml.substring(0, lastOpenTag);
String tagPart = xml.substring(lastOpenTag);
// 如果是开始标签但没有结束,尝试关闭它
if (tagPart.matches("<[A-Z_]+[^>]*") && !tagPart.startsWith("</")) {
String tagName = tagPart.replaceAll("<([A-Z_]+).*", "$1");
xml = beforeTag + "<" + tagName + "></" + tagName + ">";
log.info("智能修复标签: " + tagName);
}
}
}
log.info("XML修复完成修复后长度: " + xml.length());
return xml;
}
/**
* 重构XML结构 - 激进修复方法
*/
private String reconstructXml(String xml) {
log.info("开始重构XML结构");
// 提取关键信息
String requestSn = extractValue(xml, "REQUEST_SN");
String custId = extractValue(xml, "CUST_ID");
String txCode = extractValue(xml, "TX_CODE");
String returnCode = extractValue(xml, "RETURN_CODE");
String returnMsg = extractValue(xml, "RETURN_MSG");
String language = "CN"; // 默认值
// 如果能找到LANGUAGE标签的内容使用它
String langValue = extractValue(xml, "LANGUAGE");
if (langValue != null && !langValue.isEmpty()) {
language = langValue;
}
// 重构完整的XML
StringBuilder reconstructed = new StringBuilder();
reconstructed.append("<?xml version=\"1.0\" encoding=\"GB18030\"?>\n");
reconstructed.append("<TX>");
if (requestSn != null) reconstructed.append("<REQUEST_SN>").append(requestSn).append("</REQUEST_SN>");
if (custId != null) reconstructed.append("<CUST_ID>").append(custId).append("</CUST_ID>");
if (txCode != null) reconstructed.append("<TX_CODE>").append(txCode).append("</TX_CODE>");
if (returnCode != null) reconstructed.append("<RETURN_CODE>").append(returnCode).append("</RETURN_CODE>");
if (returnMsg != null) reconstructed.append("<RETURN_MSG>").append(returnMsg).append("</RETURN_MSG>");
reconstructed.append("<LANGUAGE>").append(language).append("</LANGUAGE>");
reconstructed.append("</TX>");
String result = reconstructed.toString();
log.info("重构完成新XML长度: " + result.length());
return result;
}
/**
* 从XML中提取指定标签的值
*/
private String extractValue(String xml, String tagName) {
try {
String pattern = "<" + tagName + "([^>]*)>([^<]*?)(?=<|$)";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
java.util.regex.Matcher m = p.matcher(xml);
if (m.find()) {
String value = m.group(2);
log.debug("提取 " + tagName + ": " + value);
return value;
}
} catch (Exception e) {
log.warn("提取 " + tagName + " 失败: " + e.getMessage());
}
return null;
}
/**
* 专门修复LANGUAGE标签截断的字节级方法
*/
private String fixLanguageTagFromBytes(byte[] responseBytes) {
log.info("开始字节级修复LANGUAGE标签原始字节数: " + responseBytes.length);
// 输出最后20个字节的十六进制用于调试
StringBuilder debugHex = new StringBuilder();
for (int i = Math.max(0, responseBytes.length - 20); i < responseBytes.length; i++) {
debugHex.append(String.format("%02X ", responseBytes[i]));
}
log.info("待修复的尾部字节: " + debugHex.toString());
// 查找 </LANG 的字节位置(从后往前找,更准确)
// </LANG = 3C 2F 4C 41 4E 47
byte[] langEndPattern = {0x3C, 0x2F, 0x4C, 0x41, 0x4E, 0x47};
int langEndPos = -1;
// 从后往前搜索,找到最后一个匹配位置
for (int i = responseBytes.length - langEndPattern.length; i >= 0; i--) {
boolean match = true;
for (int j = 0; j < langEndPattern.length; j++) {
if (responseBytes[i + j] != langEndPattern[j]) {
match = false;
break;
}
}
if (match) {
langEndPos = i;
log.info("找到</LANG位置: " + langEndPos + " (从字节 " + i + " 开始)");
break;
}
}
if (langEndPos >= 0) {
// 检查</LANG后面是否还有其他字节
int afterLangPos = langEndPos + langEndPattern.length;
log.info("</LANG后的位置: " + afterLangPos + ", 剩余字节数: " + (responseBytes.length - afterLangPos));
// 创建新的字节数组,确保有足够空间
byte[] newBytes = new byte[responseBytes.length + 10];
// 复制到</LANG结束位置
System.arraycopy(responseBytes, 0, newBytes, 0, afterLangPos);
int pos = afterLangPos;
// 插入缺失的UAGE>
newBytes[pos++] = 0x55; // U
newBytes[pos++] = 0x41; // A
newBytes[pos++] = 0x47; // G
newBytes[pos++] = 0x45; // E
newBytes[pos++] = 0x3E; // >
// 如果</LANG后面还有字节复制它们
if (afterLangPos < responseBytes.length) {
System.arraycopy(responseBytes, afterLangPos, newBytes, pos, responseBytes.length - afterLangPos);
pos += (responseBytes.length - afterLangPos);
log.info("复制了 " + (responseBytes.length - afterLangPos) + " 个剩余字节");
}
// 检查是否需要添加</TX>
String tempResult = new String(newBytes, 0, pos, java.nio.charset.StandardCharsets.ISO_8859_1);
if (!tempResult.contains("</TX>")) {
log.info("添加缺失的</TX>标签");
newBytes[pos++] = 0x3C; // <
newBytes[pos++] = 0x2F; // /
newBytes[pos++] = 0x54; // T
newBytes[pos++] = 0x58; // X
newBytes[pos++] = 0x3E; // >
}
// 转换为字符串
String result = new String(newBytes, 0, pos, java.nio.charset.StandardCharsets.ISO_8859_1);
log.info("字节级修复成功,新长度: " + pos);
log.info("修复后内容尾部: " + result.substring(Math.max(0, result.length() - 100)));
return result;
}
log.warn("未找到</LANG模式尝试应急修复");
// 应急修复:直接在字符串级别处理
String fallback = new String(responseBytes, java.nio.charset.StandardCharsets.ISO_8859_1);
if (fallback.contains("<LANGUAGE>CN</LANG") && !fallback.contains("</LANGUAGE>")) {
fallback = fallback.replace("<LANGUAGE>CN</LANG", "<LANGUAGE>CN</LANGUAGE>");
if (!fallback.contains("</TX>")) {
fallback = fallback + "</TX>";
}
log.info("应急修复完成");
}
return fallback;
}
/**
* 从ZIP流中解析Excel文件
* @param zipInputStream ZIP输入流
* @param entryName 文件名
* @return 解析后的文本行列表每行使用制表符分隔各列
*/
private List<String> parseExcelFromZip(ZipInputStream zipInputStream, String entryName) throws IOException {
List<String> result = new ArrayList<>();
Workbook workbook = null;
try {
// 将ZipInputStream的内容读取到ByteArrayInputStream中
// 因为POI需要支持随机访问的流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = zipInputStream.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
log.info("Excel文件字节数: " + baos.size());
// 根据文件扩展名选择合适的Workbook类型
if (entryName.toLowerCase().endsWith(".xlsx")) {
workbook = new XSSFWorkbook(bais);
} else {
// 对于.xls文件使用HSSFWorkbook需要poi-ooxml依赖
workbook = WorkbookFactory.create(bais);
}
// 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0);
log.info("Excel工作表名称: " + sheet.getSheetName() + ", 行数: " + sheet.getLastRowNum());
// 输出Excel的基本信息
log.info("Excel总行数: " + sheet.getLastRowNum());
// 输出第24行的表头信息真正的列标题
Row headerRow24 = sheet.getRow(23); // 第24行索引是23
if (headerRow24 != null) {
StringBuilder headerLine = new StringBuilder();
for (int col = 0; col < headerRow24.getLastCellNum(); col++) {
Cell cell = headerRow24.getCell(col);
if (cell != null) {
headerLine.append("[列").append(col).append("]=");
try {
headerLine.append(cell.toString());
} catch (Exception e) {
headerLine.append("ERROR");
}
headerLine.append(" | ");
}
}
log.info("第24行(真正的表头): " + headerLine.toString());
}
// 输出第25行的第一条数据示例
Row dataRow25 = sheet.getRow(24); // 第25行索引是24
if (dataRow25 != null) {
StringBuilder dataLine = new StringBuilder();
for (int col = 0; col < Math.min(20, dataRow25.getLastCellNum()); col++) {
Cell cell = dataRow25.getCell(col);
if (cell != null) {
dataLine.append("[列").append(col).append("]=");
try {
dataLine.append(cell.toString());
} catch (Exception e) {
dataLine.append("ERROR");
}
dataLine.append(" | ");
}
}
log.info("第25行(第1条数据): " + dataLine.toString());
}
// 遍历所有行
for (int rowIndex = 0; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
continue;
}
StringBuilder lineBuilder = new StringBuilder();
int lastCellNum = row.getLastCellNum();
// 遍历该行的所有列
for (int cellIndex = 0; cellIndex < lastCellNum; cellIndex++) {
Cell cell = row.getCell(cellIndex);
String cellValue = "";
if (cell != null) {
// 根据单元格类型获取值JDK 8兼容方式
int cellType = cell.getCellType();
if (cellType == Cell.CELL_TYPE_STRING) {
cellValue = cell.getStringCellValue();
} else if (cellType == Cell.CELL_TYPE_NUMERIC) {
// 检查是否为日期格式
if (DateUtil.isCellDateFormatted(cell)) {
try {
// 使用SimpleDateFormat格式化日期时间
java.util.Date dateValue = cell.getDateCellValue();
2025-11-03 11:42:55 +08:00
// 检查是否只有时间日期部分为1899-12-31或1900-01-01
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.setTime(dateValue);
int year = cal.get(java.util.Calendar.YEAR);
if (year == 1899 || year == 1900) {
// 只有时间,格式化为 HH:mm:ss
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("HH:mm:ss");
cellValue = sdf.format(dateValue);
} else {
// 完整的日期时间,格式化为 yyyy-MM-dd HH:mm:ss
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
cellValue = sdf.format(dateValue);
}
2025-10-30 17:21:43 +08:00
} catch (Exception e) {
cellValue = cell.getDateCellValue().toString();
}
} else {
// 数字格式,避免科学计数法
cellValue = String.valueOf(cell.getNumericCellValue());
// 如果是整数,去掉小数点
if (cellValue.endsWith(".0")) {
cellValue = cellValue.substring(0, cellValue.length() - 2);
}
}
} else if (cellType == Cell.CELL_TYPE_BOOLEAN) {
cellValue = String.valueOf(cell.getBooleanCellValue());
} else if (cellType == Cell.CELL_TYPE_FORMULA) {
try {
cellValue = String.valueOf(cell.getNumericCellValue());
} catch (Exception e) {
try {
cellValue = cell.getStringCellValue();
} catch (Exception e2) {
cellValue = "";
}
}
} else if (cellType == Cell.CELL_TYPE_BLANK) {
cellValue = "";
} else {
cellValue = "";
}
}
// 添加制表符分隔
if (cellIndex > 0) {
lineBuilder.append("\t");
}
lineBuilder.append(cellValue);
}
result.add(lineBuilder.toString());
}
log.info("Excel解析完成" + result.size() + "");
} catch (Exception e) {
log.error("解析Excel文件失败: " + entryName, e);
throw new IOException("解析Excel文件失败", e);
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
log.warn("关闭Excel工作簿时出错", e);
}
}
}
return result;
}
/**
* 等待文件生成
* @param fileName 文件名
* @param timeoutSeconds 超时时间
* @return 文件是否生成成功
*/
private boolean waitForFileGeneration(String fileName, int timeoutSeconds) {
String filePath = "C:" + File.separator + "CCB_EBSClient" + File.separator + "download" + File.separator + fileName;
File file = new File(filePath);
int waitTime = 0;
int checkInterval = 2; // 每2秒检查一次
// 检查目录是否存在
File downloadDir = new File("C:" + File.separator + "CCB_EBSClient" + File.separator + "download");
if (!downloadDir.exists()) {
log.error("下载目录不存在: " + downloadDir.getAbsolutePath());
return false;
}
log.info("开始等待文件生成: " + fileName + ", 超时时间: " + timeoutSeconds + "");
log.info("文件完整路径: " + filePath);
log.info("下载目录: " + downloadDir.getAbsolutePath() + ", 存在: " + downloadDir.exists());
while (waitTime < timeoutSeconds) {
// 刷新文件对象状态
file = new File(filePath);
if (file.exists()) {
long fileSize = file.length();
log.info("文件已存在: " + fileName + ", 当前大小: " + fileSize + " 字节");
if (fileSize > 0) {
log.info("文件生成成功: " + filePath + ", 最终大小: " + fileSize + " 字节");
// 额外等待2秒确保文件写入完成
try {
Thread.sleep(2000);
// 再次检查文件大小是否稳定
long finalSize = file.length();
if (finalSize == fileSize) {
log.info("文件写入完成,大小稳定: " + finalSize + " 字节");
return true;
} else {
log.info("文件仍在写入中,大小变化: " + fileSize + " -> " + finalSize + " 字节");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
} else {
// 列出目录中的文件,帮助调试
if (waitTime % 10 == 0) { // 每10秒打印一次目录内容
File[] files = downloadDir.listFiles();
if (files != null && files.length > 0) {
log.info("下载目录中的文件: ");
for (File f : files) {
log.info(" - " + f.getName() + " (大小: " + f.length() + " 字节)");
}
} else {
log.info("下载目录为空");
}
}
}
try {
log.info("等待文件生成: " + fileName + ", 已等待 " + waitTime + " 秒 / " + timeoutSeconds + "");
Thread.sleep(checkInterval * 1000);
waitTime += checkInterval;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("等待文件生成被中断", e);
return false;
}
}
log.error("文件生成超时: " + fileName + ", 等待了 " + timeoutSeconds + "");
// 超时后再次检查目录状态
File[] files = downloadDir.listFiles();
if (files != null && files.length > 0) {
log.error("超时时下载目录中的文件: ");
for (File f : files) {
log.error(" - " + f.getName() + " (大小: " + f.length() + " 字节)");
}
} else {
log.error("超时时下载目录为空");
}
return false;
}
/**
* 从中专服务器下载ZIP文件到本地目录
* @param fileName 文件名
* @param serviceParamsService 服务参数服务
* @return 下载是否成功
*/
private boolean downloadZipFromIntermediaryServer(String fileName, ServiceParamsService serviceParamsService) {
FileOutputStream fos = null;
InputStream inputStream = null;
CloseableHttpResponse response = null;
try {
// 1. 获取中专服务器地址
List<HashMap<Object, Object>> serviceParams = serviceParamsService.findParamValByParamCode("hgd_dmz");
if (serviceParams == null || serviceParams.isEmpty()) {
log.error("未配置中转服务地址(hgd_dmz)");
return false;
}
String dmzUrl = StringDUtil.changeNullToEmpty(serviceParams.get(0).get("PARAM_VAL"));
if (dmzUrl.isEmpty()) {
log.error("中转服务地址(hgd_dmz)为空");
return false;
}
// 2. 构建下载接口URL
String downloadUrl = dmzUrl + "/download/" + fileName;
log.info("中专服务器下载接口URL: " + downloadUrl);
// 3. 创建HTTP客户端并发送GET请求
CloseableHttpClient httpClient = HttpClients.createDefault();
org.apache.http.client.methods.HttpGet httpGet = new org.apache.http.client.methods.HttpGet(downloadUrl);
httpGet.setHeader("Connection", "close");
response = httpClient.execute(httpGet);
// 4. 检查响应状态码
int statusCode = response.getStatusLine().getStatusCode();
log.info("中专服务器响应状态码: " + statusCode);
if (statusCode != 200) {
log.error("下载失败HTTP状态码: " + statusCode + ", 原因: " + response.getStatusLine().getReasonPhrase());
return false;
}
// 5. 获取响应实体
HttpEntity entity = response.getEntity();
if (entity == null) {
log.error("响应实体为空");
return false;
}
// 6. 确保目标目录存在
String targetDir = "C:" + File.separator + "CCB_EBSClient" + File.separator + "download";
File dir = new File(targetDir);
if (!dir.exists()) {
boolean created = dir.mkdirs();
log.info("创建目标目录: " + targetDir + ", 结果: " + created);
}
// 7. 构建目标文件路径
String targetFilePath = targetDir + File.separator + fileName;
File targetFile = new File(targetFilePath);
// 8. 如果文件已存在,先删除
if (targetFile.exists()) {
boolean deleted = targetFile.delete();
log.info("删除已存在的文件: " + targetFilePath + ", 结果: " + deleted);
}
// 9. 将响应流写入本地文件
inputStream = entity.getContent();
fos = new FileOutputStream(targetFile);
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
fos.flush();
log.info("文件下载完成: " + targetFilePath + ", 总大小: " + totalBytesRead + " 字节");
// 10. 验证文件是否下载成功
if (targetFile.exists() && targetFile.length() > 0) {
log.info("文件验证成功,大小: " + targetFile.length() + " 字节");
return true;
} else {
log.error("文件下载失败文件不存在或大小为0");
return false;
}
} catch (Exception e) {
log.error("从中专服务器下载ZIP文件异常: " + fileName, e);
return false;
} finally {
// 11. 关闭所有资源
try {
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
response.close();
}
} catch (IOException e) {
log.warn("关闭资源时出错", e);
}
}
}
2025-07-23 09:55:50 +08:00
}