From 34065c1c8b1424ab52401c52ff6b4d76854762c4 Mon Sep 17 00:00:00 2001 From: Yuan <1450637472@qq.com> Date: Thu, 30 Oct 2025 17:21:43 +0800 Subject: [PATCH] =?UTF-8?q?update=EF=BC=9A=E5=BB=BA=E8=A1=8C=E8=B4=A6?= =?UTF-8?q?=E5=8D=95=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hospitalgd/mapper/HisDetailMapper.java | 5 + .../mapper/TransactionDetailMapper.java | 10 + .../hospitalgd/mapper/UnilateralMapper.java | 10 + .../jobMethod/BankGetDataMethodByJHLZF.java | 1240 ++++++++++++++++- .../jobMethod/HISGetDataMethodByJH.java | 109 +- .../jobMethod/ReconciliationMethod.java | 111 ++ .../hospitalgd/service/HisDetailService.java | 5 + .../service/TransactionDetailService.java | 10 + .../hospitalgd/service/UnilateralService.java | 10 + .../impl/BankbillHistoryServiceImpl.java | 37 + .../service/impl/HisDetailServiceImpl.java | 5 + .../impl/TransactionDetailServiceImpl.java | 10 + .../service/impl/UnilateralServiceImpl.java | 10 + src/main/resources/mapper/HisDetailMapper.xml | 41 +- .../mapper/ReconciliationResultMapper.xml | 20 +- .../mapper/TransactionDetailMapper.xml | 42 +- .../resources/mapper/UnilateralMapper.xml | 27 + .../historyLog/ReconciliationLogMapper.xml | 63 +- .../reconciliationResult.html | 27 +- 19 files changed, 1611 insertions(+), 181 deletions(-) diff --git a/src/main/java/com/saye/hospitalgd/mapper/HisDetailMapper.java b/src/main/java/com/saye/hospitalgd/mapper/HisDetailMapper.java index 2f4a65d..6a1b634 100644 --- a/src/main/java/com/saye/hospitalgd/mapper/HisDetailMapper.java +++ b/src/main/java/com/saye/hospitalgd/mapper/HisDetailMapper.java @@ -43,4 +43,9 @@ public interface HisDetailMapper { void insertAllHisBillOriginalHY(List> addList) throws Exception; List> findMedicalInsuranceGroupData(HashMap map) throws Exception; + + /** + * 查询有自费金额的HIS账单记录(用于混合支付对账) + */ + List> findHisDetailWithZfAmount(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/mapper/TransactionDetailMapper.java b/src/main/java/com/saye/hospitalgd/mapper/TransactionDetailMapper.java index 8a49f9b..1c71ffb 100644 --- a/src/main/java/com/saye/hospitalgd/mapper/TransactionDetailMapper.java +++ b/src/main/java/com/saye/hospitalgd/mapper/TransactionDetailMapper.java @@ -75,4 +75,14 @@ public interface TransactionDetailMapper { void deleteHisAndThirdJoinDataByParamAndNotUnique(HashMap map) throws Exception; void addNotUniqueData(List> list) throws Exception; + + /** + * 根据三方信息更新关联表记录(用于自费对账自动核销) + */ + void updateJoinDateByThird(HashMap map) throws Exception; + + /** + * 根据三方信息删除关联表记录 + */ + void deleteJoinDataByThird(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/mapper/UnilateralMapper.java b/src/main/java/com/saye/hospitalgd/mapper/UnilateralMapper.java index af9b524..ececa12 100644 --- a/src/main/java/com/saye/hospitalgd/mapper/UnilateralMapper.java +++ b/src/main/java/com/saye/hospitalgd/mapper/UnilateralMapper.java @@ -28,4 +28,14 @@ public interface UnilateralMapper { void deleteUnilateralByJoinIdNotNull(HashMap map) throws Exception; void deleteUnilateralByIdandStatus(HashMap map) throws Exception; + + /** + * 根据ID更新单边账记录(用于自费对账自动核销) + */ + void updateUnilateralById(HashMap map) throws Exception; + + /** + * 根据ID删除单边账记录 + */ + void deleteUnilateralById(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/BankGetDataMethodByJHLZF.java b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/BankGetDataMethodByJHLZF.java index 5202cfe..9b9eadc 100644 --- a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/BankGetDataMethodByJHLZF.java +++ b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/BankGetDataMethodByJHLZF.java @@ -17,6 +17,7 @@ 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; +import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; @@ -28,6 +29,9 @@ import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.springframework.util.CollectionUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + import java.io.*; import java.net.URI; import java.nio.charset.Charset; @@ -92,6 +96,23 @@ public class BankGetDataMethodByJHLZF { String wlip = StringDUtil.changeNullToEmpty(map.get("WLIP")); String wlport = StringDUtil.changeNullToEmpty(map.get("WLPORT")); + // 验证和清理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("[<>&\"']", ""); + String requset_sn_sc = String.valueOf(System.currentTimeMillis()); log.info("传去的时间是dateStr:" + dateStr); @@ -134,9 +155,72 @@ public class BankGetDataMethodByJHLZF { httpPost.setEntity(entity); CloseableHttpResponse response = closeableHttpClient.execute(httpPost); - String s = EntityUtils.toString(response.getEntity(), "GB18030"); + + // 增强响应读取,防止截断 + 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()); + } + log.info("接受到的数据是s:" + s); - Document document = DocumentHelper.parseText(s); + + // 增强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); + } + } Iterator iterator = document.nodeIterator(); @@ -161,6 +245,7 @@ public class BankGetDataMethodByJHLZF { Boolean fileIsExist = false; if (!exist) { //文件不存在开始下载 + log.info("文件不存在,开始下载: " + fileName); HashMap searchMap = new HashMap<>(); searchMap.put("FUBS", "2"); List> wlConfigList = thirdSftpConfigService.findWLIF(searchMap); @@ -192,12 +277,114 @@ public class BankGetDataMethodByJHLZF { httpPostD.setHeader("content-type", "application/x-www-form-urlencoded"); httpPostD.setHeader("Connection", "close"); StringEntity entityD = new StringEntity(ps, "GB18030"); - httpPost.setEntity(entityD); + httpPostD.setEntity(entityD); - CloseableHttpResponse responseD = closeableHttpClientD.execute(httpPost); - String s_d = EntityUtils.toString(responseD.getEntity(), "GB18030"); - log.info("s_d is :" + s_d); - Document documentD = DocumentHelper.parseText(s_d); + 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标签截断的十六进制模式 + // = 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("CN")) { + log.warn("字符串检测到LANGUAGE标签截断,进行字节级修复"); + s_d = fixLanguageTagFromBytes(responseBytes); + log.info("字符串检测修复完成,新字符串长度: " + s_d.length()); + } + } + + log.info("最终字符串长度: " + s_d.length()); + + // 验证修复结果 + if (s_d.contains("CN") && s_d.contains("")) { + log.info("XML修复成功,包含完整的LANGUAGE标签和TX根元素"); + } else if (s_d.contains("CN")) { + log.error("XML修复失败,LANGUAGE标签仍然截断"); + // 最后尝试字符串级别的修复 + s_d = s_d.replace("CNCN"); + if (!s_d.contains("")) { + s_d = s_d + ""; + } + 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); + + // 尝试解析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); + } + } Iterator iteratorD = documentD.nodeIterator(); Element txD = (Element) iteratorD.next(); return_code = txD.element("RETURN_CODE"); @@ -205,93 +392,332 @@ public class BankGetDataMethodByJHLZF { if (return_code != null) { if (!return_code.getText().equals("000000")) { errCode = "999"; - errMsg = return_msg.getText(); - log.info("获取到的返回code是" + return_code); - } else {//下载成功了 - fileIsExist = true; + 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); + } + } } } } if (fileIsExist || exist) { //确定文件存在,开始读取数据 String filePath = "C:" + File.separator + "CCB_EBSClient" + File.separator + "download" + File.separator + fileName; + 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; // 继续下一次循环重试 + } + + log.info("文件验证通过,大小: " + file.length() + " 字节"); + List txtList = null; - FileInputStream stream = new FileInputStream(filePath); + FileInputStream stream = null; + ZipInputStream zipInputStream = null; + BufferedReader br = null; - ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(stream), Charset.forName("GBK")); - -// ZipEntry ze = null; + try { + stream = new FileInputStream(filePath); + zipInputStream = new ZipInputStream(new BufferedInputStream(stream), Charset.forName("GBK")); ZipEntry nextEntry = zipInputStream.getNextEntry(); - log.info("读取的文件是" + nextEntry); + if (nextEntry == null) { + throw new RuntimeException("ZIP文件中没有找到任何条目"); + } + + String entryName = nextEntry.getName(); + log.info("读取的ZIP条目: " + entryName + ", 大小: " + nextEntry.getSize()); - BufferedReader br = new BufferedReader(new InputStreamReader(zipInputStream, StandardCharsets.UTF_8)); + // 判断文件类型 + 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)); String line; txtList = new ArrayList<>(); + int lineCount = 0; //内容不为空,输出 while ((line = br.readLine()) != null) { txtList.add(line); + 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); + } } log.info("继续执行程序3"); List bankbillHistoryList = new ArrayList<>(); - for (int i = 2; i < txtList.size(); i++) { + + // 跳过前24行(Excel的前24行是标题和说明,第24行是表头,从第25行开始是数据) + for (int i = 24; i < txtList.size(); i++) { + try { String d = txtList.get(i); + + // 跳过空行 + if (d == null || d.trim().isEmpty()) { + continue; + } String[] s1 = d.split("\t"); - if (!StrUtil.contains(s1[4].trim(), "ZZJ")) { + // 跳过列数不足的行(至少需要10列才能有交易金额) + if (s1.length < 10) { + log.debug("第 " + (i + 1) + " 行列数不足,跳过"); continue; } + + // 检查是否是汇总行(包含"小计"、"合计"、"终端小计"等关键字) + 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) + " 行数据"); BankbillHistory bankbillHistory = new BankbillHistory(); - String[] split = s1[0].split(" "); - - // 设置BankbillHistory的字段 - bankbillHistory.setCJyrq(split[0]); // 交易日期 - bankbillHistory.setCJysj(split[1]); // 交易时间 - bankbillHistory.setCQsrq(s1[1]); // 清算日期 - bankbillHistory.setCLsh(s1[2]); // 流水号 - bankbillHistory.setCShddh(s1[3]); // 商户订单号 - bankbillHistory.setCYsddh(s1[4]); // 银商订单号 - bankbillHistory.setCJyje(s1[9]); // 交易金额 - bankbillHistory.setCSxf(s1[10]); // 手续费 - bankbillHistory.setCJylx(s1[15]); // 交易类型 - - // 设置默认值 - bankbillHistory.setCQsje(s1[9]); // 清算金额 = 交易金额 - bankbillHistory.setCSjzfje(s1[9]); // 实际支付金额 = 交易金额 - bankbillHistory.setCCkh(s1[2]); // 参考号 = 流水号 - bankbillHistory.setCCard(""); // 卡号(空) - bankbillHistory.setCKlx(""); // 卡类型(空) - bankbillHistory.setCFkh(s1[13]); // 发卡行 = 付款行 - bankbillHistory.setCZffs("建行龙支付"); // 支付方式 - bankbillHistory.setCBzzd(""); // 备注字段(空) - bankbillHistory.setCQbyhje("0"); // 钱包优惠金额 - bankbillHistory.setCShyhje("0"); // 商户优惠金额 - bankbillHistory.setCYjylsh(""); // 原交易流水号(空) - bankbillHistory.setCFqqs(""); // 分期期数(空) - bankbillHistory.setCFqsxf("0"); // 分期手续费 - bankbillHistory.setCFqfwf(""); // 分期服务方(空) - bankbillHistory.setCFqfxf(""); // 分期付息方(空) - bankbillHistory.setCQtyhje("0"); // 其他优惠金额 - bankbillHistory.setCThddh(""); // 退货订单号(空) - bankbillHistory.setCFkfy(""); // 付款附言(空) - bankbillHistory.setCFdjc(""); // 分店简称(空) - bankbillHistory.setCZddh(""); // 子订单号(空) - bankbillHistory.setBillTableName("建行龙支付对账单"); // 对账表名 + // 根据你提供的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]; // 时间部分 + log.info("从日期列提取时间 - 日期: [" + jyrq + "], 时间: [" + jysj + "]"); + } + } else { + // 如果还是空,设置默认值 + jysj = "00:00:00"; + log.debug("交易时间为空,设置默认值: 00:00:00"); + } + } + + // 调试日志:输出交易日期、时间和支付方式 + String debugZffs = s1.length > 16 ? s1[16] : "空"; + log.info("第 " + (i + 1) + " 行 - 交易日期(列13): [" + jyrq + "], 交易时间(列15): [" + jysj + "], 支付方式(列16): [" + debugZffs + "]"); + + 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] : ""); // 发卡行 + + // 支付方式固定为"建行龙支付" + bankbillHistory.setCZffs("建行龙支付"); // 支付方式 + + // 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("建行龙支付对账单"); // 对账表名 bankbillHistoryList.add(bankbillHistory); + log.info("成功解析第 " + (i + 1) + " 行数据"); + + } catch (Exception e) { + log.error("处理第 " + (i + 1) + " 行数据时出错,跳过该行", e); + continue; + } } if (!CollectionUtils.isEmpty(bankbillHistoryList)) { + log.info("成功解析 " + bankbillHistoryList.size() + " 条建行龙支付对账数据"); // 先存一份原始的 这份只是留底查询 bankbillHistoryService.insertBankBillOriginal(bankbillHistoryList, trade_date, "建行龙支付对账单"); + log.info("已保存原始对账数据到数据库"); // 再存一份修改的用于对账 bankbillHistoryService.insertAllBankHistory(bankbillHistoryList, trade_date, "建行龙支付对账单"); + log.info("已保存对账数据到对账表"); isOk = true; } else { errCode = "999"; - errMsg = "未查询到数据"; + errMsg = "未查询到有效数据(可能是文件格式不正确)"; + log.error("解析建行龙支付对账单失败: " + errMsg); isOk = false; } } @@ -311,7 +737,7 @@ public class BankGetDataMethodByJHLZF { addMap.put("trade_date", trade_date); addMap.put("quartz_id", id); addMap.put("quartz_name", name); - addMap.put("bill_table_name", fileName); + addMap.put("bill_table_name", "建行龙支付对账单"); addMap.put("thirdConfigId", "999"); if (isOk) { @@ -342,5 +768,707 @@ public class BankGetDataMethodByJHLZF { return resultMap; } + /** + * 清理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(" 0) { + response = response.substring(xmlStart); + } + + // 如果没有XML声明,查找根元素开始位置 + if (xmlStart == -1) { + int rootStart = response.indexOf(""); + 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("\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); + } + } + + // 特殊处理:检测CNCN")) { + log.warn("检测到LANGUAGE标签特定截断模式"); + // 直接替换为完整的标签 + xml = xml.replaceAll("CN]*$", "CN"); + xml = xml.replaceAll("CNCN"); + log.info("修复LANGUAGE标签截断"); + } + + // 处理XML结尾被截断的情况 + if (xml.endsWith("CNCNCN"); + log.info("修复XML结尾LANGUAGE标签"); + } + + // 处理XML在LANGUAGE标签中间被截断的情况 + if (xml.endsWith("") || xml.endsWith("CN") || xml.endsWith("CN<")) { + log.warn("检测到LANGUAGE标签内容截断"); + if (xml.endsWith("")) { + xml = xml.replace("", "CN"); + } else if (xml.endsWith("CN")) { + xml = xml.replace("CN", "CN"); + } else if (xml.endsWith("CN<")) { + xml = xml.replace("CN<", "CN"); + } + log.info("修复LANGUAGE标签内容截断"); + } + + // 处理标签名被截断的情况,如 + xml = xml.replaceAll(""); + xml = xml.replaceAll("]*)>([^<]*)$2"); + + // 修复LANGUAGE标签问题 - 处理各种截断情况 + // 特殊处理:如果发现CN")) { + xml = xml.replaceAll("CN]*", "CN"); + log.info("修复CNCN]*)>CN]*", "CN"); + + // 处理...内容被截断的情况,保留已有内容 + xml = xml.replaceAll("]*)>([^<]*?)(?=<(?!/LANGUAGE))", "$2"); + xml = xml.replaceAll("]*)>([^<]*?)$", "$2"); + + // 特殊处理:如果LANGUAGE标签内容为空但原始响应中有CN,尝试恢复 + if (xml.contains("") && xml.contains("CN")) { + // 查找CN在原始XML中的位置,如果在LANGUAGE标签附近,则恢复 + String originalPart = xml.substring(Math.max(0, xml.indexOf("") - 50), + Math.min(xml.length(), xml.indexOf("") + 20)); + if (originalPart.contains("CN")) { + xml = xml.replace("", "CN"); + 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"); + xml = xml.replaceAll("<" + tag + "([^>]*)>([^<]*?)$", + "<" + tag + "$1>$2"); + } + + // 移除重复的XML声明 + String[] parts = xml.split("\\<\\?xml[^\\>]*\\?\\>"); + if (parts.length > 2) { + StringBuilder sb = new StringBuilder(); + sb.append(""); + for (int i = 1; i < parts.length; i++) { + sb.append(parts[i]); + } + xml = sb.toString(); + } + + // 确保根元素完整 + if (!xml.contains("") && xml.contains("")) { + xml = xml + ""; + } + + // 最后的激进修复:如果仍然有问题,尝试重构XML + if (!xml.contains("") || xml.contains("CN][^>]*$")) { + 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(""; + 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("\n"); + reconstructed.append(""); + + if (requestSn != null) reconstructed.append("").append(requestSn).append(""); + if (custId != null) reconstructed.append("").append(custId).append(""); + if (txCode != null) reconstructed.append("").append(txCode).append(""); + if (returnCode != null) reconstructed.append("").append(returnCode).append(""); + if (returnMsg != null) reconstructed.append("").append(returnMsg).append(""); + + reconstructed.append("").append(language).append(""); + reconstructed.append(""); + + 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()); + + // 查找 = 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("找到= 0) { + // 检查 + newBytes[pos++] = 0x55; // U + newBytes[pos++] = 0x41; // A + newBytes[pos++] = 0x47; // G + newBytes[pos++] = 0x45; // E + newBytes[pos++] = 0x3E; // > + + // 如果 + String tempResult = new String(newBytes, 0, pos, java.nio.charset.StandardCharsets.ISO_8859_1); + if (!tempResult.contains("")) { + log.info("添加缺失的标签"); + 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("未找到CN")) { + fallback = fallback.replace("CNCN"); + if (!fallback.contains("")) { + fallback = fallback + ""; + } + log.info("应急修复完成"); + } + return fallback; + } + + /** + * 从ZIP流中解析Excel文件 + * @param zipInputStream ZIP输入流 + * @param entryName 文件名 + * @return 解析后的文本行列表(每行使用制表符分隔各列) + */ + private List parseExcelFromZip(ZipInputStream zipInputStream, String entryName) throws IOException { + List 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(); + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + cellValue = sdf.format(dateValue); + } 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> 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); + } + } + } } diff --git a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/HISGetDataMethodByJH.java b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/HISGetDataMethodByJH.java index 3e14fe9..a0166e6 100644 --- a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/HISGetDataMethodByJH.java +++ b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/HISGetDataMethodByJH.java @@ -140,10 +140,6 @@ public class HISGetDataMethodByJH { //将原始的转换成统一格式 List> newAddList= new ArrayList<>(); - List> cashList= new ArrayList<>(); - - List> zhipiaoList= new ArrayList<>(); - for (int i = 0; i < hisBillList.size(); i++) { HashMap hisBillHashMap = hisBillList.get(i); @@ -158,11 +154,12 @@ public class HISGetDataMethodByJH { String amount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("amount")); String tradeTime = StringDUtil.changeNullToEmpty(hisBillHashMap.get("tradeTime")); - // 提取新增字段:清算类别、险种类型、医保账户金额、医保统筹金额 + // 提取新增字段:清算类别、险种类型、医保账户金额、医保统筹金额、自费金额 String clrType = StringDUtil.changeNullToEmpty(hisBillHashMap.get("clrType")); String insutype = StringDUtil.changeNullToEmpty(hisBillHashMap.get("insutype")); String ybzhAmount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("ybzhAmount")); String ybtcAmount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("ybtcAmount")); + String zfAmount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("zfAmount")); // 只有当缺少关键业务信息时才跳过(患者ID、金额、交易时间都为空) if ("".equals(patientId) && "".equals(amount) && "".equals(tradeTime)){ @@ -232,104 +229,12 @@ public class HISGetDataMethodByJH { addMap.put("insutype",insutype); // 险种类型 addMap.put("ybzhAmount",ybzhAmount); // 医保账户金额 addMap.put("ybtcAmount",ybtcAmount); // 医保统筹金额 + addMap.put("zfAmount",zfAmount); // 自费金额(混合支付中的自费部分) - //如果是现金记录先放进现金集合内 - if (cash_code.equals(payType)){ - cashList.add(addMap); - }else if (zhipiao.equals(payType)){ - zhipiaoList.add(addMap); - }else{ - newAddList.add(addMap); - } - } - - //循环现金记录 合并同操作员同交易日期 同类别的(门诊,住院) his获取,记录应该就是前一天的。 - HashMap> cashMap=new HashMap<>(); - for (int i = 0; i < cashList.size(); i++) { - HashMap hashMap = cashList.get(i); - - String hisOperCode = StringDUtil.changeNullToEmpty(hashMap.get("hisOperCode")); - String payMethod = StringDUtil.changeNullToEmpty(hashMap.get("payMethod")); - String amount = StringDUtil.changeNullToEmpty(hashMap.get("amount")); - - HashMap map = cashMap.get(hisOperCode+"_"+payMethod); - - if (map==null){ - HashMap addMap=new HashMap<>(); - addMap.put("payMethod",payMethod); - addMap.put("tradingStatus",""); - addMap.put("bizType",""); - addMap.put("payType",cash_code); - addMap.put("tradeTime",trade_date+" 00:00:00"); - addMap.put("hisOperCode",hisOperCode); - addMap.put("amount",amount); - addMap.put("remarks",""); - addMap.put("platformTransId",""); - addMap.put("patientId",""); - addMap.put("patientName",""); - addMap.put("trade_date",trade_date); - addMap.put("his_wsdl_id",his_wsdl_id); - addMap.put("clrType",""); // 现金记录没有清算类别 - addMap.put("insutype",""); // 现金记录没有险种类型 - addMap.put("ybzhAmount","0"); // 现金记录没有医保账户金额 - addMap.put("ybtcAmount","0"); // 现金记录没有医保统筹金额 - - cashMap.put(hisOperCode+"_"+payMethod,addMap); - }else { - String oldAmount = StringDUtil.changeNullToEmpty(map.get("amount")); - String newAmount = new BigDecimal(oldAmount).add(new BigDecimal(amount)).toString(); - map.put("amount",newAmount); - } - } - - //循环map 把记录放入要添加的集合 - for (String key:cashMap.keySet()){ - newAddList.add(cashMap.get(key)); - } - - //循环支票的记录 和现金相同处理 - HashMap> zhipiaoMap=new HashMap<>(); - for (int i = 0; i < zhipiaoList.size(); i++) { - HashMap hashMap = zhipiaoList.get(i); - - String hisOperCode = StringDUtil.changeNullToEmpty(hashMap.get("hisOperCode")); - String payMethod = StringDUtil.changeNullToEmpty(hashMap.get("payMethod")); - String amount = StringDUtil.changeNullToEmpty(hashMap.get("amount")); - String payType = StringDUtil.changeNullToEmpty(hashMap.get("payType")); - - HashMap map = zhipiaoMap.get(hisOperCode+"_"+payMethod); - - if (map==null){ - HashMap addMap=new HashMap<>(); - addMap.put("payMethod",payMethod); - addMap.put("tradingStatus",""); - addMap.put("bizType",""); - addMap.put("payType",payType); - addMap.put("tradeTime",trade_date+" 00:00:00"); - addMap.put("hisOperCode",hisOperCode); - addMap.put("amount",amount); - addMap.put("remarks",""); - addMap.put("platformTransId",""); - addMap.put("patientId",""); - addMap.put("patientName",""); - addMap.put("trade_date",trade_date); - addMap.put("his_wsdl_id",his_wsdl_id); - addMap.put("clrType",""); // 支票记录没有清算类别 - addMap.put("insutype",""); // 支票记录没有险种类型 - addMap.put("ybzhAmount","0"); // 支票记录没有医保账户金额 - addMap.put("ybtcAmount","0"); // 支票记录没有医保统筹金额 - - zhipiaoMap.put(hisOperCode+"_"+payMethod,addMap); - }else { - String oldAmount = StringDUtil.changeNullToEmpty(map.get("amount")); - String newAmount = new BigDecimal(oldAmount).add(new BigDecimal(amount)).toString(); - map.put("amount",newAmount); - } - } - - //循环map 把记录放入要添加的集合 - for (String key:zhipiaoMap.keySet()){ - newAddList.add(zhipiaoMap.get(key)); + // 不再合并现金和支票记录,直接保留原始数据 + // 因为有些医保账户余额不足时会用现金支付,但仍然属于医保相关记录 + // 合并会导致清算类别(clrType)和险种(insutype)信息丢失 + newAddList.add(addMap); } //存一份his针对要调整的字段修改的 diff --git a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/ReconciliationMethod.java b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/ReconciliationMethod.java index 8247328..c9dc0f6 100644 --- a/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/ReconciliationMethod.java +++ b/src/main/java/com/saye/hospitalgd/scheduler/jobMethod/ReconciliationMethod.java @@ -799,6 +799,9 @@ public class ReconciliationMethod { //!!!! managerReconciliation(transactionDetailService, searchMap); + + // 处理混合支付中的自费部分对账 + processZfAmountReconciliation(transactionDetailService, unilateralService, searchMap, trade_date); } else { errCode = "999"; errMsg = "生成对账信息失败,原因:his或三方有记录没有获取到。"; @@ -952,5 +955,113 @@ public class ReconciliationMethod { reconciliationResultService.insertReconciliationResult(addMap); } + /** + * 处理混合支付中自费部分与三方的对账 + * @description: 当HIS账单中zfAmount不为0时,使用该金额与三方账单进行对账,匹配成功则消除对应的三方单边账 + * @author thuang + * @date 2025/10/27 + * @version 1.0 + */ + public static void processZfAmountReconciliation(TransactionDetailService transactionDetailService, + UnilateralService unilateralService, + HashMap searchMap, + String trade_date) throws Exception { + + // 1. 查询当日有zfAmount且不为0的HIS账单 + HisDetailService hisDetailService = GetBeanUtil.getBean(HisDetailServiceImpl.class); + HashMap zfAmountSearchMap = new HashMap<>(); + zfAmountSearchMap.put("trade_date", trade_date); + zfAmountSearchMap.put("has_zf_amount", "1"); // 标识查询有自费金额的记录 + + List> hisZfAmountList = hisDetailService.findHisDetailWithZfAmount(zfAmountSearchMap); + + if (hisZfAmountList == null || hisZfAmountList.size() == 0) { + System.out.println("当日没有自费金额记录,跳过zfAmount对账"); + return; + } + + System.out.println("开始处理自费金额对账,共" + hisZfAmountList.size() + "条记录"); + + // 2. 查询当日的三方单边账 + HashMap thirdUnilateralSearchMap = new HashMap<>(); + thirdUnilateralSearchMap.put("trade_date", trade_date); + thirdUnilateralSearchMap.put("err_type", "2"); // 三方单边账 + thirdUnilateralSearchMap.put("is_manager", "0"); // 未处理的 + + List> thirdUnilateralList = unilateralService.findUnilateralByParam(thirdUnilateralSearchMap); + + if (thirdUnilateralList == null || thirdUnilateralList.size() == 0) { + System.out.println("当日没有三方单边账,跳过zfAmount对账"); + return; + } + + System.out.println("当日三方单边账共" + thirdUnilateralList.size() + "条"); + + int matchedCount = 0; + + // 3. 遍历每条有自费金额的HIS记录(包括正数和负数) + for (HashMap hisRecord : hisZfAmountList) { + String zfAmount = StringDUtil.changeNullToEmpty(hisRecord.get("ZFAMOUNT")); + String platformTransId = StringDUtil.changeNullToEmpty(hisRecord.get("PLATFORMTRANSID")); + String tradingStatus = StringDUtil.changeNullToEmpty(hisRecord.get("TRADINGSTATUS")); + + // 跳过金额为0的记录,但保留负数 + if ("".equals(zfAmount) || "0".equals(zfAmount) || "0.00".equals(zfAmount)) { + continue; + } + + try { + BigDecimal amount = new BigDecimal(zfAmount); + if (amount.abs().compareTo(BigDecimal.ZERO) == 0) { + continue; + } + } catch (Exception e) { + continue; + } + + // 4. 在三方单边账中查找所有匹配的记录(同一订单号可能有一正一负) + Iterator> iterator = thirdUnilateralList.iterator(); + while (iterator.hasNext()) { + HashMap thirdRecord = iterator.next(); + String thirdAmount = StringDUtil.changeNullToEmpty(thirdRecord.get("I_JYJE")); + String thirdOrderNo = StringDUtil.changeNullToEmpty(thirdRecord.get("I_DDH")); + String thirdTradingType = StringDUtil.changeNullToEmpty(thirdRecord.get("I_JYLX")); + String unilateralId = StringDUtil.changeNullToEmpty(thirdRecord.get("ID")); + + // 检查金额是否匹配(绝对值相等) + boolean amountMatch = AmountUtil.isAmountEqual(zfAmount, thirdAmount); + + // 检查订单号和交易类型是否都匹配 + boolean orderMatch = platformTransId.equals(thirdOrderNo) && tradingStatus.equals(thirdTradingType); + + if (amountMatch && orderMatch) { + // 找到匹配的记录,直接删除单边账和关联表记录 + System.out.println("找到匹配记录并删除 - HIS自费金额: " + zfAmount + ", 三方金额: " + thirdAmount + + ", 订单号: " + platformTransId + " = " + thirdOrderNo + ", 交易类型: " + tradingStatus); + + // 5. 直接删除三方单边账记录 + HashMap deleteMap = new HashMap<>(); + deleteMap.put("id", unilateralId); + unilateralService.deleteUnilateralById(deleteMap); + + // 6. 删除关联表中对应的三方记录 + HashMap deleteJoinMap = new HashMap<>(); + deleteJoinMap.put("i_ddh", thirdOrderNo); + deleteJoinMap.put("i_jylx", thirdTradingType); + deleteJoinMap.put("trade_date", trade_date); + transactionDetailService.deleteJoinDataByThird(deleteJoinMap); + + matchedCount++; + + // 从列表中移除已匹配的记录 + iterator.remove(); + // 不使用break,继续查找同一订单号的其他记录(如一正一负) + } + } + } + + System.out.println("自费金额对账完成,成功匹配并删除 " + matchedCount + " 条单边账记录"); + } + } diff --git a/src/main/java/com/saye/hospitalgd/service/HisDetailService.java b/src/main/java/com/saye/hospitalgd/service/HisDetailService.java index b62062a..8cab5b4 100644 --- a/src/main/java/com/saye/hospitalgd/service/HisDetailService.java +++ b/src/main/java/com/saye/hospitalgd/service/HisDetailService.java @@ -32,4 +32,9 @@ public interface HisDetailService { List> findHisDetailCountData(HashMap map) throws Exception; List> findMedicalInsuranceGroupData(HashMap map) throws Exception; + + /** + * 查询有自费金额的HIS账单记录(用于混合支付对账) + */ + List> findHisDetailWithZfAmount(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/service/TransactionDetailService.java b/src/main/java/com/saye/hospitalgd/service/TransactionDetailService.java index 843b9eb..98f8f93 100644 --- a/src/main/java/com/saye/hospitalgd/service/TransactionDetailService.java +++ b/src/main/java/com/saye/hospitalgd/service/TransactionDetailService.java @@ -78,4 +78,14 @@ public interface TransactionDetailService { * @version 1.0 */ void addNotUniqueData(List> list) throws Exception; + + /** + * 根据三方信息更新关联表记录(用于自费对账自动核销) + */ + void updateJoinDateByThird(HashMap map) throws Exception; + + /** + * 根据三方信息删除关联表记录 + */ + void deleteJoinDataByThird(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/service/UnilateralService.java b/src/main/java/com/saye/hospitalgd/service/UnilateralService.java index 2798b06..6f2e23e 100644 --- a/src/main/java/com/saye/hospitalgd/service/UnilateralService.java +++ b/src/main/java/com/saye/hospitalgd/service/UnilateralService.java @@ -26,4 +26,14 @@ public interface UnilateralService { void deleteUnilateralByJoinIdNotNull(HashMap map) throws Exception; void deleteUnilateralByIdandStatus(HashMap map) throws Exception; + + /** + * 根据ID更新单边账记录(用于自费对账自动核销) + */ + void updateUnilateralById(HashMap map) throws Exception; + + /** + * 根据ID删除单边账记录 + */ + void deleteUnilateralById(HashMap map) throws Exception; } diff --git a/src/main/java/com/saye/hospitalgd/service/impl/BankbillHistoryServiceImpl.java b/src/main/java/com/saye/hospitalgd/service/impl/BankbillHistoryServiceImpl.java index bb01033..82128f4 100644 --- a/src/main/java/com/saye/hospitalgd/service/impl/BankbillHistoryServiceImpl.java +++ b/src/main/java/com/saye/hospitalgd/service/impl/BankbillHistoryServiceImpl.java @@ -119,6 +119,43 @@ public class BankbillHistoryServiceImpl implements BankbillHistoryService { zffs = payMethodMap.get("掌医支付"); bankbillHistory.setCZffs(zffs); } + else if ("建行龙支付对账单".equals(billTableName)){ + // 建行龙支付对账单的支付方式映射 + if (cZffs != null && !cZffs.trim().isEmpty()) { + String cZffsLower = cZffs.trim(); + + // 优先精确匹配 + zffs = payMethodMap.get(cZffsLower); + + // 如果精确匹配失败,进行模糊匹配 + if (zffs == null) { + // 根据支付方式名称映射到字典值(按优先级匹配) + if (cZffsLower.contains("微信退款")) { + zffs = payMethodMap.get("微信退款"); + } else if (cZffsLower.contains("支付宝退款")) { + zffs = payMethodMap.get("支付宝退款"); + } else if (cZffsLower.contains("微信")) { + zffs = payMethodMap.get("微信支付"); + } else if (cZffsLower.contains("支付宝")) { + zffs = payMethodMap.get("支付宝支付"); + } else if (cZffsLower.contains("云闪付")) { + zffs = payMethodMap.get("云闪付支付"); + } else if (cZffsLower.contains("刷卡") || cZffsLower.contains("银联") || cZffsLower.contains("银行卡")) { + zffs = payMethodMap.get("刷卡支付"); + } else { + // 都匹配不到,使用其他支付 + zffs = payMethodMap.get("其他支付"); + } + } + + // 添加调试日志 + org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass()); + log.info("建行龙支付映射 - 原始支付方式: [" + cZffsLower + "], 映射后字典值: [" + zffs + "]"); + } else { + zffs = payMethodMap.get("其他支付"); + } + bankbillHistory.setCZffs(zffs); + } else{ if (zffs==null){ zffs = payMethodMap.get("其他支付"); diff --git a/src/main/java/com/saye/hospitalgd/service/impl/HisDetailServiceImpl.java b/src/main/java/com/saye/hospitalgd/service/impl/HisDetailServiceImpl.java index 91ca88b..bad5d46 100644 --- a/src/main/java/com/saye/hospitalgd/service/impl/HisDetailServiceImpl.java +++ b/src/main/java/com/saye/hospitalgd/service/impl/HisDetailServiceImpl.java @@ -184,4 +184,9 @@ public class HisDetailServiceImpl implements HisDetailService { return hisDetailMapper.findMedicalInsuranceGroupData(map); } + @Override + public List> findHisDetailWithZfAmount(HashMap map) throws Exception { + return hisDetailMapper.findHisDetailWithZfAmount(map); + } + } diff --git a/src/main/java/com/saye/hospitalgd/service/impl/TransactionDetailServiceImpl.java b/src/main/java/com/saye/hospitalgd/service/impl/TransactionDetailServiceImpl.java index 22f895f..b8e56fb 100644 --- a/src/main/java/com/saye/hospitalgd/service/impl/TransactionDetailServiceImpl.java +++ b/src/main/java/com/saye/hospitalgd/service/impl/TransactionDetailServiceImpl.java @@ -164,5 +164,15 @@ public class TransactionDetailServiceImpl implements TransactionDetailService { transactionDetailMapper.addNotUniqueData(list); } + @Override + public void updateJoinDateByThird(HashMap map) throws Exception { + transactionDetailMapper.updateJoinDateByThird(map); + } + + @Override + public void deleteJoinDataByThird(HashMap map) throws Exception { + transactionDetailMapper.deleteJoinDataByThird(map); + } + } diff --git a/src/main/java/com/saye/hospitalgd/service/impl/UnilateralServiceImpl.java b/src/main/java/com/saye/hospitalgd/service/impl/UnilateralServiceImpl.java index 76b942a..4224ba3 100644 --- a/src/main/java/com/saye/hospitalgd/service/impl/UnilateralServiceImpl.java +++ b/src/main/java/com/saye/hospitalgd/service/impl/UnilateralServiceImpl.java @@ -76,4 +76,14 @@ public class UnilateralServiceImpl implements UnilateralService { public void deleteUnilateralByIdandStatus(HashMap map) throws Exception { unilateralMapper.deleteUnilateralByIdandStatus(map); } + + @Override + public void updateUnilateralById(HashMap map) throws Exception { + unilateralMapper.updateUnilateralById(map); + } + + @Override + public void deleteUnilateralById(HashMap map) throws Exception { + unilateralMapper.deleteUnilateralById(map); + } } diff --git a/src/main/resources/mapper/HisDetailMapper.xml b/src/main/resources/mapper/HisDetailMapper.xml index e372934..cfb9810 100644 --- a/src/main/resources/mapper/HisDetailMapper.xml +++ b/src/main/resources/mapper/HisDetailMapper.xml @@ -59,6 +59,43 @@ + + + delete from hisbill_original where trade_date=#{trade_date} @@ -114,12 +151,12 @@ insert into - hisbill_history(PayMethod,TradingStatus,BizType,PayType,TradeTime,HisOperCode,Amount,remarks,PlatformTransId,PatientId,PatientName,trade_date,his_wsdl_id,HisTransId,clr_type,insutype,ybzhAmount,ybtcAmount) + hisbill_history(PayMethod,TradingStatus,BizType,PayType,TradeTime,HisOperCode,Amount,remarks,PlatformTransId,PatientId,PatientName,trade_date,his_wsdl_id,HisTransId,clr_type,insutype,ybzhAmount,ybtcAmount,zfAmount) values ( #{itm.payMethod},#{itm.tradingStatus},#{itm.bizType},#{itm.payType},#{itm.tradeTime},#{itm.hisOperCode},#{itm.amount},#{itm.remarks},#{itm.platformTransId},#{itm.patientId} - ,#{itm.patientName},#{itm.trade_date},#{itm.his_wsdl_id},#{itm.hisTransId},#{itm.clrType},#{itm.insutype},#{itm.ybzhAmount},#{itm.ybtcAmount} + ,#{itm.patientName},#{itm.trade_date},#{itm.his_wsdl_id},#{itm.hisTransId},#{itm.clrType},#{itm.insutype},#{itm.ybzhAmount},#{itm.ybtcAmount},#{itm.zfAmount} ) diff --git a/src/main/resources/mapper/ReconciliationResultMapper.xml b/src/main/resources/mapper/ReconciliationResultMapper.xml index 52356bd..1018da0 100644 --- a/src/main/resources/mapper/ReconciliationResultMapper.xml +++ b/src/main/resources/mapper/ReconciliationResultMapper.xml @@ -4,17 +4,31 @@ diff --git a/src/main/resources/mapper/TransactionDetailMapper.xml b/src/main/resources/mapper/TransactionDetailMapper.xml index 0989ee1..97cee16 100644 --- a/src/main/resources/mapper/TransactionDetailMapper.xml +++ b/src/main/resources/mapper/TransactionDetailMapper.xml @@ -117,6 +117,9 @@ and payType!=#{military_code} + + and payType!=#{medical_insurance_code} + ) a inner join (select * from bankbill_history where C_JYRQ = #{trade_date}) b @@ -151,6 +154,9 @@ and payType!=#{military_code} + + and payType!=#{medical_insurance_code} + ) a left join (select * from bankbill_history where C_JYRQ = #{trade_date}) b @@ -186,6 +192,9 @@ and payType!=#{military_code} + + and payType!=#{medical_insurance_code} + ) a right join (select * from bankbill_history where C_JYRQ = #{trade_date}) b @@ -428,7 +437,23 @@ where HisOperCode = #{hisOperCode} and trade_date = #{trade_date} and PayType = #{payType} - and payMethod = #{payMethod} + + + + + update third_join_his + set check_result=#{check_result} + + + and I_DDH=#{i_ddh} + + + and I_JYLX=#{i_jylx} + + + and trade_date=#{trade_date} + + @@ -440,6 +465,21 @@ and payMethod = #{payMethod} + + delete from third_join_his + + + and I_DDH=#{i_ddh} + + + and I_JYLX=#{i_jylx} + + + and trade_date=#{trade_date} + + + + - select trade_date,modify_time,create_time,manager_num,status,user_name,remark - from reconciliation_info - - - - and trade_date >=#{startTime} - - - and trade_date <=#{endTime} - - - and status=#{status} - - - order by trade_date desc + select + t.trade_date as TRADE_DATE, + t.modify_time as MODIFY_TIME, + t.create_time as CREATE_TIME, + t.manager_num as MANAGER_NUM, + t.status as STATUS, + t.user_name as USER_NAME, + t.remark as REMARK + from ( + select + trade_date, + modify_time, + create_time, + manager_num, + status, + user_name, + remark, + ROW_NUMBER() OVER (PARTITION BY trade_date ORDER BY create_time DESC) as rn + from reconciliation_info + + + and trade_date is not null + and trade_date != '' + and trade_date REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' + + and status in ('0', '1') + + and manager_num is not null + and manager_num not like '%.%' + and manager_num REGEXP '^[0-9]+$' + and CAST(manager_num AS UNSIGNED) < 10000 + + and (modify_time is null or modify_time not like '%.%') + and (create_time is null or create_time not like '%.%') + + + and trade_date >=#{startTime} + + + and trade_date <=#{endTime} + + + and status=#{status} + + + ) t + where t.rn = 1 + order by t.trade_date desc