Преглед изворни кода

feat: 增加一键票据回传接口;

hanchaolong пре 1 недеља
родитељ
комит
9e3c6780be

+ 5 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/InvoiceService.java

@@ -19,4 +19,9 @@ public interface InvoiceService {
     void updateInvoice() throws JsonProcessingException;
 
     Map returnShop(InvoiceRequest invoiceRequest) throws JsonProcessingException;
+
+    /**
+     * 一键回传票据(异步执行)
+     */
+    void batchReturnShop();
 }

+ 128 - 0
jd-logistics-modules/jd-logistics-system/src/main/java/com/ruoyi/logistics/service/impl/InvoiceServiceImpl.java

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.ruoyi.common.core.utils.StringUtils;
 import com.ruoyi.common.redis.service.RedisIdGenerator;
+import com.ruoyi.common.redis.service.RedisService;
 import com.ruoyi.logistics.config.InvoiceConfig;
 import com.ruoyi.logistics.domain.InvoiceRequest;
 import com.ruoyi.logistics.domain.OrderRequest;
@@ -18,6 +19,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -26,6 +28,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -54,6 +57,9 @@ public class InvoiceServiceImpl implements InvoiceService {
     @Autowired
     private IRptFinancialMonthSummaryService rptFinancialMonthSummaryService;
 
+    @Autowired
+    private RedisService redisService;
+
     /**
      * 电子发票专票开据
      * @param InvoiceRequest 开票相入参
@@ -254,6 +260,128 @@ public class InvoiceServiceImpl implements InvoiceService {
         return shopMap;
     }
 
+    /**
+     * 一键回传票据(异步执行)
+     * 处理所有已开票(status=3)的月度账单,回传到商城
+     */
+    @Override
+    @Async
+    public void batchReturnShop() {
+        // Redis 唯一 key,防止重复处理
+        String redisKey = "invoice:batch_return_shop:processing";
+        
+        // 检查是否已有任务在处理
+        Boolean isProcessing = redisService.hasKey(redisKey);
+        if (Boolean.TRUE.equals(isProcessing)) {
+            logger.warn("一键回传票据任务正在执行中,请勿重复提交");
+            return;
+        }
+        
+        try {
+            // 设置 Redis key,过期时间设置为 1 小时,防止异常情况下 key 永久存在
+            redisService.setCacheObject(redisKey, "1", 1L, TimeUnit.HOURS);
+            logger.info("开始执行一键回传票据任务");
+            
+            // 查询所有 status=3(已开票)的月度账单
+            List<Map<String, Object>> invoiceList = jdbcTemplate.queryForList(
+                "SELECT summary_id, payable_amount, bw_djbh, kprq, dept_name, invoice_name, dept_code, leader " +
+                "FROM rpt_financial_month_summary a " +
+                "LEFT JOIN sys_dept b ON a.dept_id = b.dept_id " +
+                "WHERE a.status = '3'"
+            );
+            
+            if (invoiceList == null || invoiceList.isEmpty()) {
+                logger.info("没有需要回传的票据数据");
+                return;
+            }
+            
+            logger.info("共找到 {} 条已开票数据需要回传", invoiceList.size());
+            
+            int successCount = 0;
+            int failureCount = 0;
+            
+            // 遍历处理每一条数据
+            for (int i = 0; i < invoiceList.size(); i++) {
+                Map<String, Object> map = invoiceList.get(i);
+                Long summaryId = ((Number) map.get("summary_id")).longValue();
+                
+                try {
+                    logger.info("正在处理第 {}/{} 条数据,summaryId: {}", i + 1, invoiceList.size(), summaryId);
+                    
+                    // 构建回传商城的请求参数
+                    String url = invoiceConfig.getShopUrl();
+                    HttpHeaders headers = new HttpHeaders();
+                    headers.setContentType(MediaType.APPLICATION_JSON);
+                    
+                    BigDecimal free = new BigDecimal(map.get("payable_amount").toString());
+                    OrderRequest request = new OrderRequest();
+                    request.setOrderNumber(map.get("bw_djbh").toString());
+                    request.setSkuId("787");
+                    request.setGzTime(map.get("kprq") != null ? map.get("kprq").toString() : LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    request.setOutCode("3204");
+                    request.setUserId(map.get("leader") != null ? map.get("leader").toString() : "");
+                    request.setUserCompanyName(map.get("invoice_name") != null ? map.get("invoice_name").toString() : "");
+                    request.setSapCode(map.get("dept_code") != null ? map.get("dept_code").toString() : "");
+                    request.setShopName(map.get("dept_name") != null ? map.get("dept_name").toString() : "");
+                    request.setPayType(1);
+                    request.setProdName("快递服务");
+                    request.setProdCount("1");
+                    request.setBuyMoney(free);   // 销售金额 含税
+                    request.setBuyPrice(free);   // 采购单价 含税
+                    request.setSaleMoney(free);  // 销售金额 含税
+                    request.setPurchaseRate(BigDecimal.valueOf(0.06));    // 进项税率
+                    request.setPrice(free);       // 销售单价 含税
+                    
+                    BigDecimal[] result = TaxAmountCalculator.calculateFromTaxIncluded(free, BigDecimal.valueOf(0.06));
+                    request.setBuyMoneyNoTax(result[0]);   // 采购金额(不含税)
+                    request.setJxse(result[1]);            // 进项税额
+                    
+                    BigDecimal[] result1 = TaxAmountCalculator.calculateFromTaxIncluded(free, BigDecimal.valueOf(0.06));
+                    request.setOutputRate(BigDecimal.valueOf(0.06));      // 销项税率
+                    request.setXxse(result1[1]);            // 销项税额
+                    
+                    HttpEntity<OrderRequest> entity = new HttpEntity<>(request, headers);
+                    ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
+                    
+                    ObjectMapper objectMapper = new ObjectMapper();
+                    Map<String, Object> shopMap = objectMapper.readValue(
+                        response.getBody(),
+                        new TypeReference<Map<String, Object>>() {}
+                    );
+                    
+                    // 判断回传是否成功
+                    if ("0".equals(shopMap.get("code").toString())) {
+                        // 更新状态为 4(已回传商城)
+                        RptFinancialMonthSummary rptFinancialMonthSummary = new RptFinancialMonthSummary();
+                        rptFinancialMonthSummary.setStatus("4");
+                        rptFinancialMonthSummary.setSummaryId(summaryId);
+                        rptFinancialMonthSummaryService.updateRptFinancialMonthSummary(rptFinancialMonthSummary);
+                        successCount++;
+                        logger.info("第 {}/{} 条数据回传成功,summaryId: {}", i + 1, invoiceList.size(), summaryId);
+                    } else {
+                        failureCount++;
+                        logger.error("第 {}/{} 条数据回传失败,summaryId: {}, 原因: {}", 
+                            i + 1, invoiceList.size(), summaryId, shopMap.get("msg"));
+                    }
+                    
+                } catch (Exception e) {
+                    failureCount++;
+                    logger.error("第 {}/{} 条数据处理异常,summaryId: {}", i + 1, invoiceList.size(), summaryId, e);
+                }
+            }
+            
+            logger.info("一键回传票据任务执行完成,总计: {}, 成功: {}, 失败: {}", 
+                invoiceList.size(), successCount, failureCount);
+            
+        } catch (Exception e) {
+            logger.error("一键回传票据任务执行异常", e);
+        } finally {
+            // 删除 Redis key,允许下次执行
+            redisService.deleteObject(redisKey);
+            logger.info("一键回传票据任务结束,Redis 锁已释放");
+        }
+    }
+
 
 
 }