|
|
@@ -1,17 +1,10 @@
|
|
|
package com.ruoyi.app.order.invoice;
|
|
|
|
|
|
-import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
-import com.ruoyi.app.utils.ezPay.EzPay;
|
|
|
-import com.ruoyi.app.utils.ezPay.EzPayConfig;
|
|
|
import com.ruoyi.system.domain.PosOrderInvoice;
|
|
|
import com.ruoyi.system.domain.PosOrderInvoicePrintJob;
|
|
|
import com.ruoyi.system.mapper.PosOrderInvoiceMapper;
|
|
|
import com.ruoyi.system.mapper.PosOrderInvoicePrintJobMapper;
|
|
|
-import com.ruoyi.system.mapper.PosStoreEzpayMapper;
|
|
|
-import com.ruoyi.system.mapper.PosStoreMapper;
|
|
|
-import com.ruoyi.system.domain.PosStoreEzpay;
|
|
|
-import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
@@ -25,30 +18,31 @@ import static com.ruoyi.app.order.invoice.InvoicePrintSnapshot.*;
|
|
|
public class MerchantInvoicePrintService {
|
|
|
private final PosOrderInvoiceMapper invoices;
|
|
|
private final PosOrderInvoicePrintJobMapper jobs;
|
|
|
- private final PosStoreEzpayMapper configs;
|
|
|
- private final PosStoreMapper stores;
|
|
|
- private final EzPay ezPay;
|
|
|
+ private final InvoicePrintQueryService query;
|
|
|
+ private final InvoicePrintAssetService assets;
|
|
|
private final InvoiceProofPdfRenderer renderer;
|
|
|
- @Value("${ezpay.base-url}")
|
|
|
- private String baseUrl;
|
|
|
|
|
|
public MerchantInvoicePrintService(PosOrderInvoiceMapper invoices, PosOrderInvoicePrintJobMapper jobs,
|
|
|
- PosStoreEzpayMapper configs, PosStoreMapper stores, EzPay ezPay, InvoiceProofPdfRenderer renderer) {
|
|
|
- this.invoices = invoices; this.jobs = jobs; this.configs = configs;
|
|
|
- this.stores = stores; this.ezPay = ezPay; this.renderer = renderer;
|
|
|
+ InvoicePrintQueryService query, InvoicePrintAssetService assets, InvoiceProofPdfRenderer renderer) {
|
|
|
+ this.invoices=invoices; this.jobs=jobs; this.query=query; this.assets=assets; this.renderer=renderer;
|
|
|
}
|
|
|
|
|
|
public record DetailResult(String invoiceNumber, int printCount, String fileName, String contentType,
|
|
|
String pdfBase64) { }
|
|
|
public record PrintResult(Long printJobId, String invoiceNumber, int printSequence, boolean reprint,
|
|
|
- String fileName, String contentType, String pdfBase64, String reversePdfBase64) { }
|
|
|
+ String fileName, String contentType, String pdfBase64, String reversePdfBase64,
|
|
|
+ String printerProfile, int dotsPerMm, java.util.List<InvoicePrintImage> images) { }
|
|
|
|
|
|
public record StoredPdfResult(String fileName, String contentType, String pdfBase64) { }
|
|
|
|
|
|
/** 平台查看已保存文件,不联网查票、不重新排版,也不分配新的打印序号。 */
|
|
|
public StoredPdfResult storedPdf(Long orderId) {
|
|
|
PosOrderInvoicePrintJob job = jobs.selectLatestForCurrentInvoice(orderId);
|
|
|
- if (job == null) return null;
|
|
|
+ if (job == null) {
|
|
|
+ var asset=assets.currentReady(orderId);
|
|
|
+ return asset==null?null:new StoredPdfResult(asset.getInvoiceNumber()+"-original.pdf",
|
|
|
+ "application/pdf",encode(asset.getOriginalPdf()));
|
|
|
+ }
|
|
|
return new StoredPdfResult(job.getInvoiceNumber() + "-" + job.getPrintSequence() + ".pdf",
|
|
|
"application/pdf", encode(job.getPdfContent()));
|
|
|
}
|
|
|
@@ -57,7 +51,7 @@ public class MerchantInvoicePrintService {
|
|
|
public DetailResult detail(Long orderId) {
|
|
|
PosOrderInvoice invoice = invoices.selectOne(new LambdaQueryWrapper<PosOrderInvoice>()
|
|
|
.eq(PosOrderInvoice::getOrderId, orderId));
|
|
|
- InvoicePrintSnapshot data = query(invoice);
|
|
|
+ InvoicePrintSnapshot data = query.query(invoice);
|
|
|
try {
|
|
|
return new DetailResult(invoice.getInvoiceNumber(), jobs.countForInvoice(invoice.getId(), invoice.getInvoiceTransNo()),
|
|
|
invoice.getInvoiceNumber() + "-detail.pdf", "application/pdf", encode(renderer.detail(data)));
|
|
|
@@ -79,7 +73,7 @@ public class MerchantInvoicePrintService {
|
|
|
}
|
|
|
// 多实例共享数据库行锁;不能仅用本机synchronized或先查次数再异步写记录。
|
|
|
PosOrderInvoice invoice = invoices.selectForPrintUpdate(request.getOrderId());
|
|
|
- InvoicePrintSnapshot data = query(invoice);
|
|
|
+ require(invoice != null && Objects.equals(invoice.getInvoiceStatus(), 1), "unavailable");
|
|
|
PosOrderInvoicePrintJob existing = jobs.selectByRequest(request.getOrderId(), request.getRequestId());
|
|
|
if (existing != null) {
|
|
|
// 作废/重开后不能用旧幂等键取回旧证明联;不同操作者也不能复用别人的申请键。
|
|
|
@@ -88,14 +82,15 @@ public class MerchantInvoicePrintService {
|
|
|
&& Objects.equals(existing.getRequestedBy(), userId)
|
|
|
&& (existing.getPrintSequence() > 1) == request.getReprint()
|
|
|
&& Objects.equals(normalize(existing.getReprintReason()), normalize(request.getReprintReason())), "requestConflict");
|
|
|
+ query.query(invoice);
|
|
|
return response(existing);
|
|
|
}
|
|
|
int count = jobs.countForInvoice(invoice.getId(), invoice.getInvoiceTransNo());
|
|
|
require((count > 0) == request.getReprint(), count > 0 ? "alreadyPrinted" : "firstPrintRequired");
|
|
|
- byte[] pdf;
|
|
|
- try { pdf = renderer.proof(data, count > 0); }
|
|
|
- catch (Exception e) { throw error("renderFailed"); }
|
|
|
- require(pdf.length <= 8 * 1024 * 1024, "tooLarge");
|
|
|
+ // 未准备好时不现场排版,也不先占用首次资格。查验通过才交付预生成的指定版本。
|
|
|
+ var asset=assets.requireReady(invoice);
|
|
|
+ query.query(invoice);
|
|
|
+ byte[] pdf=count>0?asset.getReprintPdf():asset.getOriginalPdf();
|
|
|
var job = new PosOrderInvoicePrintJob();
|
|
|
job.setInvoiceId(invoice.getId()); job.setOrderId(invoice.getOrderId()); job.setStoreId(invoice.getStoreId());
|
|
|
job.setInvoiceTransNo(invoice.getInvoiceTransNo()); job.setInvoiceNumber(invoice.getInvoiceNumber());
|
|
|
@@ -103,37 +98,20 @@ public class MerchantInvoicePrintService {
|
|
|
job.setReprintReason(normalize(request.getReprintReason()));
|
|
|
job.setOriginalAvailable(Boolean.TRUE.equals(request.getOriginalAvailable()));
|
|
|
job.setContentsVerified(Boolean.TRUE.equals(request.getContentsVerified()));
|
|
|
- job.setPdfContent(pdf); job.setCreateTime(new Date());
|
|
|
+ job.setPdfContent(pdf); job.setImagePages(count>0?asset.getReprintImages():asset.getOriginalImages());
|
|
|
+ job.setCreateTime(new Date());
|
|
|
require(jobs.insert(job) == 1, "saveFailed");
|
|
|
// 事务提交后才响应。即使客户端未收到响应,也只能用原requestId取回同一文件。
|
|
|
return response(job);
|
|
|
}
|
|
|
|
|
|
- private InvoicePrintSnapshot query(PosOrderInvoice invoice) {
|
|
|
- require(invoice != null && Objects.equals(invoice.getInvoiceStatus(), 1), "unavailable");
|
|
|
- var config = configs.selectOne(new LambdaQueryWrapper<PosStoreEzpay>()
|
|
|
- .eq(PosStoreEzpay::getStoreId, invoice.getStoreId()));
|
|
|
- require(config != null && !blank(config.getMerchantId()) && !blank(config.getHashKey()) && !blank(config.getHashIv()), "configuration");
|
|
|
- var store = stores.selectById(invoice.getStoreId());
|
|
|
- require(store != null, "unavailable");
|
|
|
- JSONObject response;
|
|
|
- try {
|
|
|
- response = ezPay.searchInvoiceForPrint(baseUrl,
|
|
|
- new EzPayConfig(config.getMerchantId(), config.getHashKey(), config.getHashIv()),
|
|
|
- invoice.getInvoiceNumber(), invoice.getRandomNum());
|
|
|
- } catch (Exception e) { throw error("queryFailed"); }
|
|
|
- require(response != null && "SUCCESS".equals(response.getString("Status")), "queryFailed");
|
|
|
- JSONObject result;
|
|
|
- try { result = response.getJSONObject("Result"); }
|
|
|
- catch (RuntimeException e) { throw error("invalidData"); }
|
|
|
- return InvoicePrintSnapshot.from(result, invoice, config, store.getPosName());
|
|
|
- }
|
|
|
-
|
|
|
private PrintResult response(PosOrderInvoicePrintJob job) {
|
|
|
try {
|
|
|
return new PrintResult(job.getId(), job.getInvoiceNumber(), job.getPrintSequence(), job.getPrintSequence() > 1,
|
|
|
job.getInvoiceNumber() + "-" + job.getPrintSequence() + ".pdf", "application/pdf",
|
|
|
- encode(job.getPdfContent()), encode(renderer.reverse()));
|
|
|
+ encode(job.getPdfContent()), encode(renderer.reverse()), InvoicePrintImageRenderer.PROFILE,
|
|
|
+ InvoicePrintImageRenderer.DOTS_PER_MM, blank(job.getImagePages())?java.util.List.of():
|
|
|
+ com.alibaba.fastjson2.JSON.parseArray(job.getImagePages(),InvoicePrintImage.class));
|
|
|
} catch (Exception e) { throw error("renderFailed"); }
|
|
|
}
|
|
|
private static String normalize(String text) { return blank(text) ? "" : text.trim(); }
|