✨ 接入账单 OSS 存储并区分生产测试目录

This commit is contained in:
caojia
2026-10-16 16:44:51 +08:00
parent 9824e05919
commit b0719d2ae9
9 changed files with 406 additions and 36 deletions
@@ -5,10 +5,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
@@ -85,7 +83,7 @@ import com.bms.common.exception.ServiceException;
import com.bms.common.utils.DateUtils;
import com.bms.common.utils.StringUtils;
import com.bms.common.utils.file.FileUtils;
import com.bms.common.utils.file.FileUploadUtils;
import com.bms.common.utils.file.OssStorage;
import com.bms.common.utils.uuid.IdUtils;
import com.bms.price.domain.PrcIncomeBill;
import com.bms.price.domain.PrcIncomeBillFee;
@@ -103,6 +101,9 @@ import com.bms.price.mapper.PrcOrderBillMapper;
*/
@Service
public class ArBillServiceImpl implements IArBillService {
@Autowired
private OssStorage ossStorage;
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/ar/ar_bill_template.xlsx";
@@ -1038,7 +1039,7 @@ public class ArBillServiceImpl implements IArBillService {
}
/**
* 执行异步导出任务:读取模板、流式拼接明细并原子移动临时文件。
* 执行异步导出任务:流式生成临时 Excel,上传 OSS 后才标记成功。
*/
@Override
public void processExportTask(Long exportId) {
@@ -1052,6 +1053,7 @@ public class ArBillServiceImpl implements IArBillService {
long startedAt = System.nanoTime();
log.info("开始执行应收账单导出任务,exportId={}, arBillId={}", exportId, export.getArBillId());
ArBill bill = null;
Path temp = null;
try {
bill = billMapper.selectArBillById(export.getArBillId());
if (bill == null) {
@@ -1060,28 +1062,17 @@ public class ArBillServiceImpl implements IArBillService {
if (ArBillStatus.is(bill.getStatus(), ArBillStatus.DRAFT) || ArBillStatus.is(bill.getStatus(), ArBillStatus.VOID)) {
throw new ServiceException("草稿或作废账单不可导出");
}
String relative = "ar/" + DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + ".xlsx";
String downloadDir = BmsConfig.getProfile() + File.separator + "download";
File file = FileUploadUtils.getAbsoluteFile(downloadDir, relative);
Path target = file.toPath();
Path temp = Files.createTempFile(target.getParent(), ".ar-bill-export-", ".xlsx.tmp");
boolean moved = false;
try {
ArExportTemplate template = resolveExportTemplate(export, bill);
writeOoxmlWorkbook(bill, template, temp);
moveToTarget(temp, target);
moved = true;
} finally {
if (!moved) {
Files.deleteIfExists(temp);
}
}
export.setStorageKey(FileUploadUtils.getPathFileName(downloadDir, relative));
export.setFileSize(Files.size(target));
export.setFileHash(sha256(target));
String objectKey = "ar/bill/" + DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + ".xlsx";
temp = Files.createTempFile("ar-bill-export-", ".xlsx");
ArExportTemplate template = resolveExportTemplate(export, bill);
writeOoxmlWorkbook(bill, template, temp);
export.setFileSize(Files.size(temp));
export.setFileHash(sha256(temp));
export.setStorageKey(ossStorage.upload(temp, objectKey,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
int updated = exportMapper.markSuccess(export);
if (updated == 0) {
Files.deleteIfExists(target);
ossStorage.delete(export.getStorageKey());
log.warn("应收账单导出文件已生成但任务状态未更新,exportId={}, arBillId={}",
exportId, export.getArBillId());
} else {
@@ -1097,6 +1088,14 @@ public class ArBillServiceImpl implements IArBillService {
logService.recordFailure(export.getArBillId(), ArLogObjectType.EXPORT, String.valueOf(exportId),
ArLogOperationType.EXPORT, bill == null ? null : bill.getStatus(), message, export.getExportBy(), null);
}
} finally {
if (temp != null) {
try {
Files.deleteIfExists(temp);
} catch (IOException e) {
log.warn("应收账单导出临时文件清理失败,exportId={}", exportId, e);
}
}
}
}
@@ -1127,12 +1126,17 @@ public class ArBillServiceImpl implements IArBillService {
throw new ServiceException("导出文件不存在或尚未生成成功");
}
String resource = export.getStorageKey();
if (!FileUtils.checkAllowDownload(resource)) {
boolean ossFile = resource.startsWith("oss://");
if (!ossFile && !FileUtils.checkAllowDownload(resource)) {
throw new ServiceException("导出文件路径非法");
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
FileUtils.setAttachmentResponseHeader(response, export.getFileName());
FileUtils.writeBytes(BmsConfig.getProfile() + FileUtils.stripPrefix(resource), response.getOutputStream());
if (ossFile) {
ossStorage.download(resource, response.getOutputStream());
} else {
FileUtils.writeBytes(BmsConfig.getProfile() + FileUtils.stripPrefix(resource), response.getOutputStream());
}
log.debug("应收账单导出文件下载完成,exportId={}, fileSize={}, costMs={}",
exportId, export.getFileSize(), elapsedMillis(startedAt));
}
@@ -1800,14 +1804,6 @@ public class ArBillServiceImpl implements IArBillService {
}
}
private void moveToTarget(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (AtomicMoveNotSupportedException e) {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
}
}
private String exportRemark(ArBillDetail detail) {
return StringUtils.isEmpty(detail.getRemark()) ? detail.getGoodsDescription() : detail.getRemark();
}