✨ 支持应收账单导出通过 OSS 签名链接下载

This commit is contained in:
caojia
2026-11-12 09:27:10 +08:00
parent 27715b3e13
commit 146d3469b6
6 changed files with 94 additions and 11 deletions
@@ -3,6 +3,7 @@ package com.bms.receivable.service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.bms.receivable.domain.ArBill;
import com.bms.receivable.domain.ArBankAccount;
import com.bms.receivable.domain.ArBillDetail;
@@ -85,6 +86,8 @@ public interface IArBillService
List<ArBillExport> selectExports(Long arBillId);
Map<String, Object> getExportDownloadInfo(Long exportId);
void downloadExport(Long exportId, jakarta.servlet.http.HttpServletResponse response) throws Exception;
List<ArBillLog> selectLogList(ArBillLog query);
@@ -119,6 +119,7 @@ public class ArBillServiceImpl implements IArBillService {
private static final Logger log = LoggerFactory.getLogger(ArBillServiceImpl.class);
private static final String EXPORT_TEMPLATE_BUSINESS_TYPE = "AR_BILL";
private static final String EXPORT_TEMPLATE_RESOURCE = "templates/receivable/ar_bill_template.xlsx";
private static final long EXPORT_DOWNLOAD_URL_TTL_MILLIS = 10 * 60 * 1000L;
/**
* Excel 行号从 1 开始;模板第 4 行是表头,明细从第 5 行开始写入。
*/
@@ -1245,17 +1246,29 @@ public class ArBillServiceImpl implements IArBillService {
return exports;
}
@Override
public Map<String, Object> getExportDownloadInfo(Long exportId) {
ArBillExport export = requireDownloadableExport(exportId);
String resource = export.getStorageKey();
Map<String, Object> info = new HashMap<>(3);
if (resource.startsWith("oss://")) {
Date expiresAt = new Date(System.currentTimeMillis() + EXPORT_DOWNLOAD_URL_TTL_MILLIS);
info.put("direct", true);
info.put("downloadUrl", ossStorage.generatePresignedDownloadUrl(resource, export.getFileName(), expiresAt));
info.put("expiresAt", expiresAt.getTime());
} else {
if (!FileUtils.checkAllowDownload(resource)) {
throw new ServiceException("导出文件路径非法");
}
info.put("direct", false);
}
return info;
}
@Override
public void downloadExport(Long exportId, HttpServletResponse response) throws Exception {
long startedAt = System.nanoTime();
ArBillExport export = exportMapper.selectById(exportId);
if (export == null) {
throw new ServiceException("账单不存在或无权访问");
}
billScope.requireBillAccessible(export.getArBillId());
if (!ArExportStatus.SUCCESS.getCode().equals(export.getExportStatus()) || StringUtils.isEmpty(export.getStorageKey())) {
throw new ServiceException("导出文件不存在或尚未生成成功");
}
ArBillExport export = requireDownloadableExport(exportId);
String resource = export.getStorageKey();
boolean ossFile = resource.startsWith("oss://");
if (!ossFile && !FileUtils.checkAllowDownload(resource)) {
@@ -1272,6 +1285,19 @@ public class ArBillServiceImpl implements IArBillService {
exportId, export.getFileSize(), elapsedMillis(startedAt));
}
private ArBillExport requireDownloadableExport(Long exportId) {
ArBillExport export = exportMapper.selectById(exportId);
if (export == null) {
throw new ServiceException("账单不存在或无权访问");
}
billScope.requireBillAccessible(export.getArBillId());
if (!ArExportStatus.SUCCESS.getCode().equals(export.getExportStatus())
|| StringUtils.isEmpty(export.getStorageKey())) {
throw new ServiceException("导出文件不存在或尚未生成成功");
}
return export;
}
@Override
public List<ArBillLog> selectLogList(ArBillLog query) {
long startedAt = System.nanoTime();