Commit fb637ce4 authored by huyuchen's avatar huyuchen

huych-工行回单下载

parent fea56e36
......@@ -91,7 +91,7 @@ public class EkpBankAtta {
@ExcelProperty("链接ID")
private String fdLinkId;
/**
* 类型:1:银企付款主表;2:银企付款明细表
* 类型:1:银企付款主表;2:银企付款明细表 3:银企付款回单
*/
@ExcelAttribute(name = "类型")
@HeadFontStyle(fontHeightInPoints = 11)
......
......@@ -136,9 +136,16 @@ public class IcbcTransactionFlowIssueController {
return icbcTransactionFlowIssueService.doGetEkpBankStatus();
}
@Operation(summary = "获取回单文件", description = "获取回单文件")
@Operation(summary = "定时任务获取回单文件", description = "定时任务获取回单文件")
@Inner
@PostMapping("/inner/getPdfFile")
public R getPdfFile() {
return icbcTransactionFlowIssueService.getPdfFile();
}
@Operation(summary = "定时任务获取回单文件", description = "定时任务获取回单文件")
@PostMapping("/getPdfFile")
public void getPdfFile() {
icbcTransactionFlowIssueService.getPdfFile();
public R getPdfFileTest() {
return icbcTransactionFlowIssueService.getPdfFile();
}
}
......@@ -75,6 +75,6 @@ public interface IcbcTransactionFlowIssueService extends IService<EkpSocialInfo>
**/
R<String> doGetEkpBankStatus();
void getPdfFile();
R getPdfFile();
}
......@@ -3,6 +3,7 @@ package com.yifu.cloud.plus.v1.ekp.service.impl;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.icbc.api.DefaultIcbcClient;
......@@ -29,6 +30,7 @@ import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
......@@ -37,20 +39,24 @@ import org.springframework.transaction.annotation.Transactional;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* 聚富通到账通知交易流水查询
......@@ -1036,23 +1042,37 @@ public class IcbcTransactionFlowIssueServiceImpl extends ServiceImpl<EkpSocialIn
return R.ok();
}
/**
* 重写获取PDF文件的方法
* 该方法通过SFTP协议从工商银行的服务器下载PDF文件
* @Author: huych
* @Date: 2025/5/19 17:36
**/
@Override
public void getPdfFile() {
public R getPdfFile() {
// 工商银行SFTP服务器地址
String host = "gw.open.icbc.com.cn";
// 工商银行SFTP服务器端口
int port = 8001;
// 登录用户名
String username = "wxrl";
// String privateKeyPath = "/path/to/your/private_key"; // 替换为你的私钥路径
String privateKeyPath = "D:/icbcFile/icbc"; // 替换为你的私钥路径
// 私钥路径(用于身份验证)
//String privateKeyPath = "D:/icbcFile/icbc"; // 替换为你的私钥路径
//E:\IdeaProjects\yifu_mvp\yifu-ekp\yifu-ekp-biz\src\main\resources\icbc\icbc
String privateKeyPath = System.getProperty("user.dir") + "/yifu-ekp/yifu-ekp-biz/src/main/resources/icbc/icbc"; // 远程目录路径
// 远程目录路径
String remoteDir = "/JFTStatementDownload/download";
String localPath = "D:/icbcFile/pdfFile"; // 替换为本地保存路径
Session session = null;
ChannelSftp channelSftp = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1); // 获取前一天日期
String targetDate = sdf.format(cal.getTime());
cal.add(Calendar.DATE, -1); // 获取前两天日期
String targetDateTwo = sdf.format(cal.getTime());
try {
JSch jsch = new JSch();
// // 如果有密码,添加密码参数
// jsch.addIdentity(privateKeyPath, "root@yfmaster1");
// 添加私钥(注意:需要是没有密码保护的私钥)
jsch.addIdentity(privateKeyPath);
......@@ -1062,6 +1082,7 @@ public class IcbcTransactionFlowIssueServiceImpl extends ServiceImpl<EkpSocialIn
// 关闭严格的主机密钥检查(生产环境应配置known_hosts)
session.setConfig("StrictHostKeyChecking", "no");
// 连接会话
session.connect();
// 创建SFTP通道
......@@ -1075,23 +1096,69 @@ public class IcbcTransactionFlowIssueServiceImpl extends ServiceImpl<EkpSocialIn
Vector<ChannelSftp.LsEntry> files = channelSftp.ls("*");
log.info("找到文件数量: " + files.size());
// 下载所有文件
//ESC 字符:在 Java 中,ESC 字符可以用 \u001B 表示。
//如果输入字符串中可能包含连续的 ESC 字符(如 \u001B\u001B),split("\u001B") 会在结果数组中生成空字符串("")。如果需要过滤掉空字符串,可以使用 split("\u001B", -1) 或后续处理。
// 下载所有文件
for (ChannelSftp.LsEntry entry : files) {
if (!entry.getAttrs().isDir()) {
String fileName = entry.getFilename();
log.info("正在下载: " + fileName);
channelSftp.get(fileName, localPath + fileName);
//找到存回单的压缩文件
if (fileName.startsWith("Receipt_00_") && (fileName.contains(targetDate) || fileName.contains(targetDateTwo))
&& fileName.endsWith(".zip")) {
try {
// 1. 处理压缩包文件
String date = extractDateFromFileName(fileName);
String indexFileName = "ReceiptIndex_00_" + date + ".bin";
// 2. 查找对应的索引文件
Optional<ChannelSftp.LsEntry> indexEntry = files.stream()
.filter(e -> e.getFilename().equals(indexFileName))
.findFirst();
if (!indexEntry.isPresent()) {
log.info("未找到对应的索引文件:{}", indexFileName);
continue;
}
// 3. 创建临时目录
String tempDir = System.getProperty("user.dir") + "/yifu-ekp/yifu-ekp-biz/src/main/resources/tem/sftp_processing/" + System.currentTimeMillis() + "/";
new File(tempDir).mkdirs();
// 4. 下载并解压压缩包
String localZipPath = tempDir + fileName;
channelSftp.get(fileName, localZipPath);
String unzipDir = tempDir + "unzipped/";
unzipFile(localZipPath, unzipDir);
// 5. 下载并解析索引文件
String localIndexPath = tempDir + indexFileName;
channelSftp.get(indexFileName, localIndexPath);
Map<String, String> salaryPdfMap = parseIndexFile(localIndexPath);
// 6. 处理映射关系
processPdfFiles(unzipDir, salaryPdfMap);
// 7. 清理临时文件
FileUtils.deleteDirectory(new File(tempDir));
} catch (Exception e) {
log.error("处理文件[{}]时发生错误:", fileName, e);
}
}
// log.info("正在下载: " + fileName);
// channelSftp.get(fileName, localPath + fileName);
}
}
} catch (JSchException e) {
// 如果认证失败,直接返回
if (e.getMessage().contains("Auth fail")) {
return;
return R.failed("认证失败");
}
} catch (SftpException e) {
return;
// 如果SFTP操作失败,直接返回
return R.failed(e.getMessage());
} finally {
// 确保通道和会话被正确关闭
if (channelSftp != null && channelSftp.isConnected()) {
channelSftp.exit();
}
......@@ -1099,6 +1166,118 @@ public class IcbcTransactionFlowIssueServiceImpl extends ServiceImpl<EkpSocialIn
session.disconnect();
}
}
return R.ok();
}
// 辅助方法定义
private String extractDateFromFileName(String fileName) {
Pattern pattern = Pattern.compile(".*_(\\d{8})_.*\\.zip");
Matcher matcher = pattern.matcher(fileName);
if (matcher.find()) {
return matcher.group(1);
}
throw new IllegalArgumentException("无效的文件名格式:" + fileName);
}
private void unzipFile(String zipPath, String outputDir) throws IOException {
byte[] buffer = new byte[1024];
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipPath))) {
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
File newFile = new File(outputDir, zipEntry.getName());
// 防止Zip Slip攻击
String canonicalPath = newFile.getCanonicalPath();
if (!canonicalPath.startsWith(new File(outputDir).getCanonicalPath())) {
throw new IOException("非法文件路径:" + zipEntry.getName());
}
if (zipEntry.isDirectory()) {
newFile.mkdirs();
} else {
newFile.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
}
zipEntry = zis.getNextEntry();
}
}
}
private Map<String, String> parseIndexFile(String indexPath) throws IOException {
Map<String, String> mapping = new HashMap<>();
char escChar = (char) 27;
Files.lines(Paths.get(indexPath), StandardCharsets.UTF_8)
.forEach(line -> {
String[] parts = line.split(Pattern.quote(String.valueOf(escChar)));
if (parts.length >= 4) {
String salaryFile = parts[2].contains("_") ?
parts[2].substring(parts[2].indexOf('_') + 1) :
parts[2];
mapping.put(salaryFile, parts[3]);
}
});
return mapping;
}
private void processPdfFiles(String pdfDir, Map<String, String> mapping) {
File directory = new File(pdfDir);
File[] pdfFiles = directory.listFiles((dir, name) -> name.endsWith(".pdf"));
if (pdfFiles == null) return;
String filePath = "ekpBankDownPdfFile";
EkpBankAtta attaOld;
for (File pdfFile : pdfFiles) {
String pdfName = pdfFile.getName();
String salaryFileName = mapping.entrySet().stream()
.filter(entry -> pdfName.equals(entry.getValue()))
.findFirst()
.map(Map.Entry::getKey)
.orElse(null);
if (salaryFileName != null) {
//文件上传至阿里云,以便ekp服务调用
try (InputStream inputStream = new FileInputStream(pdfFile)) {
String key = filePath + "/" + pdfName;
// 调用上传服务
boolean flag = ossUtil.uploadFileByStream(inputStream, key, null);
if (flag) {
String linkId;
int underscoreIndex = salaryFileName.indexOf('_');
if (underscoreIndex != -1) {
// 输出: WX1747376807702
linkId = salaryFileName.substring(0, underscoreIndex);
} else {
// 处理没有下划线的情况(根据需求可选)
linkId = salaryFileName;
}
log.info("文件:" + pdfName + "上传至存储空间" + ossUtil.getBucketName() + "成功!");
//重复的过滤
attaOld = ekpBankAttaService.getOne(Wrappers.<EkpBankAtta>query().lambda().
eq(EkpBankAtta::getFdLinkId, linkId).last(CommonConstants.LAST_ONE_SQL));
if (Common.isEmpty(attaOld)) {
EkpBankAtta atta = new EkpBankAtta();
atta.setFdAttaName(pdfName);
atta.setFdAttaSrc(key);
atta.setFdAttaSize(pdfFile.length());
atta.setFdCreateTime(LocalDateTime.now());
atta.setFdLinkId(linkId);
atta.setFdLinkType(CommonConstants.THREE_INT);
ekpBankAttaService.save(atta);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
......
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
......@@ -60,5 +60,16 @@ public class EkpTask {
log.info("------------每小时获取银企付款结果-定时任务结束------------");
}
/**
* @Author huyc
* @Description 生成昨日的回单文件
* @Date 2025-5-19 15:21:28
* @Param
* @return
**/
public void doCreateYesterdayPdfFile() {
log.info("------------定时生成生成昨日的回单文件-定时任务开始------------");
HttpDaprUtil.invokeMethodPost(daprEkpProperties.getAppUrl(),daprEkpProperties.getAppId(),"/icbcIssue/inner/getPdfFile","", Object.class, SecurityConstants.FROM_IN);
log.info("------------定时生成生成昨日的回单文件-定时任务结束------------");
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment