Просмотр исходного кода

OMS发票信息部分代码上传

徐滕 1 месяц назад
Родитель
Сommit
8e4a69baf7

+ 11 - 9
src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralProjectSignatureOldMessageDisposeController.java

@@ -951,9 +951,10 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
     /**
      * 给accessToken查询的有效时间设置为1天
      */
-    private final String appId = "hyc1";
-    private final String appKey = "hyc1";
-    private final String deptCode = "500102204228315131";
+
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
 
     /**
      * 开具蓝票
@@ -966,7 +967,7 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
     @RequestMapping(value = "/invoiceOMSView")
     @ResponseBody
     @Transactional(readOnly = false)
-    public Map<String,Object> invoiceOMSView(@Param("orderno") String workInvoiceId){
+    public Map<String,Object> invoiceOMSView(@Param("workInvoiceId") String workInvoiceId){
         Map<String,Object> map = new HashMap<>();
         // 调用抽离后的核心业务方法,实现流程复用(0003时可重新调用)
         omsDisposeService.doInvoiceBusiness(map, workInvoiceId);
@@ -1044,10 +1045,10 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
     @RequestMapping(value = "/invoiceFastRedOMSView")
     @ResponseBody
     @Transactional(readOnly = false)
-    public Map<String,Object> invoiceFastRedOMSView(String allEinvno){
+    public Map<String,Object> invoiceFastRedOMSView(String allEinvno, String workInvoiceId) {
         Map<String,Object> map = new HashMap<>();
         // 调用抽离后的核心业务方法,实现流程复用(0003时可重新调用)
-        omsDisposeService.doFastRedInvoiceBusiness(map, allEinvno);
+        omsDisposeService.doFastRedInvoiceBusiness(map, allEinvno, workInvoiceId);
         return map;
     }
 
@@ -1056,16 +1057,17 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
 
     /**
      * 开具红票(全场景红冲,包含已入账红冲处理。我方发起)
-     * @param applyNo   申请单号
+     * @param workInvoiceId   申请单号
+     * @param originalInvno   原蓝票发票号码(数电票号码)
      * @return
      */
     @RequestMapping(value = "/invoiceAllScenarioRedOMSView")
     @ResponseBody
     @Transactional(readOnly = false)
-    public Map<String,Object> invoiceAllScenarioRedOMSView(String applyNo, String originalInvno){
+    public Map<String,Object> invoiceAllScenarioRedOMSView(String workInvoiceId, String originalInvno) {
         Map<String,Object> map = new HashMap<>();
         // 调用抽离后的核心业务方法,实现流程复用(0003时可重新调用)
-        omsDisposeService.doAllScenarioRedInvoiceBusiness(map, applyNo, originalInvno);
+        omsDisposeService.doAllScenarioRedInvoiceBusiness(map, workInvoiceId, originalInvno);
         return map;
     }
 

+ 9 - 2
src/main/java/com/jeeplus/modules/workcalendar/service/WorkCalendarTaskService.java

@@ -14,6 +14,7 @@ import com.jeeplus.modules.sys.utils.UserUtils;
 import com.jeeplus.modules.workcalendar.entity.WorkCalendar;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.service.OMS.InvoiceDownloadService;
+import com.jeeplus.modules.workinvoice.service.OMS.RedInvoiceDownloadService;
 import com.jeeplus.modules.workinvoice.service.OMS.RedInvoiceRetryScheduledService;
 import com.jeeplus.modules.workinvoice.service.OMS.RedInvoiceScheduledService;
 import com.jeeplus.modules.workinvoice.service.WorkInvoiceService;
@@ -67,6 +68,8 @@ public class WorkCalendarTaskService  {
 
     @Autowired
     private InvoiceDownloadService invoiceDownloadService;
+    @Autowired
+    private RedInvoiceDownloadService redInvoiceDownloadService;
 
     @Autowired
     private RedInvoiceScheduledService redInvoiceScheduledService;
@@ -501,16 +504,19 @@ public class WorkCalendarTaskService  {
      * 用于发票开票获取数电票信息处理
      * 和开票系统相关的定时任务
      */
-    //@Scheduled(cron = "0 */1 * * * ?")
+    @Scheduled(cron = "0 */1 * * * ?")
+    @Transactional(readOnly = false)
     public void processInvoiceDownloadTasks() {
         invoiceDownloadService.processInvoiceDownloadTasks();
+        redInvoiceDownloadService.processRedInvoiceDownloadTasks();
     }
 
     /**
      * 用于发票开票红冲定时任务信息处理
      * 和开票系统相关的定时任务
      */
-    //@Scheduled(cron = "0 */3 * * * ?")
+    @Scheduled(cron = "0 */3 * * * ?")
+    @Transactional(readOnly = false)
     public void processRedInvoiceScheduledTask() {
         redInvoiceScheduledService.processAllRedInvoiceTasks();
     }
@@ -520,6 +526,7 @@ public class WorkCalendarTaskService  {
      * 和开票系统相关的定时任务
      */
     //@Scheduled(cron = "0 */2 * * * ?")
+    @Transactional(readOnly = false)
     public void redInvoiceRetryScheduledTask() {
         redInvoiceRetryScheduledService.handleInvoice9998RetryTask();
     }

+ 29 - 0
src/main/java/com/jeeplus/modules/workinvoice/dao/WorkInvoiceDao.java

@@ -236,4 +236,33 @@ public interface WorkInvoiceDao extends CrudDao<WorkInvoice> {
 	String getInvoiceNumberStr(String invoiceId);
 
 	WorkInvoice getById(@Param("id")String id);
+
+
+	/**
+	 * 修改开票获取accesstoken失败结果
+	 * @param workInvoice
+	 * @return
+	 */
+	Integer updateAccessTokenErrorById(WorkInvoice workInvoice);
+
+	/**
+	 * 修改开票获取的url以及发票号结果
+	 * @param workInvoice
+	 * @return
+	 */
+	Integer updateOmsUrlById(WorkInvoice workInvoice);
+
+	/**
+	 * 修改订单提交接口返回值信息
+	 * @param workInvoice
+	 * @return
+	 */
+	Integer updateOrderForGoodsResultStrById(WorkInvoice workInvoice);
+
+	/**
+	 * 修改红字确认单返回结果信息
+	 * @param workInvoice
+	 * @return
+	 */
+	Integer updateConfirmationSlipResultStr(WorkInvoice workInvoice);
 }

+ 90 - 0
src/main/java/com/jeeplus/modules/workinvoice/entity/WorkInvoice.java

@@ -138,6 +138,23 @@ public class WorkInvoice extends ActEntity<WorkInvoice> {
 	private List<WorkInvoiceCloud> workInvoiceCloudList;
 	private String isSzCloud; //判断是否是苏州分公司的发票数据
 
+
+
+	private String omsAccessTokenError; //开具发票时,获取token失败报错信息保存
+	private String omsAccessToken; //开具发票时,获取token失败报错信息保存
+	private String omsErrorMessage; //开具发票失败时返回的错误信息
+
+
+	private String orderForGoodsResultStr; //订单提交接口返回值(蓝票的第2步。红票的第2步)
+	private String confirmationSlipResultStr; //红字确认单(红冲第3步)
+
+	private String omsEinVno; //对应发票的发票号
+	private String omsOfdUrl; //对应发票的ofdUrl
+	private String omsPdfUrl; //对应发票的pdfUrl
+	private String omsXmlUrl; //对应发票的xmlUrl
+
+
+
 	public String getIsSzCloud() {
 		return isSzCloud;
 	}
@@ -983,4 +1000,77 @@ public class WorkInvoice extends ActEntity<WorkInvoice> {
 	public void setSituationDetail(String situationDetail) {
 		this.situationDetail = situationDetail;
 	}
+
+	public String getOmsAccessTokenError() {
+		return omsAccessTokenError;
+	}
+
+	public void setOmsAccessTokenError(String omsAccessTokenError) {
+		this.omsAccessTokenError = omsAccessTokenError;
+	}
+
+	public String getOmsAccessToken() {
+		return omsAccessToken;
+	}
+
+	public void setOmsAccessToken(String omsAccessToken) {
+		this.omsAccessToken = omsAccessToken;
+	}
+
+	public String getOmsErrorMessage() {
+		return omsErrorMessage;
+	}
+
+	public void setOmsErrorMessage(String omsErrorMessage) {
+		this.omsErrorMessage = omsErrorMessage;
+	}
+
+
+	public String getOmsEinVno() {
+		return omsEinVno;
+	}
+
+	public void setOmsEinVno(String omsEinVno) {
+		this.omsEinVno = omsEinVno;
+	}
+
+	public String getOmsOfdUrl() {
+		return omsOfdUrl;
+	}
+
+	public void setOmsOfdUrl(String omsOfdUrl) {
+		this.omsOfdUrl = omsOfdUrl;
+	}
+
+	public String getOmsPdfUrl() {
+		return omsPdfUrl;
+	}
+
+	public void setOmsPdfUrl(String omsPdfUrl) {
+		this.omsPdfUrl = omsPdfUrl;
+	}
+
+	public String getOmsXmlUrl() {
+		return omsXmlUrl;
+	}
+
+	public void setOmsXmlUrl(String omsXmlUrl) {
+		this.omsXmlUrl = omsXmlUrl;
+	}
+
+	public String getOrderForGoodsResultStr() {
+		return orderForGoodsResultStr;
+	}
+
+	public void setOrderForGoodsResultStr(String orderForGoodsResultStr) {
+		this.orderForGoodsResultStr = orderForGoodsResultStr;
+	}
+
+	public String getConfirmationSlipResultStr() {
+		return confirmationSlipResultStr;
+	}
+
+	public void setConfirmationSlipResultStr(String confirmationSlipResultStr) {
+		this.confirmationSlipResultStr = confirmationSlipResultStr;
+	}
 }

+ 71 - 26
src/main/java/com/jeeplus/modules/workinvoice/service/OMS/InvoiceDownloadService.java

@@ -2,10 +2,13 @@ package com.jeeplus.modules.workinvoice.service.OMS;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.common.config.Global;
 import com.jeeplus.common.utils.JedisUtils;
 import com.jeeplus.modules.workinvoice.entity.OMS.InvoiceDown.OMSInvoiceDetailInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSInvoiceResultDownloadData;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
+import com.jeeplus.modules.workinvoice.service.WorkInvoiceService;
 import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
 import com.jeeplus.modules.workinvoice.utils.OMSNationUtil;
 import org.apache.commons.lang3.StringUtils;
@@ -20,20 +23,27 @@ import java.util.Base64;
 import java.util.Set;
 import java.util.UUID;
 
+/**
+ * OMS发票下载
+ */
 @Service
 @Transactional(readOnly = true)
 @Lazy
 public class InvoiceDownloadService {
 
 
-    private final String appId = "hyc1";
-    private final String appKey = "hyc1";
-    private final String deptCode = "500102204228315131";
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
 
 
     @Autowired
     private OMSDisposeService omsDisposeService;
 
+    @Autowired
+    private WorkInvoiceService workInvoiceService;
+
+
 
     public void processInvoiceDownloadTasks() {
         Jedis jedis = null;
@@ -47,11 +57,24 @@ public class InvoiceDownloadService {
 
             // 遍历所有解析任务
             for (String taskKey : downloadTaskKeys) {
-                String orderno = taskKey.replace("OMS_invoice_download:", "");
                 String accessToken = jedis.hget(taskKey, "accessToken");
+                String workInvoiceId = jedis.hget(taskKey, "workInvoiceId");
                 //获取当前redis中的accessToken
                 //如果有值,则使用当前redis中的accessToken,否则重新生成一个新的accessToken
-
+                accessToken = jedis.get("OMSAccessToken");
+
+                if(StringUtils.isBlank(accessToken)){
+                    // 获取AccessToken 9998重试5次
+                    accessToken = omsDisposeService.getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
+                    if(StringUtils.isNotBlank(accessToken)){
+                        jedis.setex("OMSAccessToken", 86400, accessToken);
+                        System.out.println("重新获取token成功,存入Redis");
+                    } else {
+                        accessToken = "";
+                        System.out.println("获取token失败");
+                        return;
+                    }
+                }
 
                 String firstExecTimeStr = jedis.hget(taskKey, "firstExecTime");
 
@@ -65,18 +88,14 @@ public class InvoiceDownloadService {
                 // ========== 改动1:超时判断改为23小时(82800000毫秒) ==========
                 // 23*60*60*1000 = 82800000 ms(原86400000是24小时)
                 if (System.currentTimeMillis() - firstExecTime > 82800000L) {
-                    System.err.println("[InvoiceDownloadTask] 任务["+orderno+"]已重试23小时,触发兜底");
-                    omsDisposeService.handleInvoiceRetryAllFail(accessToken);
-                    //需要修改项目相关信息
-
-
-
+                    System.err.println("[InvoiceDownloadTask] 任务["+workInvoiceId+"]已重试23小时,触发兜底");
+                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票下载解析失败,请通过税务系统进行查看");
                     jedis.del(taskKey); // 兜底后删除Redis,保证数据一致
                     continue;
                 }
 
                 // ========== 核心:调用原始解析方法(移至此) ==========
-                executeInvoiceDownloadWithRetry(accessToken, orderno);
+                executeInvoiceDownloadWithRetry(accessToken, workInvoiceId);
             }
         } catch (Exception e) {
             e.printStackTrace();
@@ -85,15 +104,21 @@ public class InvoiceDownloadService {
         }
     }
 
-    // ========== 原始解析方法(完全保留核心逻辑,仅调整重试规则) ==========
-    private void executeInvoiceDownloadWithRetry(String accessToken, String orderno) {
+    /**
+     * 执行发票下载分析方法,获取发票下载接口以及其他信息
+     * @param accessToken   发票当时的accessToken
+     * @param workInvoiceId 发票的id(数据库中work_invoice中对应的id)
+     */
+
+    @Transactional(readOnly = false)
+    public void executeInvoiceDownloadWithRetry(String accessToken, String workInvoiceId) {
         try {
-            System.out.println("✅ [InvoiceDownloadTask] 开始解析发票,订单号:" + orderno);
+            System.out.println("✅ [InvoiceDownloadTask] 开始解析发票,订单号:" + workInvoiceId);
 
             // ========== 原始解析逻辑 —— 完全不变 ==========
             OMSInvoiceResultDownloadData getInvoiceInfo = new OMSInvoiceResultDownloadData();
             getInvoiceInfo.setDeptCode(deptCode);
-            getInvoiceInfo.setOrderno(orderno);
+            getInvoiceInfo.setOrderno(workInvoiceId);
             getInvoiceInfo.setIsDetail("1");
             String jsonInvoiceInfoStr = JSON.toJSONString(getInvoiceInfo);
             String base64Str = Base64.getEncoder().encodeToString(jsonInvoiceInfoStr.getBytes(StandardCharsets.UTF_8));
@@ -121,12 +146,32 @@ public class InvoiceDownloadService {
                     if (StringUtils.isNotBlank(invoceDownJsonStr)) {
                         OMSInvoiceDetailInfo invoiceInfo = JSONObject.parseObject(invoceDownJsonStr, OMSInvoiceDetailInfo.class);
                         if (invoiceInfo != null) {
-                            System.out.println("✅ [InvoiceDownloadTask] 解析成功!");
-                            System.out.println("allEinVno:" + invoiceInfo.getAllEinVno());
-                            System.out.println("ofdUrl:" + invoiceInfo.getOfdUrl());
-                            System.out.println("pdfUrl:" + invoiceInfo.getPdfUrl());
-                            System.out.println("xmlUrl:" + invoiceInfo.getXmlUrl());
-                            isSuccess = true;
+                            //根据开票id查询数据并进行修改
+                            //需要将错误信息保存到对应开票信息表中
+                            WorkInvoice workInvoice = workInvoiceService.get(workInvoiceId);
+                            if(null != workInvoice){
+                                if(StringUtils.isBlank(invoiceInfo.getOfdUrl())){
+                                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票信息获取失败,请到税务系统进行获取");
+                                }else{
+                                    System.out.println("✅ [InvoiceDownloadTask] 解析成功!");
+                                    workInvoice.setOmsEinVno(invoiceInfo.getAllEinVno());
+                                    workInvoice.setOmsOfdUrl(invoiceInfo.getOfdUrl());
+                                    workInvoice.setOmsPdfUrl(invoiceInfo.getPdfUrl());
+                                    workInvoice.setOmsXmlUrl(invoiceInfo.getXmlUrl());
+                                }
+                                //修改结果
+                                workInvoiceService.updateOmsUrlById(workInvoice);
+                                //同时 还需要将这个开票申请流程结束,并且向发起人发送通知
+
+
+
+
+                                isSuccess = true;
+                            }else{
+
+                                omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票信息获取失败,请到税务系统进行获取");
+                                return;
+                            }
                         }
                     }
                 }
@@ -134,17 +179,17 @@ public class InvoiceDownloadService {
 
             // ========== 重试规则调整:去掉次数限制,改为23小时超时(由定时任务控制) ==========
             if (isSuccess) {
-                System.out.println("✅ [InvoiceDownloadTask] 任务["+orderno+"]解析成功,删除Redis任务");
-                deleteInvoiceDownloadTaskFromRedis(orderno); // 成功删除Redis,保证数据一致
+                System.out.println("✅ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析成功,删除Redis任务");
+                deleteInvoiceDownloadTaskFromRedis(workInvoiceId); // 成功删除Redis,保证数据一致
                 return;
             } else {
-                System.out.println("❌ [InvoiceDownloadTask] 任务["+orderno+"]解析失败,5分钟后重试");
+                System.out.println("❌ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析失败,5分钟后重试");
                 // 无需手动延迟,由定时任务每5分钟扫描即可
             }
 
         } catch (Exception e) {
             e.printStackTrace();
-            System.err.println("❌ [InvoiceDownloadTask] 任务["+orderno+"]解析抛出异常,5分钟后重试");
+            System.err.println("❌ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析抛出异常,5分钟后重试");
             // 异常不删除Redis,由定时任务继续重试
         }
     }

+ 188 - 103
src/main/java/com/jeeplus/modules/workinvoice/service/OMS/OMSDisposeService.java

@@ -1,16 +1,23 @@
 package com.jeeplus.modules.workinvoice.service.OMS;
 
 import com.alibaba.fastjson.JSON;
+import com.jeeplus.common.config.Global;
 import com.jeeplus.common.utils.JedisUtils;
 import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.modules.sys.entity.MainDictDetail;
+import com.jeeplus.modules.sys.utils.DictUtils;
+import com.jeeplus.modules.workinvoice.dao.WorkInvoiceDao;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
 import com.jeeplus.modules.workinvoice.utils.OMSNationUtil;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import redis.clients.jedis.Jedis;
 
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -23,17 +30,22 @@ public class OMSDisposeService {
      * 给accessToken查询的有效时间设置为1天
      */
     private final int seconds = 86400;
-    private final String appId = "hyc1";
-    private final String appKey = "hyc1";
-    private final String deptCode = "500102204228315131";
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
     //如果接口访问不正确,可以循环访问的次数
     private final int remainRetryTimes = 20;
 
+
+    @Autowired
+    private WorkInvoiceDao workInvoiceDao;
+
     /**
      * 用于生成开蓝票信息
      * @param map
-     * @param workInvoiceId
+     * @param workInvoiceId 需要开票的开票信息id
      */
+    @Transactional(readOnly = false)
     public void doInvoiceBusiness(Map<String,Object> map, String workInvoiceId) {
         Jedis jedis = null;
         String accessToken = null;
@@ -42,7 +54,7 @@ public class OMSDisposeService {
             accessToken = jedis.get("OMSAccessToken");
             if(StringUtils.isBlank(accessToken)){
                 // 获取AccessToken 9998重试5次
-                accessToken = getOmsAccessTokenWithRetry(5, "accessToken");
+                accessToken = getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
                 if(StringUtils.isNotBlank(accessToken)){
                     jedis.setex("OMSAccessToken", seconds, accessToken);
                     map.put("token状态", "重新获取token成功,存入Redis");
@@ -55,29 +67,56 @@ public class OMSDisposeService {
                 map.put("token状态", "从Redis获取token成功");
             }
 
+
+
             OMSNationUtil util = new OMSNationUtil();
-            //生成开票基础信息
-            String string = util.neatenData(workInvoiceId);
+            //获取需要开票的发票信息
+            WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+            if(null != workInvoice){
+
+                List<MainDictDetail> billingContentList = DictUtils.getMainDictList("billing_content");
+                for (MainDictDetail dictDetail: billingContentList) {
+                    if(workInvoice.getBillingContent().equals(dictDetail.getValue())){
+                        workInvoice.setBillingContent(dictDetail.getLabel());
+                        break;
+                    }
+                }
 
-            OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
-            InvoiceTokenInfo.setAppId(appId);
-            InvoiceTokenInfo.setAppKey(appKey);
-            InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
-            InvoiceTokenInfo.setAccessToken(accessToken);
-            InvoiceTokenInfo.setData(string);
-            String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);
+                //生成开票基础信息
+                String string = util.neatenData(workInvoiceId,workInvoice);
 
-            String jsonInvoicResultStr = HttpPostJsonUtil.doPost("https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/order/upload", jsonInvoiceStr);
-            System.out.println("✅ 订单提交接口返回值:" + jsonInvoicResultStr);
-            map.put("订单接口信息", jsonInvoicResultStr);
+                OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
+                InvoiceTokenInfo.setAppId(appId);
+                InvoiceTokenInfo.setAppKey(appKey);
+                InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
+                InvoiceTokenInfo.setAccessToken(accessToken);
+                InvoiceTokenInfo.setData(string);
+                String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);
 
-            // 调用订单上传重试方法(包含所有码值规则)
-            if(StringUtils.isNotBlank(jsonInvoicResultStr)){
-                String finalAccessToken = accessToken;
-                String finalJsonInvoiceStr = jsonInvoiceStr;
-                executeOrderUploadRetry(remainRetryTimes, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, workInvoiceId, map,"accessToken", "blueTicket");
+                String jsonInvoicResultStr = HttpPostJsonUtil.doPost("https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/order/upload", jsonInvoiceStr);
+                System.out.println("✅ 订单提交接口返回值:" + jsonInvoicResultStr);
+                map.put("订单接口信息", jsonInvoicResultStr);
+
+                // 调用订单上传重试方法(包含所有码值规则)
+                if(StringUtils.isNotBlank(jsonInvoicResultStr)){
+                    //将上个节点获取的值存到数据库,留作redis崩溃后进行重新获取处理
+                    workInvoice.setOrderForGoodsResultStr(jsonInvoicResultStr);
+                    workInvoiceDao.updateOrderForGoodsResultStrById(workInvoice);
+
+
+                    String finalAccessToken = accessToken;
+                    String finalJsonInvoiceStr = jsonInvoiceStr;
+                    executeOrderUploadRetry(workInvoiceId, remainRetryTimes, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, map,"accessToken", "blueTicket");
+                }else{
+                    handleInvoiceRetryAllFail(jsonInvoiceStr,workInvoiceId, "蓝票发送订单生成接口返回值为空,开票失败。节点访问参数为:" + jsonInvoicResultStr); // 解析失败直接兜底
+
+                }
+            }else{
+                handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败"); // 解析失败直接兜底
             }
 
+
+
         } catch (Exception e) {
             e.printStackTrace();
             map.put("errorMsg", "系统异常:" + e.getMessage());
@@ -94,7 +133,8 @@ public class OMSDisposeService {
      * @param getKey
      * @return
      */
-    public String getOmsAccessTokenWithRetry(int remainRetryTimes, String getKey) {
+    @Transactional(readOnly = false)
+    public String getOmsAccessTokenWithRetry(int remainRetryTimes, String getKey, String workInvoiceId) {
         try {
             OMSAccessTokenInfo tokenInfo = new OMSAccessTokenInfo();
             tokenInfo.setAppId(appId);
@@ -115,6 +155,7 @@ public class OMSDisposeService {
             }
 
             String code = resultTokenInfo.getResult().getCode();
+            String message = resultTokenInfo.getResult().getMessage();
             if ("0000".equals(code)) {
                 String token = OMSNationUtil.extractAccessTokenFromBase64(getKey, resultTokenInfo.getData().toString());
                 System.out.println("✅ 获取AccessToken成功,重试次数剩余:"+remainRetryTimes);
@@ -124,13 +165,29 @@ public class OMSDisposeService {
                     int nextRetry = remainRetryTimes - 1;
                     System.err.println("⚠️ 获取AccessToken返回9998接口波动,30秒后重试,剩余次数:"+nextRetry);
                     Thread.sleep(30 * 1000);
-                    return getOmsAccessTokenWithRetry(nextRetry,getKey);
+                    return getOmsAccessTokenWithRetry(nextRetry, getKey, workInvoiceId);
                 } else {
                     System.err.println("❌ 获取AccessToken失败:连续5次返回9998,重试次数耗尽!");
+                    //需要将错误信息保存到对应开票信息表中
+                    WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+                    if(null != workInvoice){
+                        workInvoice.setOmsAccessTokenError("获取AccessToken失败,需要业务人员重新发起");
+                        //修改结果
+                        workInvoiceDao.updateAccessTokenErrorById(workInvoice);
+                    }
+                    //如果需要 可以将执行失败信息通过短信通知业务发起人
+
                     return "";
                 }
             } else {
-                System.err.println("❌ 获取AccessToken失败:返回业务错误码,code="+code);
+                System.err.println("❌ 获取AccessToken失败:返回业务错误码,code:"+code + "。 错误信息为:"+message);
+                //需要将错误信息保存到对应开票信息表中
+                WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+                if(null != workInvoice){
+                    workInvoice.setOmsAccessTokenError("获取AccessToken失败:返回业务错误码,code:"+code + "; 错误信息为:"+message + "。需要业务人员重新发起");
+                    //修改结果
+                    workInvoiceDao.updateAccessTokenErrorById(workInvoice);
+                }
                 return "";
             }
         } catch (InterruptedException e) {
@@ -146,23 +203,25 @@ public class OMSDisposeService {
 
 
     /**
-     * 对方法进行发起并进行处理
-     * @param remainRetryTimes
-     * @param jsonInvoicResultStr
-     * @param jsonInvoiceStr
-     * @param accessToken
-     * @param orderno
-     * @param map
-     * @param getKey
+     * 调用订单上传方法,用于执行开票处理
+     * @param workInvoiceId     发票id
+     * @param remainRetryTimes  允许接口重调次数
+     * @param jsonInvoicResultStr      上一个接口返回值
+     * @param jsonInvoiceStr    上一个接口参数值
+     * @param accessToken   接口访问accessToken
+     * @param map   作用不大,仅作为保留
+     * @param getKey    用来判定的参数值
      * @param initiationType    用来判定是什么类型的,比如蓝票、全类型红票、还是快捷红票
      */
-    public void executeOrderUploadRetry(int remainRetryTimes, String jsonInvoicResultStr, String jsonInvoiceStr, String accessToken, String orderno, Map<String,Object> map, String getKey, String initiationType) {
+
+    @Transactional(readOnly = false)
+    public void executeOrderUploadRetry(String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String jsonInvoiceStr, String accessToken, Map<String,Object> map, String getKey, String initiationType) {
         String jsonInvoicResult = "";
         try {
             OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
             if(null == resultTokenInfo || null == resultTokenInfo.getResult()){
                 System.err.println("❌ 订单上传解析失败,剩余重试次数:"+remainRetryTimes);
-                handleInvoiceRetryAllFail(accessToken); // 解析失败也执行兜底方法
+                handleInvoiceRetryAllFail(accessToken, workInvoiceId, ""); // 解析失败也执行兜底方法
                 return;
             }
             String code = resultTokenInfo.getResult().getCode();
@@ -176,18 +235,18 @@ public class OMSDisposeService {
                 System.out.println("✅ 订单上传返回0000成功,触发发票下载接口");
                 if(StringUtils.isNotBlank(jsonInvoicResult)){
                     if(initiationType.equals("fastRed")){
-                        orderno = jsonInvoicResult;
+                        workInvoiceId = jsonInvoicResult;
                     }
                 }
-                String finalOrderno = orderno;
+                String finalWorkInvoiceId = workInvoiceId;
                 // 存入Redis,由InvoiceDownloadTask接管
-                saveInvoiceDownloadTaskToRedis(accessToken, finalOrderno);
-                System.out.println("✅ 解析开票数据任务["+finalOrderno+"]已存入Redis,由InvoiceDownloadTask接管重试");
+                saveInvoiceDownloadTaskToRedis(accessToken, finalWorkInvoiceId);
+                System.out.println("✅ 解析开票数据任务["+finalWorkInvoiceId+"]已存入Redis,由InvoiceDownloadTask接管重试");
 
             } else if ("9998".equals(code)) {
                 // 通过redis调用 方式系统崩溃导致的数据丢失
                 // ✅ 9998 接口波动,放入redis中 用于定时任务进行处理
-                saveInvoiceRetryScheduledTaskToRedis(jsonInvoiceStr, accessToken, orderno, getKey, initiationType);
+                saveInvoiceRetryScheduledTaskToRedis(jsonInvoiceStr, accessToken, workInvoiceId, getKey, initiationType);
 
             } else if ("0003".equals(code)) {
                 // ✅ 0003 token失效:清除旧token → 重新获取token → 从头完整执行所有流程
@@ -202,18 +261,19 @@ public class OMSDisposeService {
                 } finally {
                     if(jedis != null) jedis.close();
                 }
-                doInvoiceBusiness(map, orderno);
+                doInvoiceBusiness(map, workInvoiceId);
             } else {
                 // ✅ ✅ ✅ 核心修正:0001/0002/其他任意错误码 → 直接调用handleInvoiceRetryAllFail执行修改系统信息逻辑 ✅ ✅ ✅
-                System.err.println("❌ 订单上传返回业务错误码:"+code+"(0001/0002等),立即执行失败兜底逻辑修改系统信息!");
+                System.err.println("❌ 订单上传返回业务错误码:"+code+",立即执行失败兜底逻辑修改系统信息!");
                 System.err.println("❌ 订单上传返回业务错误原因:"+message);
                 map.put("订单状态", "失败,错误码:"+code);
-                handleInvoiceRetryAllFail(accessToken); // 关键:直接执行你的修改逻辑,不抛异常、不重试
+                handleInvoiceRetryAllFail(accessToken, workInvoiceId, "订单上传返回业务错误码:"+code+",错误原因:"+message); // 解析失败也执行兜底方法
+                remainRetryTimes = 0;
             }
         } catch (Exception e) {
             e.printStackTrace();
             System.err.println("❌ 订单上传重试异常,剩余次数:"+remainRetryTimes);
-            handleInvoiceRetryAllFail(accessToken); // 异常也执行兜底方法
+            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "订单上传重试异常,请重新发起"); // 解析失败也执行兜底方法
         }
     }
 
@@ -222,11 +282,11 @@ public class OMSDisposeService {
      * 硬编码配置:初始重试次数20次,Redis过期时间1天(86400秒)
      * @param jsonInvoiceStr 订单上传的JSON参数
      * @param accessToken    AccessToken
-     * @param orderno        订单号
+     * @param workInvoiceId        订单号
      * @param getKey         获取token的key
      * @param initiationType 开票类型(蓝票/红票)
      */
-    public void saveInvoiceRetryScheduledTaskToRedis(String jsonInvoiceStr ,String accessToken, String orderno, String getKey, String initiationType) {
+    public void saveInvoiceRetryScheduledTaskToRedis(String jsonInvoiceStr ,String accessToken, String workInvoiceId, String getKey, String initiationType) {
         Jedis jedis = null;
         // 硬编码:初始重试次数20次
         final int INIT_RETRY_COUNT = 20;
@@ -235,7 +295,7 @@ public class OMSDisposeService {
 
         try {
             jedis = JedisUtils.getResource();
-            String redisKey = "OMS_invoice_retry_scheduled:" + orderno;
+            String redisKey = "OMS_invoice_retry_scheduled:" + workInvoiceId;
 
             // 先检查是否已存在,存在则复用原有重试次数,不存在则初始化20次
             String retryTimes = jedis.hget(redisKey, "retryTimes");
@@ -246,17 +306,17 @@ public class OMSDisposeService {
             // 存储所有需要的字段
             jedis.hset(redisKey, "jsonInvoiceStr", jsonInvoiceStr);
             jedis.hset(redisKey, "accessToken", accessToken);
-            jedis.hset(redisKey, "orderno", orderno);
+            jedis.hset(redisKey, "workInvoiceId", workInvoiceId);
             jedis.hset(redisKey, "getKey", getKey);
             jedis.hset(redisKey, "initiationType", initiationType);
             jedis.hset(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
             jedis.hset(redisKey, "retryTimes", retryTimes); // 重试次数
 
             jedis.expire(redisKey, REDIS_EXPIRE_SECONDS); // 1天过期
-            System.out.println("✅ 9998重试任务已存入Redis,订单号:" + orderno + ",剩余重试次数:" + retryTimes);
+            System.out.println("✅ 9998重试任务已存入Redis,订单号:" + workInvoiceId + ",剩余重试次数:" + retryTimes);
         } catch (Exception e) {
             e.printStackTrace();
-            System.err.println("❌ 存入9998重试任务到Redis失败,订单号:" + orderno + ",原因:" + e.getMessage());
+            System.err.println("❌ 存入9998重试任务到Redis失败,订单号:" + workInvoiceId + ",原因:" + e.getMessage());
         } finally {
             if (jedis != null) jedis.close();
         }
@@ -266,15 +326,15 @@ public class OMSDisposeService {
     /**
      * 新增开蓝票生成的下载用的redis
      * @param accessToken
-     * @param orderno
+     * @param workInvoiceId 开票的id
      */
-    public void saveInvoiceDownloadTaskToRedis(String accessToken, String orderno) {
+    public void saveInvoiceDownloadTaskToRedis(String accessToken, String workInvoiceId) {
         Jedis jedis = null;
         try {
             jedis = JedisUtils.getResource();
-            String redisKey = "OMS_invoice_download:" + orderno;
+            String redisKey = "OMS_invoice_download:" + workInvoiceId;
             jedis.hset(redisKey, "accessToken", accessToken);
-            jedis.hset(redisKey, "orderno", orderno);
+            jedis.hset(redisKey, "workInvoiceId", workInvoiceId);
             jedis.hset(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
             jedis.expire(redisKey, seconds); // 1天过期
         } catch (Exception e) {
@@ -288,22 +348,24 @@ public class OMSDisposeService {
      * 兜底方法
      * @param accessToken
      */
-    public void handleInvoiceRetryAllFail(String accessToken) {
+    @Transactional(readOnly = false)
+    public void handleInvoiceRetryAllFail(String accessToken, String workInvoiceId, String errorMessage) {
         // ============ 你的所有【修改系统信息/更新订单状态/写失败日志】逻辑 全部写在这里!!! ============
         try {
             System.err.println("============ 开始执行【失败兜底-系统信息修改逻辑】 ============");
+            WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+            if(null != workInvoice){
+                //将错误信息保存到数据库
+                workInvoice.setOmsErrorMessage(errorMessage);
+                workInvoice.setOmsAccessToken(accessToken);
+                //修改结果
+                workInvoiceDao.updateAccessTokenErrorById(workInvoice);
 
-            // 示例1:更新订单表的发票状态为【开票失败】
-            // orderService.updateInvoiceStatusByToken(accessToken, "99");
+                //将业务流程进行撤回
 
-            // 示例2:写入失败日志到数据库,方便人工排查和补偿处理
-            // invoiceFailLogService.saveFailLog(accessToken, "开票失败,错误码非0000/9998/0003", new Date());
-
-            // 示例3:标记该单据为异常,方便后台运维查看
-            // abnormalOrderService.saveAbnormalOrder(accessToken, "开票失败");
+                //通知发起人或者开票管理员
+            }
 
-            // 示例4:调用其他系统接口,同步失败状态
-            // otherSystemApi.notifyInvoiceFail(accessToken);
 
             System.err.println("============ 【失败兜底-系统信息修改逻辑】执行完成 ============");
 
@@ -320,7 +382,8 @@ public class OMSDisposeService {
      * @param map
      * @param allEinvno
      */
-    public void doFastRedInvoiceBusiness(Map<String,Object> map, String allEinvno) {
+    @Transactional(readOnly = false)
+    public void doFastRedInvoiceBusiness(Map<String,Object> map, String allEinvno, String workInvoiceId) {
         Jedis jedis = null;
         String accessToken = null;
         try {
@@ -328,7 +391,7 @@ public class OMSDisposeService {
             accessToken = jedis.get("OMSAccessToken");
             if(StringUtils.isBlank(accessToken)){
                 // 获取AccessToken 9998重试5次
-                accessToken = getOmsAccessTokenWithRetry(5, "accessToken");
+                accessToken = getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
                 if(StringUtils.isNotBlank(accessToken)){
                     jedis.setex("OMSAccessToken", seconds, accessToken);
                     map.put("token状态", "重新获取token成功,存入Redis");
@@ -360,7 +423,7 @@ public class OMSDisposeService {
             if(StringUtils.isNotBlank(jsonInvoicResultStr)){
                 String finalAccessToken = accessToken;
                 String finalJsonInvoiceStr = jsonInvoiceStr;
-                executeOrderUploadRetry(5, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, allEinvno, map, "orderno", "fastRed");
+                executeOrderUploadRetry(workInvoiceId, 5, jsonInvoicResultStr, finalJsonInvoiceStr, finalAccessToken, map, "orderno", "fastRed");
             }
 
         } catch (Exception e) {
@@ -377,11 +440,11 @@ public class OMSDisposeService {
     /**
      * 调用生成红字确认申请单
      * @param map
-     * @param applyNo
-     * @param originalInvno
+     * @param workInvoiceId 需要开票的开票id信息
+     * @param originalInvno 原蓝票发票号码(数电票号码)
      */
-    public void doAllScenarioRedInvoiceBusiness(Map<String,Object> map, String applyNo, String originalInvno) {
-        int seconds = 86400;
+    @Transactional(readOnly = false)
+    public void doAllScenarioRedInvoiceBusiness(Map<String,Object> map, String workInvoiceId, String originalInvno) {
         Jedis jedis = null;
         String accessToken = null;
         try {
@@ -389,7 +452,7 @@ public class OMSDisposeService {
             accessToken = jedis.get("OMSAccessToken");
             if(StringUtils.isBlank(accessToken)){
                 // 获取AccessToken 9998重试5次
-                accessToken = getOmsAccessTokenWithRetry(5, "accessToken");
+                accessToken = getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
                 if(StringUtils.isNotBlank(accessToken)){
                     jedis.setex("OMSAccessToken", seconds, accessToken);
                     map.put("token状态", "重新获取token成功,存入Redis");
@@ -403,27 +466,42 @@ public class OMSDisposeService {
             }
 
             OMSNationUtil util = new OMSNationUtil();
-            //生成红冲的数据
-            String string = util.neatenAllScenarioRedInvoiceData(applyNo, originalInvno);
-
-            OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
-            InvoiceTokenInfo.setAppId(appId);
-            InvoiceTokenInfo.setAppKey(appKey);
-            InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
-            InvoiceTokenInfo.setAccessToken(accessToken);
-            InvoiceTokenInfo.setData(string);
-            String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);
-
-            String jsonInvoicResultStr = HttpPostJsonUtil.doPost("https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/redApply/apply", jsonInvoiceStr);
-            System.out.println("✅ 全场景红冲订单提交接口返回值:" + jsonInvoicResultStr);
-            map.put("全场景红冲订单接口信息", jsonInvoicResultStr);
-
-            // 调用订单上传重试方法(包含所有码值规则)
-            if(StringUtils.isNotBlank(jsonInvoicResultStr)){
-                String finalAccessToken = accessToken;
-                queryRedInvoiceConfirm (5, jsonInvoicResultStr, finalAccessToken, applyNo);
+            //获取需要开票的发票信息
+            WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+            if(null != workInvoice){
+                //生成红冲的数据
+                String string = util.neatenAllScenarioRedInvoiceData(workInvoiceId, originalInvno,workInvoice);
+
+                OMSAccessTokenInfo InvoiceTokenInfo = new OMSAccessTokenInfo();
+                InvoiceTokenInfo.setAppId(appId);
+                InvoiceTokenInfo.setAppKey(appKey);
+                InvoiceTokenInfo.setExchangeId(UUID.randomUUID().toString());
+                InvoiceTokenInfo.setAccessToken(accessToken);
+                InvoiceTokenInfo.setData(string);
+                String jsonInvoiceStr = JSON.toJSONString(InvoiceTokenInfo);
+
+                String jsonInvoicResultStr = HttpPostJsonUtil.doPost("https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/redApply/apply", jsonInvoiceStr);
+                System.out.println("✅ 全场景红冲订单提交接口返回值:" + jsonInvoicResultStr);
+                map.put("全场景红冲订单接口信息", jsonInvoicResultStr);
+
+                // 调用订单上传重试方法(包含所有码值规则)
+                if(StringUtils.isNotBlank(jsonInvoicResultStr)){
+
+                    //将上个节点获取的值存到数据库,留作redis崩溃后进行重新获取处理
+                    workInvoice.setOrderForGoodsResultStr(jsonInvoicResultStr);
+                    workInvoiceDao.updateOrderForGoodsResultStrById(workInvoice);
+
+
+                    String finalAccessToken = accessToken;
+                    queryRedInvoiceConfirm (workInvoiceId, 5, jsonInvoicResultStr, finalAccessToken);
+                } else {
+                    handleInvoiceRetryAllFail(accessToken,workInvoiceId, "全场景红冲订单接口返回值为空,开票失败。节点申请参数为:"+ jsonInvoiceStr); // 解析失败直接兜底
+                }
+            }else{
+                handleInvoiceRetryAllFail("",workInvoiceId, "开票获取发票信息失败"); // 解析失败直接兜底
             }
 
+
         } catch (Exception e) {
             e.printStackTrace();
             map.put("errorMsg", "系统异常:" + e.getMessage());
@@ -439,51 +517,58 @@ public class OMSDisposeService {
      * @param remainRetryTimes  剩余重试次数
      * @param jsonInvoicResultStr   需要解析的返回值密文
      * @param accessToken   accessToken
-     * @param applyNo
+     * @param workInvoiceId 开票的id
      */
-    public void queryRedInvoiceConfirm (int remainRetryTimes, String jsonInvoicResultStr, String accessToken, String applyNo) {
+    @Transactional(readOnly = false)
+    public void queryRedInvoiceConfirm (String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String accessToken) {
         try {
             OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
             if(null == resultTokenInfo || null == resultTokenInfo.getResult()){
                 System.err.println("❌ 全场景红冲订单提交接口返回值解析失败,剩余重试次数:"+remainRetryTimes);
-                handleInvoiceRetryAllFail(accessToken); // 解析失败直接兜底
+                handleInvoiceRetryAllFail(accessToken, workInvoiceId, ""); // 解析失败也执行兜底方法
                 return;
             }
             String code = resultTokenInfo.getResult().getCode();
 
             if ("0000".equals(code)) {
                 // 仅存入Redis,删除所有业务执行逻辑
-                saveRedInvoiceTaskToRedis(remainRetryTimes, jsonInvoicResultStr, accessToken, applyNo, System.currentTimeMillis());
-                System.out.println("✅ 红冲任务["+applyNo+"]已存入Redis,由定时任务接管执行");
+                saveRedInvoiceTaskToRedis(workInvoiceId, remainRetryTimes, jsonInvoicResultStr, accessToken, workInvoiceId, System.currentTimeMillis());
+                System.out.println("✅ 红冲任务["+workInvoiceId+"]已存入Redis,由定时任务接管执行");
             } else {
                 String message = resultTokenInfo.getResult().getMessage();
-                System.err.println("❌ 订单上传返回业务错误码:"+code+",触发兜底逻辑!");
-                handleInvoiceRetryAllFail(accessToken);
+                System.err.println("❌ 全类型红冲--红字确认单查询接口返回业务错误码:"+code+",错误原因为:"+message);
+                handleInvoiceRetryAllFail(accessToken, workInvoiceId, "全类型红冲--红字确认单查询接口返回业务错误码:"+code+",错误原因为:"+message); // 解析失败也执行兜底方法
             }
         } catch (Exception e) {
             e.printStackTrace();
-            handleInvoiceRetryAllFail(accessToken);
+            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "全类型红冲--红字确认单查询接口发起失败,请重新发起"); // 解析失败也执行兜底方法
         }
     }
 
     /**
      * 存储红冲任务到Redis(供原有业务代码调用,参数完整)
+     * @param workInvoiceId 发票id
+     * @param remainRetryTimes  允许回调的次数,这里暂时未用到,仅作为保留参数
+     * @param jsonInvoicResultStr   全场景红冲订单提交接口返回值
+     * @param accessToken
+     * @param applyNo   发票id
+     * @param startTime 这个任务开始时间。限制:开始3天内如果双方没有全部确认,则本次红冲失败
      */
-    public void saveRedInvoiceTaskToRedis(int remainRetryTimes, String jsonInvoicResultStr, String accessToken, String applyNo, long startTime) {
+    public void saveRedInvoiceTaskToRedis(String workInvoiceId, int remainRetryTimes, String jsonInvoicResultStr, String accessToken, String applyNo, long startTime) {
         Jedis jedis = null;
         try {
             jedis = JedisUtils.getResource();
-            String redisKey = "OMS_red_invoice_task:" + applyNo;
+            String redisKey = "OMS_red_invoice_task:" + workInvoiceId;
             // 存储所有执行所需的参数(确保定时任务能独立执行)
             jedis.hset(redisKey, "remainRetryTimes", String.valueOf(remainRetryTimes));
             jedis.hset(redisKey, "jsonInvoicResultStr", jsonInvoicResultStr);
             jedis.hset(redisKey, "accessToken", accessToken);
-            jedis.hset(redisKey, "applyNo", applyNo);
+            jedis.hset(redisKey, "workInvoiceId", workInvoiceId);
             jedis.hset(redisKey, "startTime", String.valueOf(startTime));
             jedis.expire(redisKey, 259200 + 3600); // 3天+1小时过期
         } catch (Exception e) {
             e.printStackTrace();
-            handleInvoiceRetryAllFail(accessToken);
+            handleInvoiceRetryAllFail(accessToken, workInvoiceId, "全类型红冲--红字确认单查询接口发起失败,请重新发起"); // 解析失败也执行兜底方法
         } finally {
             if (jedis != null) jedis.close();
         }

+ 213 - 0
src/main/java/com/jeeplus/modules/workinvoice/service/OMS/RedInvoiceDownloadService.java

@@ -0,0 +1,213 @@
+package com.jeeplus.modules.workinvoice.service.OMS;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.common.config.Global;
+import com.jeeplus.common.utils.JedisUtils;
+import com.jeeplus.modules.workinvoice.entity.OMS.InvoiceDown.OMSInvoiceDetailInfo;
+import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
+import com.jeeplus.modules.workinvoice.entity.OMS.OMSInvoiceResultDownloadData;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
+import com.jeeplus.modules.workinvoice.service.WorkInvoiceService;
+import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
+import com.jeeplus.modules.workinvoice.utils.OMSNationUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import redis.clients.jedis.Jedis;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * OMS发票下载
+ */
+@Service
+@Transactional(readOnly = true)
+@Lazy
+public class RedInvoiceDownloadService {
+
+
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
+
+
+    @Autowired
+    private OMSDisposeService omsDisposeService;
+
+    @Autowired
+    private WorkInvoiceService workInvoiceService;
+
+
+
+    public void processRedInvoiceDownloadTasks() {
+        Jedis jedis = null;
+        try {
+            jedis = JedisUtils.getResource();
+            Set<String> downloadTaskKeys = jedis.keys("OMS_red_invoice_download:*");
+            if (downloadTaskKeys.isEmpty()) {
+                System.out.println("[RedInvoiceDownloadTask] 暂无待解析的发票任务");
+                return;
+            }
+
+            // 遍历所有解析任务
+            for (String taskKey : downloadTaskKeys) {
+                String accessToken = jedis.hget(taskKey, "accessToken");
+                String redInvOrderNo = jedis.hget(taskKey, "redInvOrderNo");    //红票对应的编号
+                String workInvoiceId = jedis.hget(taskKey, "workInvoiceId");
+                //获取当前redis中的accessToken
+                //如果有值,则使用当前redis中的accessToken,否则重新生成一个新的accessToken
+                accessToken = jedis.get("OMSAccessToken");
+
+                if(StringUtils.isBlank(accessToken)){
+                    // 获取AccessToken 9998重试5次
+                    accessToken = omsDisposeService.getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
+                    if(StringUtils.isNotBlank(accessToken)){
+                        jedis.setex("OMSAccessToken", 86400, accessToken);
+                        System.out.println("重新获取token成功,存入Redis");
+                    } else {
+                        accessToken = "";
+                        System.out.println("获取token失败");
+                        return;
+                    }
+                }
+
+                String firstExecTimeStr = jedis.hget(taskKey, "firstExecTime");
+
+                // 参数校验
+                if (StringUtils.isAnyBlank(accessToken, firstExecTimeStr)) {
+                    jedis.del(taskKey);
+                    continue;
+                }
+
+                long firstExecTime = Long.parseLong(firstExecTimeStr);
+                // ========== 改动1:超时判断改为23小时(82800000毫秒) ==========
+                // 23*60*60*1000 = 82800000 ms(原86400000是24小时)
+                if (System.currentTimeMillis() - firstExecTime > 82800000L) {
+                    System.err.println("[InvoiceDownloadTask] 任务["+workInvoiceId+"]已重试23小时,触发兜底");
+                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票下载解析失败,请通过税务系统进行查看");
+                    //需要修改项目相关信息
+
+
+
+                    jedis.del(taskKey); // 兜底后删除Redis,保证数据一致
+                    continue;
+                }
+
+                // ========== 核心:调用原始解析方法(移至此) ==========
+                executeInvoiceDownloadWithRetry(accessToken, redInvOrderNo, workInvoiceId);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (jedis != null) jedis.close();
+        }
+    }
+
+    /**
+     * 执行发票下载分析方法,获取发票下载接口以及其他信息
+     * @param accessToken   发票当时的accessToken
+     * @param redInvOrderNo 红字发票编号
+     * @param workInvoiceId 发票的id(数据库中work_invoice中对应的id)
+     */
+
+    @Transactional(readOnly = false)
+    public void executeInvoiceDownloadWithRetry(String accessToken, String redInvOrderNo, String workInvoiceId) {
+        try {
+            System.out.println("✅ [InvoiceDownloadTask] 开始解析发票,订单号:" + workInvoiceId);
+
+            // ========== 原始解析逻辑 —— 完全不变 ==========
+            OMSInvoiceResultDownloadData getInvoiceInfo = new OMSInvoiceResultDownloadData();
+            getInvoiceInfo.setDeptCode(deptCode);
+            getInvoiceInfo.setOrderno(redInvOrderNo);
+            getInvoiceInfo.setIsDetail("1");
+            String jsonInvoiceInfoStr = JSON.toJSONString(getInvoiceInfo);
+            String base64Str = Base64.getEncoder().encodeToString(jsonInvoiceInfoStr.getBytes(StandardCharsets.UTF_8));
+
+            OMSAccessTokenInfo invoiceDownInfo = new OMSAccessTokenInfo();
+            invoiceDownInfo.setAppId(appId);
+            invoiceDownInfo.setAppKey(appKey);
+            invoiceDownInfo.setExchangeId(UUID.randomUUID().toString());
+            invoiceDownInfo.setAccessToken(accessToken);
+            invoiceDownInfo.setData(base64Str);
+            String jsonInvoiceDownStr = JSON.toJSONString(invoiceDownInfo);
+
+            String invoiceResultStr = HttpPostJsonUtil.doPost(
+                    "https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/invoice/download",
+                    jsonInvoiceDownStr
+            );
+            System.out.println("[InvoiceDownloadTask] 发票解析结果:" + invoiceResultStr);
+
+            String invoceDownJsonStr = null;
+            boolean isSuccess = false;
+            if (StringUtils.isNotBlank(invoiceResultStr)) {
+                OMSAccessTokenInfo resultDownInfo = JSON.parseObject(invoiceResultStr, OMSAccessTokenInfo.class);
+                if (null != resultDownInfo.getResult() && "0000".equals(resultDownInfo.getResult().getCode())) {
+                    invoceDownJsonStr = OMSNationUtil.getDownFromBase64(resultDownInfo.getData().toString());
+                    if (StringUtils.isNotBlank(invoceDownJsonStr)) {
+                        OMSInvoiceDetailInfo invoiceInfo = JSONObject.parseObject(invoceDownJsonStr, OMSInvoiceDetailInfo.class);
+                        if (invoiceInfo != null) {
+                            //根据开票id查询数据并进行修改
+                            //需要将错误信息保存到对应开票信息表中
+                            WorkInvoice workInvoice = workInvoiceService.get(workInvoiceId);
+                            if(null != workInvoice){
+                                if(StringUtils.isBlank(invoiceInfo.getOfdUrl())){
+                                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票信息获取失败,请到税务系统进行获取");
+                                }else{
+                                    System.out.println("✅ [InvoiceDownloadTask] 解析成功!");
+                                    workInvoice.setOmsEinVno(invoiceInfo.getAllEinVno());
+                                    workInvoice.setOmsOfdUrl(invoiceInfo.getOfdUrl());
+                                    workInvoice.setOmsPdfUrl(invoiceInfo.getPdfUrl());
+                                    workInvoice.setOmsXmlUrl(invoiceInfo.getXmlUrl());
+                                }
+                                //修改结果
+                                workInvoiceService.updateOmsUrlById(workInvoice);
+                                //同时 还需要将这个开票申请流程结束,并且向发起人发送通知
+
+                                isSuccess = true;
+                            }else{
+
+                                omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票信息获取失败,请到税务系统进行获取");
+                                return;
+                            }
+                        }
+                    }
+                }
+            }
+
+            // ========== 重试规则调整:去掉次数限制,改为23小时超时(由定时任务控制) ==========
+            if (isSuccess) {
+                System.out.println("✅ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析成功,删除Redis任务");
+                deleteInvoiceDownloadTaskFromRedis(workInvoiceId); // 成功删除Redis,保证数据一致
+                return;
+            } else {
+                System.out.println("❌ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析失败,5分钟后重试");
+                // 无需手动延迟,由定时任务每5分钟扫描即可
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.err.println("❌ [InvoiceDownloadTask] 任务["+workInvoiceId+"]解析抛出异常,5分钟后重试");
+            // 异常不删除Redis,由定时任务继续重试
+        }
+    }
+
+    // ========== 辅助方法:删除Redis解析任务 ==========
+    private void deleteInvoiceDownloadTaskFromRedis(String orderno) {
+        Jedis jedis = null;
+        try {
+            jedis = JedisUtils.getResource();
+            jedis.del("OMS_red_invoice_download:" + orderno);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (jedis != null) jedis.close();
+        }
+    }
+
+}

+ 61 - 39
src/main/java/com/jeeplus/modules/workinvoice/service/OMS/RedInvoiceRetryScheduledService.java

@@ -2,13 +2,13 @@ package com.jeeplus.modules.workinvoice.service.OMS;
 
 import com.alibaba.fastjson.JSON;
 import com.jeeplus.common.utils.JedisUtils;
-import com.jeeplus.modules.ruralprojectrecords.web.RuralProjectSignatureOldMessageDisposeController;
+import com.jeeplus.modules.workinvoice.dao.WorkInvoiceDao;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import redis.clients.jedis.Jedis;
@@ -35,10 +35,14 @@ public class RedInvoiceRetryScheduledService {
     @Autowired
     private OMSDisposeService invoiceOMSService; // 替换为你实际的业务类名
 
+    @Autowired
+    private WorkInvoiceDao workInvoiceDao;
+
     /**
      * 定时任务:每5分钟执行一次(硬编码300000毫秒=5分钟)
      * fixedRate:固定间隔执行,直接写死5分钟
      */
+    @Transactional(readOnly = false)
     public void handleInvoice9998RetryTask() {
         Jedis jedis = null;
         try {
@@ -62,6 +66,24 @@ public class RedInvoiceRetryScheduledService {
                     String getKey = taskMap.get("getKey");
                     String initiationType = taskMap.get("initiationType");
                     String retryTimesStr = taskMap.get("retryTimes");
+                    String workInvoiceId = taskMap.get("workInvoiceId");
+
+                    //获取当前redis中的accessToken
+                    //如果有值,则使用当前redis中的accessToken,否则重新生成一个新的accessToken
+                    accessToken = jedis.get("OMSAccessToken");
+
+                    if(StringUtils.isBlank(accessToken)){
+                        // 获取AccessToken 9998重试5次
+                        accessToken = invoiceOMSService.getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
+                        if(StringUtils.isNotBlank(accessToken)){
+                            jedis.setex("OMSAccessToken", 86400, accessToken);
+                            System.out.println("重新获取token成功,存入Redis");
+                        } else {
+                            accessToken = "";
+                            System.out.println("获取token失败");
+                            return;
+                        }
+                    }
 
                     // 校验必要参数
                     if (StringUtils.isBlank(jsonInvoiceStr) || StringUtils.isBlank(orderNo) || StringUtils.isBlank(retryTimesStr)) {
@@ -77,7 +99,7 @@ public class RedInvoiceRetryScheduledService {
                     // 5. 判断是否耗尽次数(第20次,newRemainTimes=0)
                     if (newRemainTimes <= 0) {
                         System.err.println("❌ 订单" + orderNo + "重试次数已耗尽(累计20次),执行兜底逻辑handleInvoiceRetryAllFail");
-                        invoiceOMSService.handleInvoiceRetryAllFail(accessToken);
+                        invoiceOMSService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "重试次数已耗尽(累计20次),需要重新发起申请");
                         jedis.del(redisKey); // 删除Redis任务,不再重试
                         continue;
                     }
@@ -86,47 +108,47 @@ public class RedInvoiceRetryScheduledService {
                     jedis.hset(redisKey, "retryTimes", String.valueOf(newRemainTimes));
                     System.out.println("ℹ️ 订单" + orderNo + "剩余重试次数更新为:" + newRemainTimes);
 
-                    // 7. 调用OMS订单上传接口重试
-                    String newResultStr = HttpPostJsonUtil.doPost(
-                            "https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/order/upload",
-                            jsonInvoiceStr
-                    );
-                    System.out.println("✅ 订单" + orderNo + "9998重试调用接口返回:" + newResultStr);
-
-                    // 8. 调用核心重试逻辑处理返回结果
-                    Map<String, Object> resultMap = new HashMap<>();
-                    invoiceOMSService.executeOrderUploadRetry(
-                            newRemainTimes, // 传入扣减后的剩余次数
-                            newResultStr,
-                            jsonInvoiceStr,
-                            accessToken,
-                            orderNo,
-                            resultMap,
-                            getKey,
-                            initiationType
-                    );
-
-                    // 9. 根据接口返回码处理Redis任务
-                    OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(newResultStr, OMSAccessTokenInfo.class);
-                    if (resultTokenInfo != null && resultTokenInfo.getResult() != null) {
-                        String code = resultTokenInfo.getResult().getCode();
-                        if ("0000".equals(code)) {
-                            // 0000成功:删除Redis任务,结束重试
-                            System.out.println("✅ 订单" + orderNo + "9998重试成功,删除Redis任务");
-                            jedis.del(redisKey);
-                        } else if (!"9998".equals(code)) {
-                            // 非9998错误码(如0001/0002等):执行兜底+删除任务
-                            System.err.println("❌ 订单" + orderNo + "9998重试返回错误码:" + code + ",执行兜底逻辑");
-                            invoiceOMSService.handleInvoiceRetryAllFail(accessToken);
+                    WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
+                    if(null != workInvoice){
+                        // 7. 调用OMS订单上传接口重试
+                        String newResultStr = HttpPostJsonUtil.doPost(
+                                "https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/order/upload",
+                                jsonInvoiceStr
+                        );
+                        System.out.println("✅ 订单" + orderNo + "9998重试调用接口返回:" + newResultStr);
+
+                        workInvoice.setOrderForGoodsResultStr(newResultStr);
+
+                        workInvoiceDao.updateOrderForGoodsResultStrById(workInvoice);
+
+
+                        // 8. 根据接口返回码处理Redis任务
+                        OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(newResultStr, OMSAccessTokenInfo.class);
+                        if (resultTokenInfo != null && resultTokenInfo.getResult() != null) {
+                            // 9. 调用核心重试逻辑处理返回结果
+                            Map<String, Object> resultMap = new HashMap<>();
+                            invoiceOMSService.executeOrderUploadRetry(
+                                    workInvoiceId,
+                                    newRemainTimes, // 传入扣减后的剩余次数
+                                    newResultStr,
+                                    jsonInvoiceStr,
+                                    accessToken,
+                                    resultMap,
+                                    getKey,
+                                    initiationType
+                            );
                             jedis.del(redisKey);
-                        }
-                        // 仍返回9998:不删除Redis任务(已更新次数,下次定时任务继续重试)
-                    } else {
+                        }/* else {
                         // 解析返回结果失败:执行兜底+删除任务
                         System.err.println("❌ 订单" + orderNo + "9998重试返回结果解析失败,执行兜底逻辑");
-                        invoiceOMSService.handleInvoiceRetryAllFail(accessToken);
+                        invoiceOMSService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "9998重试返回结果解析失败");
                         jedis.del(redisKey);
+                    }*/
                     }
+
+
+
+
                 } catch (Exception e) {
                     e.printStackTrace();
                     System.err.println("❌ 处理订单" + orderNo + "9998重试任务异常:" + e.getMessage());

+ 97 - 37
src/main/java/com/jeeplus/modules/workinvoice/service/OMS/RedInvoiceScheduledService.java

@@ -2,9 +2,12 @@ package com.jeeplus.modules.workinvoice.service.OMS;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.common.config.Global;
 import com.jeeplus.common.utils.JedisUtils;
+import com.jeeplus.modules.workinvoice.dao.WorkInvoiceDao;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSRedInvoiceConfirmResponse;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
 import com.jeeplus.modules.workinvoice.utils.OMSNationUtil;
 import org.apache.commons.lang3.StringUtils;
@@ -18,17 +21,25 @@ import java.util.Calendar;
 import java.util.Set;
 import java.util.UUID;
 
+/**
+ * 红字确认单查询接口回调
+ * 用于发票红冲 或判定双方是否确认
+ */
 @Service
 @Transactional(readOnly = true)
 @Lazy
 public class RedInvoiceScheduledService {
 
-    private final String appId = "hyc1";
-    private final String appKey = "hyc1";
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
 
     @Autowired
     private OMSDisposeService omsDisposeService;
+    @Autowired
+    private WorkInvoiceDao workInvoiceDao;
 
+    @Transactional(readOnly = false)
     public void processAllRedInvoiceTasks() {
         // 时间窗口校验
         Calendar now = Calendar.getInstance();
@@ -55,6 +66,25 @@ public class RedInvoiceScheduledService {
                 String jsonInvoicResultStr = jedis.hget(taskKey, "jsonInvoicResultStr");
                 String accessToken = jedis.hget(taskKey, "accessToken");
                 String startTimeStr = jedis.hget(taskKey, "startTime");
+                String workInvoiceId = jedis.hget(taskKey, "workInvoiceId");
+
+                //获取当前redis中的accessToken
+                //如果有值,则使用当前redis中的accessToken,否则重新生成一个新的accessToken
+                accessToken = jedis.get("OMSAccessToken");
+
+                if(StringUtils.isBlank(accessToken)){
+                    // 获取AccessToken 9998重试5次
+                    accessToken = omsDisposeService.getOmsAccessTokenWithRetry(5, "accessToken", workInvoiceId);
+                    if(StringUtils.isNotBlank(accessToken)){
+                        jedis.setex("OMSAccessToken", 86400, accessToken);
+                        System.out.println("重新获取token成功,存入Redis");
+                    } else {
+                        accessToken = "";
+                        System.out.println("获取token失败");
+                        return;
+                    }
+                }
+
 
                 // 参数校验
                 if (StringUtils.isAnyBlank(remainRetryTimesStr, accessToken, startTimeStr)) {
@@ -65,7 +95,7 @@ public class RedInvoiceScheduledService {
                 // 转换参数并执行任务
                 int remainRetryTimes = Integer.parseInt(remainRetryTimesStr);
                 long startTime = Long.parseLong(startTimeStr);
-                executeRedInvoiceTask(applyNo, remainRetryTimes, jsonInvoicResultStr, accessToken, startTime);
+                executeRedInvoiceTask(applyNo, remainRetryTimes, jsonInvoicResultStr, accessToken, startTime, workInvoiceId);
             }
         } catch (Exception e) {
             e.printStackTrace();
@@ -76,35 +106,50 @@ public class RedInvoiceScheduledService {
 
 
     // ========== 1. 封装所有需要的业务逻辑(独立执行,不依赖原有业务类) ==========
+
     /**
-     * 核心:执行单个红冲任务的全流程逻辑
+     *
+     * @param applyNo       发票id
+     * @param remainRetryTimes  允许回调的次数,这里暂时未用到,仅作为保留参数
+     * @param jsonInvoicResultStr   全场景红冲订单提交接口返回值
+     * @param accessToken
+     * @param startTime     这个任务开始时间。限制:开始3天内如果双方没有全部确认,则本次红冲失败
+     * @param workInvoiceId 发票id
      */
-    private void executeRedInvoiceTask(String applyNo, int remainRetryTimes, String jsonInvoicResultStr, String accessToken, long startTime) {
+    @Transactional(readOnly = false)
+    public void executeRedInvoiceTask(String applyNo, int remainRetryTimes, String jsonInvoicResultStr, String accessToken, long startTime, String workInvoiceId) {
         try {
-            // 步骤1:3天超时判断
-            if (System.currentTimeMillis() - startTime > 259200000L) {
-                System.err.println("❌ 任务["+applyNo+"]超时,触发兜底逻辑");
-                omsDisposeService.handleInvoiceRetryAllFail(accessToken);
-                deleteRedInvoiceTaskFromRedis(applyNo);
-                return;
-            }
+            WorkInvoice workInvoice = workInvoiceDao.get(workInvoiceId);
 
-            // 步骤2:首次执行(有jsonInvoicResultStr)→ 解析结果,触发查询
-            if (StringUtils.isNotBlank(jsonInvoicResultStr)) {
-                OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
-                if (null == resultTokenInfo || null == resultTokenInfo.getResult() || !"0000".equals(resultTokenInfo.getResult().getCode())) {
-                    System.err.println("❌ 任务["+applyNo+"]解析失败,触发兜底");
-                    omsDisposeService.handleInvoiceRetryAllFail(accessToken);
+            if (null != workInvoice) {
+                // 步骤1:3天超时判断
+                if (System.currentTimeMillis() - startTime > 259200000L) {
+                    System.err.println("❌ 任务["+applyNo+"]超时,触发兜底逻辑");
+                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "");
                     deleteRedInvoiceTaskFromRedis(applyNo);
                     return;
                 }
-                // 解析成功,调用查询接口
-                callRedInvoiceConfirmQuery(remainRetryTimes, accessToken, applyNo, startTime);
-                return;
+
+                // 步骤2:首次执行(有jsonInvoicResultStr)→ 解析结果,触发查询
+                if (StringUtils.isNotBlank(jsonInvoicResultStr)) {
+                    OMSAccessTokenInfo resultTokenInfo = JSON.parseObject(jsonInvoicResultStr, OMSAccessTokenInfo.class);
+                    if (null == resultTokenInfo || null == resultTokenInfo.getResult() || !"0000".equals(resultTokenInfo.getResult().getCode())) {
+                        System.err.println("❌ 任务["+applyNo+"]解析失败,触发兜底");
+                        omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "");
+                        deleteRedInvoiceTaskFromRedis(applyNo);
+                        return;
+                    }
+                    // 解析成功,调用查询接口
+                    callRedInvoiceConfirmQuery(remainRetryTimes, accessToken, applyNo, startTime, workInvoiceId, workInvoice);
+                    return;
+                }
+
+                // 步骤3:后续重试(无jsonInvoicResultStr)→ 直接调用查询接口
+                callRedInvoiceConfirmQuery(remainRetryTimes, accessToken, applyNo, startTime, workInvoiceId, workInvoice);
             }
 
-            // 步骤3:后续重试(无jsonInvoicResultStr)→ 直接调用查询接口
-            callRedInvoiceConfirmQuery(remainRetryTimes, accessToken, applyNo, startTime);
+
+
 
         } catch (Exception e) {
             e.printStackTrace();
@@ -112,7 +157,7 @@ public class RedInvoiceScheduledService {
             if (remainRetryTimes > 1) {
                 saveRedInvoiceTaskToRedis(remainRetryTimes - 1, jsonInvoicResultStr, accessToken, applyNo, startTime);
             } else {
-                omsDisposeService.handleInvoiceRetryAllFail(accessToken);
+                omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "红字确认单查询接口回调失败,请重新发起");
                 deleteRedInvoiceTaskFromRedis(applyNo);
             }
         }
@@ -120,8 +165,14 @@ public class RedInvoiceScheduledService {
 
     /**
      * 调用红字确认单查询接口(核心业务逻辑)
+     * @param remainRetryTimes  允许回调的次数,这里暂时未用到,仅作为保留参数
+     * @param accessToken
+     * @param applyNo   发票id
+     * @param startTime 这个任务开始时间。限制:开始3天内如果双方没有全部确认,则本次红冲失败
+     * @param workInvoiceId 发票id
      */
-    private void callRedInvoiceConfirmQuery(int remainRetryTimes, String accessToken, String applyNo, long startTime) {
+    @Transactional(readOnly = false)
+    public void callRedInvoiceConfirmQuery(int remainRetryTimes, String accessToken, String applyNo, long startTime, String workInvoiceId, WorkInvoice workInvoice) {
         try {
             // 组装查询参数
             OMSNationUtil util = new OMSNationUtil();
@@ -134,7 +185,7 @@ public class RedInvoiceScheduledService {
             invoiceDownInfo.setData(queryData);
             String jsonInvoiceDownStr = JSON.toJSONString(invoiceDownInfo);
 
-            // 调用查询接口
+            // 调用查询接口(红字确认单接口,即红冲的第3步)
             String invoiceResultStr = HttpPostJsonUtil.doPost("https://oms-sandbox.einvoice.js.cn:7079/prod-api/output/server/redApply/query", jsonInvoiceDownStr);
             if (StringUtils.isBlank(invoiceResultStr)) {
                 throw new RuntimeException("查询接口返回空");
@@ -145,6 +196,10 @@ public class RedInvoiceScheduledService {
             if (null == resultDownInfo.getResult() || !"0000".equals(resultDownInfo.getResult().getCode())) {
                 throw new RuntimeException("查询接口返回错误码:" + (resultDownInfo.getResult() != null ? resultDownInfo.getResult().getCode() : "NULL"));
             }
+            //对 invoiceResultStr 这个调用的结果进行保存,方便redis崩溃重新处理
+            workInvoice.setConfirmationSlipResultStr(invoiceResultStr);
+            workInvoiceDao.updateConfirmationSlipResultStr(workInvoice);
+
 
             String invoceDownJsonStr = OMSNationUtil.extractFromBase64OnClassStr(resultDownInfo.getData().toString());
             OMSRedInvoiceConfirmResponse invoiceInfo = JSONObject.parseObject(invoceDownJsonStr, OMSRedInvoiceConfirmResponse.class);
@@ -179,15 +234,14 @@ public class RedInvoiceScheduledService {
 
                 case "01":
                 case "04":
-
-                    // 你原有注释和代码:访问成功后的后续方法
+                    // 访问成功后的后续方法
                     // 整体默认是自动开票
                     // 判定 开票状态是否是已开票(03)
                     if ("3".equals(invoiceInfo.getMakeStatus())) {
 
                         String redInvOrderNo = invoiceInfo.getRedInvOrderNo();
                         // 存入Redis,由InvoiceDownloadTask接管
-                        saveInvoiceDownloadTaskToRedis(accessToken, redInvOrderNo);
+                        saveInvoiceDownloadTaskToRedis(accessToken, redInvOrderNo, workInvoiceId);
                         System.out.println("✅ 解析开票数据任务["+redInvOrderNo+"]已存入Redis,由InvoiceDownloadTask接管重试");
 
                         deleteRedInvoiceTaskFromRedis(applyNo); // 成功后删除Redis
@@ -195,7 +249,8 @@ public class RedInvoiceScheduledService {
                         // 开票中,重新存入Redis等待下次重试
                         saveRedInvoiceTaskToRedis(remainRetryTimes, "", accessToken, applyNo, startTime);
                     } else {
-                        omsDisposeService.handleInvoiceRetryAllFail(accessToken);
+                        //到这的 应该返回值就是2 了  表示 开票失败
+                        omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "红冲开票失败,请确认后重新开票");
                         deleteRedInvoiceTaskFromRedis(applyNo);
                     }
                     break;
@@ -203,6 +258,7 @@ public class RedInvoiceScheduledService {
                 case "03":
                 case "15":
                     // 待确认,重新存入Redis等待下次重试
+                    //到这 说明 对方应该是没有进行发票确认,所以没能进行开票成功,需要对方进行确认
                     saveRedInvoiceTaskToRedis(remainRetryTimes, "", accessToken, applyNo, startTime);
                     break;
 
@@ -214,7 +270,7 @@ public class RedInvoiceScheduledService {
                 case "10":
                 case "16":
                     // 终止本次红冲操作,通知发起人确认后重新处理
-                    omsDisposeService.handleInvoiceRetryAllFail(accessToken);
+                    omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "本次红冲的开票作废或失败,请确认后重新开票");
                     // 失败后删除Redis任务
                     deleteRedInvoiceTaskFromRedis(applyNo);
                     break;
@@ -227,7 +283,7 @@ public class RedInvoiceScheduledService {
             if (remainRetryTimes > 1) {
                 saveRedInvoiceTaskToRedis(remainRetryTimes - 1, "", accessToken, applyNo, startTime);
             } else {
-                omsDisposeService.handleInvoiceRetryAllFail(accessToken);
+                omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "红冲开票失败,请确认后重新开票");
                 deleteRedInvoiceTaskFromRedis(applyNo);
             }
         }
@@ -270,15 +326,19 @@ public class RedInvoiceScheduledService {
     }
 
 
-
-    // ========== 新增:解析开票数据Redis操作(仅新增,不修改原有) ==========
-    private void saveInvoiceDownloadTaskToRedis(String accessToken, String orderno) {
+    /**
+     * 开票成功,需要进行下载解析的开票信息存储到redis中,进行下一步处理
+     * @param accessToken
+     * @param workInvoiceId 开票id
+     */
+    private void saveInvoiceDownloadTaskToRedis(String accessToken, String redInvOrderNo, String workInvoiceId) {
         Jedis jedis = null;
         try {
             jedis = JedisUtils.getResource();
-            String redisKey = "OMS_invoice_download:" + orderno;
+            String redisKey = "OMS_red_invoice_download:" + workInvoiceId;
             jedis.hset(redisKey, "accessToken", accessToken);
-            jedis.hset(redisKey, "orderno", orderno);
+            jedis.hset(redisKey, "redInvOrderNo", redInvOrderNo);
+            jedis.hset(redisKey, "workInvoiceId", workInvoiceId);
             jedis.hset(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
             jedis.expire(redisKey, 86400); // 1天过期
         } catch (Exception e) {

+ 6 - 0
src/main/java/com/jeeplus/modules/workinvoice/service/WorkInvoiceService.java

@@ -4064,4 +4064,10 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 	public List<WorkInvoiceCloud> getByInvoiceId(String id) {
 		return workInvoiceCloudDao.getByInvoiceId(id);
 	}
+
+
+	@Transactional(readOnly = false)
+	public  void updateOmsUrlById(WorkInvoice workInvoice){
+		dao.updateOmsUrlById(workInvoice);
+	}
 }

+ 44 - 29
src/main/java/com/jeeplus/modules/workinvoice/utils/OMSNationUtil.java

@@ -4,12 +4,14 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.collect.Lists;
+import com.jeeplus.common.config.Global;
 import com.jeeplus.modules.workinvoice.entity.OMS.InvoiceOMSImportInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.OrderItem;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSAllScenarioRedInvoiceInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSApplyItem;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSFastRedInvoiceInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSRedInvoiceConfirmQueryRequest;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
@@ -84,42 +86,69 @@ public class OMSNationUtil {
     }
 
 
-    private final String deptCode = "500102204228315131";
-    private final String sellerTaxno = "500102204228315131";
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
+    private static final String sellerName = Global.getConfig("sellerName");
+    private static final String sellerTaxno = Global.getConfig("sellerTaxno");
+
     private final String goodstaxno = "109050901"; //税收分类编码(电脑)
-    //private final String goodstaxno = "3070401"; //税收分类编码(餐饮)
     private final String goodsName = "电脑配件"; //税收分类编码(餐饮)
 
 
     /**
      * 开蓝票
      * 整理数据明文
+     * @param workInvoiceId 开票信息id,最好是将数据整体传过来
+     * @Param workInvoice   需要开票的信息
+     * @return
      */
-    public String neatenData(String orderno) {
+    public String neatenData(String workInvoiceId, WorkInvoice workInvoice) {
         List<OrderItem> orderItems = Lists.newArrayList();
 
         InvoiceOMSImportInfo omsImportInfo = new InvoiceOMSImportInfo();
         omsImportInfo.setDeptCode(deptCode);
-        omsImportInfo.setOrderno(orderno);
-        omsImportInfo.setSellerName("深圳市松胜电子有限公司");
+        omsImportInfo.setOrderno(workInvoiceId);
+        omsImportInfo.setSellerName(sellerName);
         omsImportInfo.setSellerTaxno(sellerTaxno); //销方纳税人识别号,必填
+        //* 开票类型(此处固定是蓝字票)
+        //* 01:红字发票 02:蓝字发票,必填
         omsImportInfo.setInvKind("02");
-        omsImportInfo.setInvType("01");
+        //* 发票类型
+        //* 01:数电票(增值税专用发票) 02:数电票(普通发票)等
+        if(workInvoice.getInvoiceType().equals("1")){
+            omsImportInfo.setInvType("01");
+        }else{
+            omsImportInfo.setInvType("02");
+        }
 
         //添加购买方信息
         omsImportInfo.setBuyerName("深圳市爱人人餐饮服务有限公司");
         omsImportInfo.setBuyerTaxno("500102203117204029");
+        omsImportInfo.setBuyerAddr(""); //购买方地址
+        omsImportInfo.setBuyerPhone(""); //购买方电话
+        omsImportInfo.setBuyerBank(""); //购买方开户行名称
+        omsImportInfo.setBuyerBankaccount(""); //购买方开户银行账号
+        omsImportInfo.setBuyerNationality(""); //购买方国籍/地区代码
+        omsImportInfo.setBuyerIdcardno(""); //购买方证件号码
+        omsImportInfo.setBuyerIdcartype(""); //购买方证件类型
+        omsImportInfo.setBuyerType(""); //购买方类型(0:企业;1:自然人) 默认企业
+        omsImportInfo.setBuyerCellphone(""); //购买方手机号
+        omsImportInfo.setBuyerEmail(""); //购买方邮箱
         //omsImportInfo.setRemarks("zzsytdm01,xfsytdm01");
 
 
         OrderItem orderItem = new OrderItem();
+        //发票行编号
         orderItem.setLineCode("1");
+        //发票行性质
         orderItem.setLineType("00");
         //添加商品名称
-        //orderItem.setGoodsCode("1001");
-        orderItem.setGoodsName(goodsName);
+        orderItem.setGoodsName(workInvoice.getBillingContent());
+        //商品数量
         orderItem.setQty(BigDecimal.valueOf(1));
         //orderItem.setPrice(BigDecimal.valueOf(100.00));
+        //默认含税
         orderItem.setPriceTaxFlag("1"); //含税状态 0不含税;1含税
         orderItem.setTaxrate(BigDecimal.valueOf(0.01)); //税率
         //orderItem.setAmount(BigDecimal.valueOf(100.00)); //金额
@@ -130,23 +159,6 @@ public class OMSNationUtil {
 
         orderItems.add(orderItem);
 
-
-        /*OrderItem orderItem2 = new OrderItem();
-        orderItem2.setLineCode("2");
-        orderItem2.setLineType("00");
-        //添加商品名称
-        orderItem2.setGoodsName("电脑固态硬盘256G");
-        orderItem2.setQty(BigDecimal.valueOf(1));
-        orderItem2.setPrice(BigDecimal.valueOf(50.00));
-        orderItem2.setPriceTaxFlag("0"); //含税状态
-        orderItem2.setTaxrate(BigDecimal.valueOf(0.01)); //税率
-        orderItem2.setAmount(BigDecimal.valueOf(50.00)); //金额
-        orderItem2.setTax(BigDecimal.valueOf(0.5));//税额
-        orderItem2.setTaxamount(BigDecimal.valueOf(50.5));//含税金额
-        orderItem2.setGoodstaxno(goodstaxno);//税收分类编码
-
-
-        orderItems.add(orderItem2);*/
         omsImportInfo.setOrderItems(orderItems);
         // 核心一行代码:Java对象 转 JSON格式字符串
         String jsonStr = JSON.toJSONString(omsImportInfo);
@@ -183,16 +195,19 @@ public class OMSNationUtil {
     /**
      * 开红票(全类型)
      * 整理数据明文
+     * @param workInvoiceId 开票的id
+     * @param originalInvno 原蓝票发票号码(数电票号码)
+     * @return
      */
-    public String neatenAllScenarioRedInvoiceData(String applyNo, String originalInvno) {
+    public String neatenAllScenarioRedInvoiceData(String workInvoiceId, String originalInvno, WorkInvoice workInvoice) {
         List<OMSApplyItem> orderItems = Lists.newArrayList();
 
         OMSAllScenarioRedInvoiceInfo omsAllScenarioRedInvoiceInfo = new OMSAllScenarioRedInvoiceInfo();
         omsAllScenarioRedInvoiceInfo.setDeptCode(deptCode);
-        omsAllScenarioRedInvoiceInfo.setApplyNo(applyNo);   //红字发票申请单号
+        omsAllScenarioRedInvoiceInfo.setApplyNo(workInvoiceId);   //红字发票申请单号 即 开票的id
         omsAllScenarioRedInvoiceInfo.setApplyIdentity("0");
         omsAllScenarioRedInvoiceInfo.setSellerTaxno(sellerTaxno);
-        omsAllScenarioRedInvoiceInfo.setSellerName("深圳市松胜电子有限公司");
+        omsAllScenarioRedInvoiceInfo.setSellerName(sellerName);
         //添加购买方信息
         omsAllScenarioRedInvoiceInfo.setBuyerName("深圳市爱人人餐饮服务有限公司");
         omsAllScenarioRedInvoiceInfo.setBuyerTaxno("500102203117204029");

+ 4 - 2
src/main/java/com/jeeplus/modules/workinvoice/utils/RedInvoiceScheduledTask.java

@@ -1,6 +1,7 @@
 package com.jeeplus.modules.workinvoice.utils;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.jeeplus.common.config.Global;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.fastRed.OMSRedInvoiceConfirmResponse;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -18,8 +19,9 @@ import java.util.UUID;
 @Component
 public class RedInvoiceScheduledTask {
 
-    private final String appId = "hyc1";
-    private final String appKey = "hyc1";
+    private static final String appId = Global.getConfig("appId");
+    private static final String appKey = Global.getConfig("appKey");
+    private static final String deptCode = Global.getConfig("deptCode");
 
 
     // ========== 2. 定时任务入口(8:00-18:00每小时执行) ==========

+ 12 - 1
src/main/resources/jeeplus.properties

@@ -411,4 +411,15 @@ SZCLOUD_STATUS: sz
 publicPassword: Xg@sys9hB2!xWm
 
 #���ݹ�������
-szPublicPassword: Xg@sys9hB2!xWm
+szPublicPassword: Xg@sys9hB2!xWm
+
+
+#OMS开票相关参数
+omsAppId: hyc1
+omsAppKey: hyc1
+#组织编码
+omsDeptCode: 500102204228315131
+#销售方纳税人识别号
+sellerTaxno: 500102204228315131
+#销售方名称
+sellerName: 深圳市松胜电子有限公司

+ 34 - 0
src/main/resources/mappings/modules/workinvoice/WorkInvoiceDao.xml

@@ -3336,4 +3336,38 @@
 	</select>
 
 
+	<update id="updateAccessTokenErrorById">
+		UPDATE work_invoice SET
+			oms_access_token_error = #{omsAccessTokenError},
+			oms_access_token = #{omsAccessToken},
+			oms_error_message = #{omsErrorMessage}
+		WHERE id = #{id}
+	</update>
+
+
+	<update id="updateOmsUrlById">
+		UPDATE work_invoice SET
+			oms_ein_vno = #{omsEinVno},
+			oms_ofd_url = #{omsOfdUrl},
+			oms_pdf_url = #{omsPdfUrl},
+			oms_xml_url = #{omsXmlUrl}
+		WHERE id = #{id}
+	</update>
+
+
+	<update id="updateOrderForGoodsResultStrById">
+		UPDATE work_invoice SET
+			order_for_goods_result_str = #{orderForGoodsResultStr}
+		WHERE id = #{id}
+	</update>
+
+
+	<update id="updateConfirmationSlipResultStr">
+		UPDATE work_invoice SET
+			confirmation_slip_result_str = #{confirmationSlipResultStr}
+		WHERE id = #{id}
+	</update>
+
+
+
 </mapper>