Kaynağa Gözat

发票批量收款部分功能

徐滕 2 ay önce
ebeveyn
işleme
782b920a46
20 değiştirilmiş dosya ile 573 ekleme ve 175 silme
  1. 44 15
      src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralCostProjectMessageController.java
  2. 31 2
      src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralProjectSignatureOldMessageDisposeController.java
  3. 5 56
      src/main/java/com/jeeplus/modules/sys/utils/ALiYunSmsUtil.java
  4. 8 0
      src/main/java/com/jeeplus/modules/workinvoice/dao/WorkInvoiceDao.java
  5. 6 0
      src/main/java/com/jeeplus/modules/workinvoice/dao/WorkInvoiceReceiptDao.java
  6. 122 0
      src/main/java/com/jeeplus/modules/workinvoice/entity/WorkInvoiceReceiptInfo.java
  7. 2 2
      src/main/java/com/jeeplus/modules/workinvoice/service/OMS/InvoiceDownloadService.java
  8. 2 2
      src/main/java/com/jeeplus/modules/workinvoice/service/OMS/RedInvoiceDownloadService.java
  9. 114 44
      src/main/java/com/jeeplus/modules/workinvoice/service/WorkInvoiceService.java
  10. 3 1
      src/main/java/com/jeeplus/modules/workinvoice/thread/ApprovalThread.java
  11. 3 0
      src/main/java/com/jeeplus/modules/workinvoice/thread/RedApprovalThread.java
  12. 118 28
      src/main/java/com/jeeplus/modules/workinvoice/web/WorkInvoiceAllController.java
  13. 9 0
      src/main/java/com/jeeplus/modules/workinvoicedetail/dao/WorkInvoiceDetailDao.java
  14. 5 7
      src/main/java/com/jeeplus/modules/workreimbursement/service/WorkReimbursementNewService.java
  15. 7 7
      src/main/resources/jeeplus.properties
  16. 30 1
      src/main/resources/mappings/modules/workinvoice/WorkInvoiceDao.xml
  17. 35 2
      src/main/resources/mappings/modules/workinvoice/WorkInvoiceReceiptDao.xml
  18. 19 0
      src/main/resources/mappings/modules/workinvoicedetail/WorkInvoiceDetailDao.xml
  19. 8 8
      src/main/webapp/WEB-INF/tags/table/importExcel.tag
  20. 2 0
      src/main/webapp/webpage/modules/workinvoice/workInvoiceAllList.jsp

+ 44 - 15
src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralCostProjectMessageController.java

@@ -1627,26 +1627,55 @@ public class RuralCostProjectMessageController extends BaseController {
      */
     @ResponseBody
     @RequestMapping(value = "getWorkUser")
-    public WorkStaffCertificate getWorkUser(WorkStaffBasicInfo workStaffBasicInfo){
+    public WorkStaffCertificate getWorkUser(WorkStaffBasicInfo workStaffBasicInfo) {
+        // 1. 基础信息查询和详情加载(保留原逻辑)
         workStaffBasicInfo = workStaffBasicInfoService.getWorkStaffBasicInfoByUserId(workStaffBasicInfo.getId());
         workStaffBasicInfoService.queryDetails(workStaffBasicInfo);
-        WorkStaffCertificate workStaffCertificate=new WorkStaffCertificate();
-        for (WorkStaffCertificate workStaff:workStaffBasicInfo.getCertificateList()){
-            //判定是否是一级造价师 161一级造价师 171二级造价师
-            if ("161".equals( workStaff.getName()) || "171".equals( workStaff.getName())){
-                workStaffCertificate.setName(workStaff.getName());
-                workStaffCertificate.setNum(workStaff.getNum());
-                List<MainDictDetail> mainDictDetails=DictUtils.getMainDictListOnProjectAdvent("certificate_major");
-                for (MainDictDetail mainDictDetail:mainDictDetails){
-                    if (mainDictDetail.getValue().equals(workStaff.getMajor())){
-                        workStaffCertificate.setMajor(mainDictDetail.getLabel());
-                        workStaffCertificate.setIdCard(workStaffBasicInfo.getIdCard());
-                        return workStaffCertificate;
-                    }
+
+        WorkStaffCertificate workStaffCertificate = new WorkStaffCertificate();
+        // 先给证书对象设置身份证号(原逻辑,无论是否找到证书都要设置)
+        workStaffCertificate.setIdCard(workStaffBasicInfo.getIdCard());
+
+        // 防御性判空:避免证书列表为null导致空指针异常(原代码未处理,重要优化)
+        if (workStaffBasicInfo.getCertificateList() == null || workStaffBasicInfo.getCertificateList().isEmpty()) {
+            return workStaffCertificate;
+        }
+
+        WorkStaffCertificate targetCert = null;
+        // 2. 第一步:优先查找161(一级造价师)证书,找到则赋值给目标证书
+        for (WorkStaffCertificate workStaff : workStaffBasicInfo.getCertificateList()) {
+            if ("161".equals(workStaff.getName())) {
+                targetCert = workStaff;
+                break; // 找到161直接退出循环,无需继续遍历
+            }
+        }
+
+        // 3. 第二步:若未找到161,再查找171(二级造价师)证书
+        if (targetCert == null) {
+            for (WorkStaffCertificate workStaff : workStaffBasicInfo.getCertificateList()) {
+                if ("171".equals(workStaff.getName())) {
+                    targetCert = workStaff;
+                    break; // 找到171直接退出循环
                 }
             }
         }
-        workStaffCertificate.setIdCard(workStaffBasicInfo.getIdCard());
+
+        // 4. 若找到目标证书(161/171),则处理专业名称映射并赋值
+        if (targetCert != null) {
+            workStaffCertificate.setName(targetCert.getName());
+            workStaffCertificate.setNum(targetCert.getNum());
+            // 获取证书专业的字典列表
+            List<MainDictDetail> mainDictDetails = DictUtils.getMainDictListOnProjectAdvent("certificate_major");
+            // 匹配字典,将专业编码转为中文名称
+            for (MainDictDetail mainDictDetail : mainDictDetails) {
+                if (mainDictDetail.getValue().equals(targetCert.getMajor())) {
+                    workStaffCertificate.setMajor(mainDictDetail.getLabel());
+                    break; // 匹配到专业后直接退出,无需继续遍历字典
+                }
+            }
+        }
+
+        // 5. 未找到161和171时,返回仅含身份证号的空证书对象(保留原逻辑)
         return workStaffCertificate;
     }
 

+ 31 - 2
src/main/java/com/jeeplus/modules/ruralprojectrecords/web/RuralProjectSignatureOldMessageDisposeController.java

@@ -40,6 +40,7 @@ import com.jeeplus.modules.workinvoice.entity.OMS.InvoiceDown.OMSInvoiceDetailIn
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSAccessTokenInfo;
 import com.jeeplus.modules.workinvoice.entity.OMS.OMSInvoiceResultDownloadData;
 import com.jeeplus.modules.workinvoice.entity.TemporaryInvoiceInfo;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.service.OMS.OMSDisposeService;
 import com.jeeplus.modules.workinvoice.service.WorkInvoiceService;
 import com.jeeplus.modules.workinvoice.utils.HttpPostJsonUtil;
@@ -134,6 +135,8 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
     private final static String appsecret = Global.getConfig("appsecret");
     private final static String signature = Global.getConfig("signature");
 
+    private static final String omsUrl = Global.getConfig("omsUrl");
+
 
     /**
      * 签章文件处理
@@ -982,7 +985,8 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
     @RequestMapping(value = "/executeInvoiceDownload")
     @ResponseBody
     @Transactional(readOnly = false)
-    public void executeInvoiceDownload(String accessToken, String orderno) {
+    public Map<String,Object> executeInvoiceDownload(String accessToken, String orderno) {
+        Map map = new HashMap<>();
         try {
 
             OMSInvoiceResultDownloadData getInvoiceInfo = new OMSInvoiceResultDownloadData();
@@ -1000,7 +1004,7 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
             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);
+            String invoiceResultStr = HttpPostJsonUtil.doPost(omsUrl + "/prod-api/output/server/invoice/download", jsonInvoiceDownStr);
             System.out.println("发票开票结果invoiceResultStr:" + invoiceResultStr);
 
             String invoceDownJsonStr = null;
@@ -1016,6 +1020,10 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
                             System.out.println("getOfdUrl:" + invoiceInfo.getOfdUrl());
                             System.out.println("getPdfUrl:" + invoiceInfo.getPdfUrl());
                             System.out.println("getXmlUrl:" + invoiceInfo.getXmlUrl());
+                            map.put("allEinVno",invoiceInfo.getAllEinVno());
+                            map.put("getOfdUrl",invoiceInfo.getOfdUrl());
+                            map.put("getPdfUrl",invoiceInfo.getPdfUrl());
+                            map.put("getXmlUrl",invoiceInfo.getXmlUrl());
                         }
                     }
                 }
@@ -1026,6 +1034,27 @@ public class RuralProjectSignatureOldMessageDisposeController extends BaseContro
         } catch (Exception e) {
             e.printStackTrace();
         }
+        return map;
+    }
+
+    @RequestMapping(value = "/saveInvoiceDownloadTaskToRedis")
+    @ResponseBody
+    @Transactional(readOnly = false)
+    public void saveInvoiceDownloadTaskToRedis(String accessToken, String workInvoiceId, String informType) {
+        Jedis jedis = null;
+        try {
+            jedis = JedisUtils.getResource();
+            String redisKey = "OMS_invoice_download:" + workInvoiceId;
+            jedis.hset(redisKey, "accessToken", accessToken);
+            jedis.hset(redisKey, "workInvoiceId", workInvoiceId);
+            jedis.hset(redisKey, "informType", informType);
+            jedis.hset(redisKey, "firstExecTime", String.valueOf(System.currentTimeMillis()));
+            jedis.expire(redisKey, 14400); // 2小时过期
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (jedis != null) jedis.close();
+        }
     }
 
 

+ 5 - 56
src/main/java/com/jeeplus/modules/sys/utils/ALiYunSmsUtil.java

@@ -182,7 +182,7 @@ public class ALiYunSmsUtil {
      * @return
      * @throws Exception
      */
-    public static HashMap<String,Object> omsWorkInvoiceSuccessSms(String phoneNumbers, String number) {
+    public static HashMap<String,Object> omsWorkInvoiceSms(String phoneNumbers, String number, String result) {
         HashMap<String,Object> map = new HashMap<>();
         try {
             Config config = new Config()
@@ -191,66 +191,15 @@ public class ALiYunSmsUtil {
                     .setEndpoint(ENDPOINT);
 
             Client client = new Client(config);
+            // 构造包含多个参数的JSON字符串
+            String templateParams = String.format("{\"unitName\":\"%s\",\"number\":\"%s\",\"result\":\"%s\"}", "兴光项目",number,result);
             SendSmsRequest sendSmsRequest = new SendSmsRequest()
                     .setPhoneNumbers(phoneNumbers)
                     .setSignName(SIGNNAME)
                     /*.setTemplateCode("SMS_472770050")*/
-                    .setTemplateCode("SMS_501905029")
+                    .setTemplateCode("SMS_501985251")
                     //此处是设计模版的时候预留的变量${number}
-                    .setTemplateParam(String.format("{\"number\":\"%s\"}", number));
-
-            SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
-            System.out.println(sendSmsResponse);
-            if(sendSmsResponse.body.code.equals("isv.BUSINESS_LIMIT_CONTROL")){
-                if(sendSmsResponse.body.message.contains("触发分钟级流控")){
-                    map.put("message","手机号获取验证码次数已触发每分钟可发送数量上限,请稍后进行重试!");
-                }else if(sendSmsResponse.body.message.contains("触发小时级流控")){
-                    map.put("message","手机号获取验证码次数已触发每小时可发送数量上限,请稍后进行重试!");
-                }else{
-                    map.put("message","手机号获取验证码次数已触发每小时可发送数量上限,请稍后进行重试!");
-                }
-                //触发云通信流控限制 每小时限量
-                map.put("statusCode",10001);
-            }else if(sendSmsResponse.body.code.contains("isv.AMOUNT_NOT_ENOUGH")){
-                //触发账户余额不足
-                map.put("statusCode",10002);
-            }else if(sendSmsResponse.body.code.contains("isv.DAY_LIMIT_CONTROL")){
-                //触发触发日发送限额
-                map.put("statusCode",10003);
-            }else if(sendSmsResponse.body.code.contains("OK")){
-                map.put("statusCode",sendSmsResponse.getStatusCode());
-            }
-            return map;
-        } catch (Throwable error) {  // 捕获所有异常和错误
-            map.put("statusCode", 500);
-            map.put("message", "发送失败:" + error.getMessage());
-            return map;
-        }
-    }
-
-    /**
-     * oms开票成功通知短信
-     * @param phoneNumbers  被通知手机号
-     * @param number    被通知的参数(此处为开票编号)
-     * @return
-     * @throws Exception
-     */
-    public static HashMap<String,Object> omsWorkInvoiceErrorSms(String phoneNumbers, String number) {
-        HashMap<String,Object> map = new HashMap<>();
-        try {
-            Config config = new Config()
-                    .setAccessKeyId(ACCESS_KEY_ID)
-                    .setAccessKeySecret(ACCESS_KEY_SECRET)
-                    .setEndpoint(ENDPOINT);
-
-            Client client = new Client(config);
-            SendSmsRequest sendSmsRequest = new SendSmsRequest()
-                    .setPhoneNumbers(phoneNumbers)
-                    .setSignName(SIGNNAME)
-                    /*.setTemplateCode("SMS_472770050")*/
-                    .setTemplateCode("SMS_501750017")
-                    //此处是设计模版的时候预留的变量${number}
-                    .setTemplateParam(String.format("{\"number\":\"%s\"}", number));
+                    .setTemplateParam(templateParams);
 
             SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
             System.out.println(sendSmsResponse);

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

@@ -11,6 +11,7 @@ import com.jeeplus.common.persistence.CrudDao;
 import com.jeeplus.common.persistence.annotation.MyBatisDao;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoice;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceProjectRelation;
+import com.jeeplus.modules.workinvoice.entity.WorkInvoiceReceiptInfo;
 import com.jeeplus.modules.workinvoice.entity.WorkInvoiceTaxClassificationCode;
 import org.apache.ibatis.annotations.Param;
 import org.apache.poi.ss.formula.functions.T;
@@ -280,4 +281,11 @@ public interface WorkInvoiceDao extends CrudDao<WorkInvoice> {
 	 * @return
 	 */
 	Integer updateRedInvoiceJsonByWorkInvoiceId(WorkInvoice workInvoice);
+
+	/**
+	 * 批量修改(拼接多条UPDATE)
+	 * @param list 待修改的数据集(moneyEqualList/moneyPartialList)
+	 * @return 影响的行数
+	 */
+	int batchUpdateByForeach(@Param("list") List<WorkInvoiceReceiptInfo> list);
 }

+ 6 - 0
src/main/java/com/jeeplus/modules/workinvoice/dao/WorkInvoiceReceiptDao.java

@@ -29,4 +29,10 @@ public interface WorkInvoiceReceiptDao extends CrudDao<WorkInvoiceReceipt> {
      * @return
      */
     List<WorkInvoiceReceipt> getByInvoiceNumber(@Param("list") List<String> invoiceNumberList);
+
+    /**
+     * 批量导入
+     * @param list
+     */
+    void batchInsert(@Param("list") List<WorkInvoiceReceipt> list);
 }

+ 122 - 0
src/main/java/com/jeeplus/modules/workinvoice/entity/WorkInvoiceReceiptInfo.java

@@ -0,0 +1,122 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.modules.workinvoice.entity;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.google.common.collect.Lists;
+import com.jeeplus.common.persistence.ActEntity;
+import com.jeeplus.common.persistence.DataEntity;
+import com.jeeplus.common.utils.excel.annotation.ExcelField;
+import com.jeeplus.modules.projectrecord.entity.ProjectRecords;
+import com.jeeplus.modules.sys.entity.Area;
+import com.jeeplus.modules.sys.entity.Office;
+import com.jeeplus.modules.sys.entity.User;
+import com.jeeplus.modules.sys.entity.Workattachment;
+import com.jeeplus.modules.workclientinfo.entity.WorkClientInfo;
+import com.jeeplus.modules.workinvoicedetail.entity.WorkInvoiceDetail;
+import org.activiti.engine.history.HistoricProcessInstance;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.runtime.ProcessInstance;
+import org.activiti.engine.task.Task;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 开票管理Entity
+ * @author
+ * @version
+ */
+public class WorkInvoiceReceiptInfo extends DataEntity<WorkInvoiceReceiptInfo> {
+
+	private static final long serialVersionUID = 1L;
+
+	private String invoiceNumber;	//发票号码
+	private String BuyerName;	//购方名称
+	private String money;	//收款金额
+	private String errorMessage;	//错误信息
+	private String distinctStr;	//用于去重用的临时参数
+	private Integer moneyFlag;	//用于对比金额是否符合的判定条件(0:相同。-1:小于.1:大于)
+	private String invoiceId;	//开票信息id
+	private Date receiptMoneyDate;	//全部收款时间
+	private Integer receiptMoney;	//收款状态
+
+	@ExcelField(title="发票号码", align=2, sort=1)
+	public String getInvoiceNumber() {
+		return invoiceNumber;
+	}
+
+	public void setInvoiceNumber(String invoiceNumber) {
+		this.invoiceNumber = invoiceNumber;
+	}
+
+	@ExcelField(title="购方企业名称", align=2, sort=2)
+	public String getBuyerName() {
+		return BuyerName;
+	}
+
+	public void setBuyerName(String buyerName) {
+		BuyerName = buyerName;
+	}
+
+	@ExcelField(title="收款金额", align=2, sort=3)
+	public String getMoney() {
+		return money;
+	}
+
+	public void setMoney(String money) {
+		this.money = money;
+	}
+
+	@ExcelField(title="提醒", align=2, sort=4, type = 1)
+	public String getErrorMessage() {
+		return errorMessage;
+	}
+
+	public void setErrorMessage(String errorMessage) {
+		this.errorMessage = errorMessage;
+	}
+
+	public String getDistinctStr() {
+		return distinctStr;
+	}
+
+	public void setDistinctStr(String distinctStr) {
+		this.distinctStr = distinctStr;
+	}
+
+	public Integer getMoneyFlag() {
+		return moneyFlag;
+	}
+
+	public void setMoneyFlag(Integer moneyFlag) {
+		this.moneyFlag = moneyFlag;
+	}
+
+	public String getInvoiceId() {
+		return invoiceId;
+	}
+
+	public void setInvoiceId(String invoiceId) {
+		this.invoiceId = invoiceId;
+	}
+
+	public Date getReceiptMoneyDate() {
+		return receiptMoneyDate;
+	}
+
+	public void setReceiptMoneyDate(Date receiptMoneyDate) {
+		this.receiptMoneyDate = receiptMoneyDate;
+	}
+
+	public Integer getReceiptMoney() {
+		return receiptMoney;
+	}
+
+	public void setReceiptMoney(Integer receiptMoney) {
+		this.receiptMoney = receiptMoney;
+	}
+}

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

@@ -92,8 +92,8 @@ public class InvoiceDownloadService {
                 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小时,触发兜底");
+                if (System.currentTimeMillis() - firstExecTime > 12600000L) {
+                    System.err.println("[InvoiceDownloadTask] 任务["+workInvoiceId+"]已重试3.5小时,触发兜底");
                     omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票下载解析失败,请通过税务系统进行查看", informType);
                     jedis.del(taskKey); // 兜底后删除Redis,保证数据一致
                     continue;

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

@@ -93,8 +93,8 @@ public class RedInvoiceDownloadService {
                 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小时,触发兜底");
+                if (System.currentTimeMillis() - firstExecTime > 12600000L) {
+                    System.err.println("[InvoiceDownloadTask] 任务["+workInvoiceId+"]已重试3.5小时,触发兜底");
                     omsDisposeService.handleInvoiceRetryAllFail(accessToken, workInvoiceId, "发票下载解析失败,请通过税务系统进行查看", informType);
                     //需要修改项目相关信息
 

+ 114 - 44
src/main/java/com/jeeplus/modules/workinvoice/service/WorkInvoiceService.java

@@ -72,7 +72,9 @@ import org.springframework.transaction.annotation.Transactional;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.LocalDate;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.regex.Pattern;
@@ -4347,7 +4349,7 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 				HashMap<String,Object> result = null;
 				try{
 					//调用工具类返回结果
-					result = ALiYunSmsUtil.omsWorkInvoiceSuccessSms(user.getMobile(), workInvoice.getNumber());
+					result = ALiYunSmsUtil.omsWorkInvoiceSms(user.getMobile(), workInvoice.getNumber(), "成功");
 					Integer statusCode = (Integer) result.get("statusCode");
 					if (200 == statusCode) {
 						System.out.println("进入获取密码修改短信通知接口2。获取阿里云短信通知成功");
@@ -4529,7 +4531,7 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 						HashMap<String,Object> result = null;
 						try{
 							//调用工具类返回结果
-							result = ALiYunSmsUtil.omsWorkInvoiceErrorSms(cwuser.getMobile(), workInvoice.getNumber());
+							result = ALiYunSmsUtil.omsWorkInvoiceSms(cwuser.getMobile(), workInvoice.getNumber(), "失败");
 							Integer statusCode = (Integer) result.get("statusCode");
 							if (200 == statusCode) {
 								System.out.println("进入获取密码修改短信通知接口2。获取阿里云短信通知成功");
@@ -4580,13 +4582,14 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 
 		//查询开票人员的手机号
 		User user = userDao.get(workInvoice.getCreateBy().getId());
+		//user.setMobile("18164266544");
 		if (StringUtils.isNotBlank(user.getMobile()) && mobilePattern.matcher(user.getMobile().trim()).matches() ) {
 			//验证手机号是否已经注册
 			if(userDao.validateMobile("mobile") == null){
 				HashMap<String,Object> result = null;
 				try{
 					//调用工具类返回结果
-					result = ALiYunSmsUtil.omsWorkInvoiceErrorSms(user.getMobile(), workInvoice.getNumber());
+					result = ALiYunSmsUtil.omsWorkInvoiceSms(user.getMobile(), workInvoice.getNumber(), "失败");
 					Integer statusCode = (Integer) result.get("statusCode");
 					if (200 == statusCode) {
 						System.out.println("进入获取密码修改短信通知接口2。获取阿里云短信通知成功");
@@ -4647,6 +4650,7 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 	/**
 	 * 查询同项目名称、价格、材料名称的数量是否只有一个
 	 */
+	@Transactional(readOnly = false)
 	public Map<String, List<WorkInvoiceReceiptInfo>> distinctProjectMaterialStorage(
 			List<WorkInvoiceReceiptInfo> workInvoiceReceiptInfo) {
 
@@ -4656,6 +4660,9 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 		Set<String> seen = new HashSet<>();
 		List<WorkInvoiceReceiptInfo> uniqueList = new ArrayList<>();      // 本次去重后的有效值
 		List<WorkInvoiceReceiptInfo> duplicateList = new ArrayList<>();   // 本次自身重复值
+		List<WorkInvoiceReceiptInfo> alreadyExistList = new ArrayList<>();   // 已经有收款信息的数据
+		List<WorkInvoiceReceiptInfo> inexistenceList = new ArrayList<>();   // 数据表中不存在的的数据
+		List<WorkInvoiceReceiptInfo> moneyMismatchingList = new ArrayList<>();   // 数据表中金额大于开票价的的数据
 
 		for (WorkInvoiceReceiptInfo item : workInvoiceReceiptInfo) {
 			String distinctStr = item.getDistinctStr();
@@ -4683,70 +4690,133 @@ public class WorkInvoiceService extends CrudService<WorkInvoiceDao, WorkInvoice>
 		List<WorkInvoiceReceipt> alreadyExistReceiptList = workInvoiceReceiptDao.getByInvoiceNumber(invoiceNumberList);
 
 		//遍历有效值和查出来的已经存在的收款信息进行对比,若存在同一发票号的信息,金额进行累加,若金额大于应收金额,则表示该条数据存在问题,所小于则表示部分收款,若等于 则表示完全收款,改成已收款
+		// 迭代器方式:内层循环安全删除
 		for (WorkInvoiceReceipt workInvoiceReceipt : alreadyExistReceiptList) {
-			for (WorkInvoiceReceiptInfo invoiceReceiptInfo : uniqueList) {
-				if(workInvoiceReceipt.getInvoiceNumber().equals(invoiceReceiptInfo.getInvoiceNumber())){
+			// 内层用迭代器遍历uniqueList,支持安全删除
+			Iterator<WorkInvoiceReceiptInfo> iterator = uniqueList.iterator();
+			while (iterator.hasNext()) {
+				WorkInvoiceReceiptInfo invoiceReceiptInfo = iterator.next();
+				if (workInvoiceReceipt.getInvoiceNumber().equals(invoiceReceiptInfo.getInvoiceNumber())) {
+					alreadyExistList.add(invoiceReceiptInfo); // 加入待处理列表
+					invoiceReceiptInfo.setInvoiceId(workInvoiceReceipt.getInvoiceId());
+					iterator.remove(); // 迭代器安全删除,不会触发并发修改异常
+				}
+			}
+		}
 
+		//对剩下的有效数据进行处理
+		//判定金额和开票额是否一致,若一致,则表示全部收款,若小于开票额,表示部分收款,若大于,应当是填写错误,进行抛出,不进行导入
+		invoiceNumberList = Lists.newArrayList();
+		for (WorkInvoiceReceiptInfo invoiceReceiptInfo : uniqueList) {
+			invoiceNumberList.add(invoiceReceiptInfo.getInvoiceNumber());
+		}
+		//根据 invoiceNumberList 去进行查询已经存在的数据信息
+		List<WorkInvoiceDetail> invoiceDetailList = workInvoiceDetailDao.getInvoiceDetailByNumber(invoiceNumberList);
+		for (WorkInvoiceDetail workInvoiceDetail : invoiceDetailList) {
+			for (WorkInvoiceReceiptInfo info : uniqueList) {
+				if(workInvoiceDetail.getNumber().equals(info.getInvoiceNumber())){
+					info.setInvoiceId(workInvoiceDetail.getInvoiceId());
 				}
-				invoiceNumberList.add(invoiceReceiptInfo.getInvoiceNumber());
 			}
 		}
 
+		//如果两者数据量不同,说明 要么发票号错误,要么该发票没有录入,需要抛出,其余的再进行比较金额
+		if(invoiceDetailList.size() != uniqueList.size()){
+			//将invoiceDetailList 和uniqueList 进行对比,如果uniqueList中存在 但是 invoiceDetailList 不存在,则就要将这个值从uniqueList删除并写入到 inexistenceList中
 
-		// 第二步:获取历史数据并处理其distinctStr
-		/*List<WorkInvoiceReceiptInfo> historyInfoList = dao.getByProjectId(projectId);
-		for (WorkInvoiceReceiptInfo info : historyInfoList) {
-			// 组合历史数据的distinctStr(材料名称+规格+含税价格)
-			// 处理可能的null值,转为空字符串
-			String invoiceName = (info.getInvoiceNumber() != null) ? info.getInvoiceNumber() : "";
-			String buyerName = (info.getBuyerName() != null) ? info.getBuyerName() : "";
-			String money = (info.getMoney() != null) ? info.getMoney().toString() : "";
-			// 以逗号分隔组合
-			String resultStr = invoiceName + "," + buyerName + "," + money;
+			// 1. 提取invoiceDetailList中所有发票号存入Set,用于O(1)快速判断是否存在(核心优化)
+			Set<String> detailInvoiceNumberSet = new HashSet<>();
+			for (WorkInvoiceDetail detail : invoiceDetailList) {
+				detailInvoiceNumberSet.add(detail.getNumber());
+			}
+
+			// 2. 迭代器遍历uniqueList,安全删除+收集不存在的元素(避免ConcurrentModificationException)
+			Iterator<WorkInvoiceReceiptInfo> iterator = uniqueList.iterator();
+			while (iterator.hasNext()) {
+				WorkInvoiceReceiptInfo uniqueInfo = iterator.next();
+				String uniqueInvoiceNo = uniqueInfo.getInvoiceNumber();
+				// 判断:uniqueList的发票号 在invoiceDetailList中不存在
+				if (!detailInvoiceNumberSet.contains(uniqueInvoiceNo)) {
+					inexistenceList.add(uniqueInfo); // 加入不存在的列表
+					iterator.remove(); // 迭代器安全删除,不会触发并发修改异常
+				}
+			}
 		}
+		//到此处 说明数据已经匹配几乎完成,现在还需要对有效的参数的金额进行匹配
+		for (WorkInvoiceDetail workInvoiceDetail : invoiceDetailList) {
+			for (WorkInvoiceReceiptInfo unique : uniqueList) {
+				if(workInvoiceDetail.getNumber().equals(unique.getInvoiceNumber())){
+					//判定金额是否相同,小于为-1  相同为0 大于为1
+					String totalMoney = workInvoiceDetail.getTotalMoney();
+					String money = unique.getMoney();
+					BigDecimal totalMoneyB = new BigDecimal(totalMoney);
+					BigDecimal moneyB = new BigDecimal(money);
+					// 核心比较:直接得到-1/0/1的结果
+					int compareResult = moneyB.compareTo(totalMoneyB);
+					// 后续可根据结果做业务处理
+					if (compareResult == 1) {
+						// totalMoney 小于 money 业务逻辑
+						//说明新导入的额度要大于开票额度
+						unique.setMoneyFlag(1);
+					} else if (compareResult == 0) {
+						// 金额相等 业务逻辑
+						//说明金额相等
+						unique.setMoneyFlag(0);
+					} else {
+						// totalMoney 大于 money 业务逻辑
+						//说明部分收款
+						unique.setMoneyFlag(-1);
+					}
+				}
 
-		// 第三步:筛选历史数据中的distinctStr,用于比对
-		Set<String> historyDistinctSet = new HashSet<>();
-		for (WorkInvoiceReceiptInfo historyItem : historyInfoList) {
-			String historyDistinctStr = historyItem.getDistinctStr();
-			if (historyDistinctStr != null) {
-				historyDistinctSet.add(historyDistinctStr);
 			}
 		}
 
-		// 第四步:从uniqueList中移除与历史数据重复的项,并加入重复列表
-		// 使用迭代器支持边遍历边删除
+		//对处理完的数据进行区分,大于1的值不能进行导入,需要剔除
 		Iterator<WorkInvoiceReceiptInfo> iterator = uniqueList.iterator();
 		while (iterator.hasNext()) {
-			WorkInvoiceReceiptInfo currentItem = iterator.next();
-			String currentDistinctStr = currentItem.getDistinctStr();
-
-			// 处理null值(从uniqueList移除并加入重复列表)
-			if (currentDistinctStr == null) {
-				iterator.remove();
-				duplicateList.add(currentItem);
-				continue;
+			WorkInvoiceReceiptInfo info = iterator.next();
+			// 判定moneyFlag为1(导入金额超开票金额),剔除并收集
+			if (info.getMoneyFlag() == 1) {
+				moneyMismatchingList.add(info); // 加入无效数据列表
+				iterator.remove(); // 从有效列表uniqueList中安全删除
 			}
+		}
+
+		//此处可以对剩余的数据进行处理了
 
-			// 与历史数据重复:从uniqueList移除并加入重复列表
-			if (historyDistinctSet.contains(currentDistinctStr)) {
-				iterator.remove();
-				duplicateList.add(currentItem);
+		List<WorkInvoiceReceipt> insertReceiptList = Lists.newArrayList();
+		for (WorkInvoiceReceiptInfo info : uniqueList) {
+			if (info.getMoneyFlag() == 0) {
+				info.setReceiptMoneyDate(new Date());
+				info.setReceiptMoney(1);
+			} else if(info.getMoneyFlag() == -1){
+				info.setReceiptMoney(2);
 			}
+			WorkInvoiceReceipt insertReceipt = new WorkInvoiceReceipt();
+			insertReceipt.setInvoiceId(info.getInvoiceId());
+			String dateStr = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+					.withZone(ZoneId.systemDefault())
+					.format(Instant.ofEpochMilli(new Date().getTime()));
+			insertReceipt.setReceiptDate(dateStr);
+			insertReceipt.setCompanyName(info.getBuyerName());
+			insertReceipt.setMoney(Double.valueOf(info.getMoney()));
+			insertReceipt.preInsert();
+			insertReceiptList.add(insertReceipt);
 		}
+		//将收款信息添加到数据库中
+		workInvoiceReceiptDao.batchInsert(insertReceiptList);
+		//修改work_invoice表中 receipt_money_date 和 receipt_money 参数信息
+		dao.batchUpdateByForeach(uniqueList);
+
 
-		// 1. 提取duplicateList中所有数据的唯一标识(distinctStr)
-		Set<String> duplicateDistinctSet = new HashSet<>();
-		for (WorkInvoiceReceiptInfo item : duplicateList) {
-			String distinctStr = item.getDistinctStr();
-			if (distinctStr != null) {
-				duplicateDistinctSet.add(distinctStr);
-			}
-		}*/
 
 		// 最终返回处理后的列表
 		map.put("uniqueList", uniqueList);      // 仅包含本次新增且不与历史重复的数据
 		map.put("duplicateList", duplicateList); // 包含自身重复和与历史重复的数据
+		map.put("alreadyExistList", alreadyExistList); // 已经有收款信息的数据
+		map.put("inexistenceList", inexistenceList); // 数据表中不存在的的数据 invoiceDetail 表中不存在,也就是说 没有开票信息
+		map.put("moneyMismatchingList", moneyMismatchingList); // 数据表中金额大于开票价的的数据
 		return map;
 	}
 }

+ 3 - 1
src/main/java/com/jeeplus/modules/workinvoice/thread/ApprovalThread.java

@@ -57,10 +57,12 @@ public class ApprovalThread extends Thread {
             if(null == workInvoice){
                 disposeResult = "查询不到该开票信息";
                 omsDisposeService.handleInvoiceRetryAllFail("","",disposeResult, informType);
+                return;
             }
-            if(null!= workInvoice.getOmsOfdUrl()){
+            if(StringUtils.isNotBlank(workInvoice.getOmsOfdUrl())){
                 disposeResult = "该发票已经开过票";
                 omsDisposeService.handleInvoiceRetryAllFail("",workInvoice.getId(),disposeResult, informType);
+                return;
             }
             omsDisposeService.doInvoiceBusiness(map,workInvoice.getId(), informType);
 

+ 3 - 0
src/main/java/com/jeeplus/modules/workinvoice/thread/RedApprovalThread.java

@@ -36,14 +36,17 @@ public class RedApprovalThread extends Thread {
             if(null == workInvoice){
                 disposeResult = "查询不到该开票信息";
                 omsDisposeService.handleInvoiceRetryAllFail("","",disposeResult, informType);
+                return;
             }
             if(StringUtils.isNotBlank(workInvoice.getOmsOfdUrl())){
                 disposeResult = "该发票已经开过票";
                 omsDisposeService.handleInvoiceRetryAllFail("",workInvoice.getId(),disposeResult, informType);
+                return;
             }
             if(StringUtils.isBlank(workInvoice.getRedInvoiceRelevancyId())){
                 disposeResult = "未找到该发票关联需要开红字票的发票";
                 omsDisposeService.handleInvoiceRetryAllFail("",workInvoice.getId(),disposeResult, informType);
+                return;
             }
             omsDisposeService.doAllScenarioRedInvoiceBusiness(map,workInvoice.getId(), workInvoice.getRedInvoiceRelevancyId(), originalInvno, informType);
 

+ 118 - 28
src/main/java/com/jeeplus/modules/workinvoice/web/WorkInvoiceAllController.java

@@ -15,11 +15,13 @@ import com.jeeplus.common.utils.DateUtils;
 import com.jeeplus.common.utils.MyBeanUtils;
 import com.jeeplus.common.utils.StringUtils;
 import com.jeeplus.common.utils.excel.ExportExcel;
+import com.jeeplus.common.utils.excel.ExportMultipleTabsExcel;
 import com.jeeplus.common.utils.excel.ImportExcel;
 import com.jeeplus.common.web.BaseController;
 import com.jeeplus.modules.act.entity.Act;
 import com.jeeplus.modules.act.service.ActTaskService;
 import com.jeeplus.modules.act.utils.ActUtils;
+import com.jeeplus.modules.projectmaterialstorage.entity.ProjectMaterialStorage;
 import com.jeeplus.modules.projectmaterialstorage.entity.ProjectMaterialStorageImport;
 import com.jeeplus.modules.projectrecord.entity.ProjectRecords;
 import com.jeeplus.modules.projectrecord.enums.ProjectStatusEnum;
@@ -66,10 +68,12 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.ConstraintViolationException;
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.util.*;
 
 /**
@@ -1643,13 +1647,7 @@ public class WorkInvoiceAllController extends BaseController {
 	 */
 	//@RequiresPermissions("workinvoice:workInvoice:importReceiptDataFile")
 	@RequestMapping(value = "importReceiptDataFile", method=RequestMethod.POST)
-	public Map<String, Object> importReceiptDataFile(MultipartFile file, RedirectAttributes redirectAttributes, Map map) {// 初始化返回结果Map
-		Map<String, Object> result = new HashMap<>();
-
-
-		List<WorkInvoiceReceiptInfo> workInvoiceReceiptInfoList = Lists.newArrayList();
-
-
+	public String importReceiptDataFile(MultipartFile file, RedirectAttributes redirectAttributes, HttpServletResponse response) {// 初始化返回结果Map
 		//对相对有效的数据进行处理
 		//有效的数据
 		List<WorkInvoiceReceiptInfo> effectiveList = Lists.newArrayList();
@@ -1657,8 +1655,6 @@ public class WorkInvoiceAllController extends BaseController {
 		List<WorkInvoiceReceiptInfo> errorList = Lists.newArrayList();
 
 		try {
-			int successNum = 0;
-			int failureNum = 0;
 			StringBuilder failureMsg = new StringBuilder();
 			ImportExcel ei = new ImportExcel(file, 1, 0);
 			List<WorkInvoiceReceiptInfo> list = ei.getDataList(WorkInvoiceReceiptInfo.class);
@@ -1694,31 +1690,125 @@ public class WorkInvoiceAllController extends BaseController {
 			}
 
 
-			Map<String,List<WorkInvoiceReceiptInfo>> map1 = invoiceService.distinctProjectMaterialStorage(effectiveList);
-
+			Map<String,List<WorkInvoiceReceiptInfo>> map = invoiceService.distinctProjectMaterialStorage(effectiveList);
 
 
-			/*for (WorkInvoiceReceiptInfo workInvoice : list){
-				try{
-					workInvoiceService.save(workInvoice);
-					successNum++;
-				}catch(ConstraintViolationException ex){
-					failureNum++;
-					logger.error("Exception e:"+ex);
-				}catch (Exception ex) {
-					failureNum++;
-					logger.error("Exception e:"+ex);
-				}
-			}*/
-			if (failureNum>0){
-				failureMsg.insert(0, ",失败 "+failureNum+" 条发票管理记录。");
+			//有效数据
+			List<WorkInvoiceReceiptInfo> uniqueList = map.get("uniqueList");
+			//重复数据
+			List<WorkInvoiceReceiptInfo> duplicateList = map.get("duplicateList");
+			for (WorkInvoiceReceiptInfo info : duplicateList) {
+				info.setErrorMessage("重复数据,添加失败");
 			}
-			addMessage(redirectAttributes, "已成功导入 "+successNum+" 条发票管理记录"+failureMsg);
+			//已经有收款信息的数据
+			List<WorkInvoiceReceiptInfo> alreadyExistList = map.get("alreadyExistList");
+			for (WorkInvoiceReceiptInfo info : alreadyExistList) {
+				info.setErrorMessage("该发票已存在收款信息,添加失败");
+			}
+			//数据表中不存在的的数据 invoiceDetail 表中不存在,也就是说 没有开票信息
+			List<WorkInvoiceReceiptInfo> inexistenceList = map.get("inexistenceList");
+			for (WorkInvoiceReceiptInfo info : inexistenceList) {
+				info.setErrorMessage("系统中不存在该发票信息,添加失败");
+			}
+			//数据表中金额大于开票价的的数据
+			List<WorkInvoiceReceiptInfo> moneyMismatchingList = map.get("moneyMismatchingList");
+			for (WorkInvoiceReceiptInfo info : moneyMismatchingList) {
+				info.setErrorMessage("发票金额大于系统开票金额,添加失败");
+			}
+			//如果导入的数据量大于有效的数据量,说明导入的数据存在重复、错误等问题,直接抛出
+			if(list.size() >uniqueList.size()){
+				// 6. 核心:生成结果Excel文件(分2个Sheet),并通过浏览器下载
+				generateImportResultExcel(uniqueList, errorList, duplicateList, alreadyExistList, inexistenceList, moneyMismatchingList,list, response);
+				addMessage(redirectAttributes, "导入数据存在问题,详情请查看返回的文档信息。");
+			}else{
+				addMessage(redirectAttributes, "已成功导入 "+list.size()+" 条发票收款记录"+failureMsg);
+			}
+
 		} catch (Exception e) {
-			addMessage(redirectAttributes, "导入发票管理失败!失败信息:"+e.getMessage());
+			addMessage(redirectAttributes, "导入发票收款记录失败!失败信息:"+e.getMessage());
 			logger.error("Exception e:"+e);
 		}
-		return map;
+
+		return "redirect:"+Global.getAdminPath()+"/workinvoiceAll/workInvoiceAll/?repage";
+
 	}
 
+	/**
+	 * 生成导入结果Excel,分2个Sheet:有效导入数据、错误数据(含原因)
+	 * @param uniqueList 唯一有效数据
+	 * @param errorList 错误数据
+	 * @param duplicateList 重复数据
+	 * @param alreadyExistList 该发票已存在收款信息
+	 * @param inexistenceList 系统中不存在该发票信息
+	 * @param moneyMismatchingList 发票金额大于系统开票金额
+	 * @param metadataList 导入原数据
+	 * @param response 响应对象,用于输出下载流
+	 */
+	private void generateImportResultExcel(List<WorkInvoiceReceiptInfo> uniqueList,
+										   List<WorkInvoiceReceiptInfo> errorList,
+										   List<WorkInvoiceReceiptInfo> duplicateList,
+										   List<WorkInvoiceReceiptInfo> alreadyExistList,
+										   List<WorkInvoiceReceiptInfo> inexistenceList,
+										   List<WorkInvoiceReceiptInfo> moneyMismatchingList,
+										   List<WorkInvoiceReceiptInfo> metadataList,
+										   HttpServletResponse response) throws Exception {
+
+		// 1. 创建多页签导出工具实例
+		ExportMultipleTabsExcel exporter = new ExportMultipleTabsExcel();
+
+		// 处理可能的空列表,避免导出工具报错
+		uniqueList = uniqueList == null ? Collections.emptyList() : uniqueList;
+		errorList = errorList == null ? Collections.emptyList() : errorList;
+		duplicateList = duplicateList == null ? Collections.emptyList() : duplicateList;
+		alreadyExistList = alreadyExistList == null ? Collections.emptyList() : alreadyExistList;
+		inexistenceList = inexistenceList == null ? Collections.emptyList() : inexistenceList;
+		moneyMismatchingList = moneyMismatchingList == null ? Collections.emptyList() : moneyMismatchingList;
+
+		// 3. 添加Sheet页(页签名称、标题、实体类、数据列表)
+		if (!uniqueList.isEmpty()) {
+			exporter.addSheet("有效数据", "批量收款导入有效数据", WorkInvoiceReceiptInfo.class, uniqueList);
+		}
+		if (!errorList.isEmpty()) {
+			exporter.addSheet("错误数据", "批量收款导入错误数据", WorkInvoiceReceiptInfo.class, errorList);
+		}
+		if (!duplicateList.isEmpty()) {
+			exporter.addSheet("重复数据", "批量收款导入重复数据", WorkInvoiceReceiptInfo.class, duplicateList);
+		}
+		if (!alreadyExistList.isEmpty()) {
+			exporter.addSheet("已存在收款数据", "批量收款导入已存在收款数据", WorkInvoiceReceiptInfo.class, alreadyExistList);
+		}
+
+		if (!inexistenceList.isEmpty()) {
+			exporter.addSheet("不存在的发票信息", "批量收款导入不存在的发票信息", WorkInvoiceReceiptInfo.class, inexistenceList);
+		}
+
+		if (!moneyMismatchingList.isEmpty()) {
+			exporter.addSheet("发票金额大于系统开票金额", "批量收款导入发票金额大于系统开票金额数据", WorkInvoiceReceiptInfo.class, moneyMismatchingList);
+		}
+
+		if (!metadataList.isEmpty()) {
+			exporter.addSheet("原数据", "批量收款导入原数据", WorkInvoiceReceiptInfo.class, metadataList);
+		}
+
+
+		// 4. 导出文件(关键:设置响应头,确保浏览器正确下载)
+		String fileName = "发票收款导入数据汇总_" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
+		// 设置响应类型为Excel
+		response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+		// 设置文件名(解决中文乱码)
+		String encodedFileName = URLEncoder.encode(fileName, "UTF-8");
+		response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
+		// 禁用缓存
+		response.setHeader("Pragma", "no-cache");
+		response.setHeader("Cache-Control", "no-cache");
+		response.setDateHeader("Expires", 0);
+
+		// 修正:直接传递response对象给write方法(移除错误的类型转换)
+		exporter.write(response, fileName);
+		exporter.dispose(); // 清理资源
+
+	}
+
+
+
 }

+ 9 - 0
src/main/java/com/jeeplus/modules/workinvoicedetail/dao/WorkInvoiceDetailDao.java

@@ -56,4 +56,13 @@ public interface WorkInvoiceDetailDao extends CrudDao<WorkInvoiceDetail> {
      * @param invoice
      */
     void updateNumberById(WorkInvoiceDetail invoice);
+
+
+    /**
+     * 根据发票号查询开票具体信息
+     * @param numberList
+     * @return
+     */
+    List<WorkInvoiceDetail> getInvoiceDetailByNumber(@Param("list") List<String> numberList);
+
 }

+ 5 - 7
src/main/java/com/jeeplus/modules/workreimbursement/service/WorkReimbursementNewService.java

@@ -1285,13 +1285,11 @@ public class WorkReimbursementNewService extends CrudService<WorkReimbursementDa
             }
 
 
-            if(StringUtils.isNotBlank(workAccount.getReimbursementName())){
-                //添加人员部门id
-                User user = UserUtils.get(workAccount.getReimbursementName());
-                workAccount.setOfficeId(user.getOffice().getId());
-            }else{
-                workAccount.setOfficeId("");
-            }
+
+            //添加人员部门id
+            User user = UserUtils.getUser();
+            workAccount.setOfficeId(user.getOffice().getId());
+
             //根据是否为项目报销设置数据信息
             if(null != workAccount.getProjectRadio() && workAccount.getProjectRadio() == 0){
                 workAccount.setReimburseRemarks(workAccount.getProject().getProjectName());

+ 7 - 7
src/main/resources/jeeplus.properties

@@ -415,12 +415,12 @@ szPublicPassword: Xg@sys9hB2!xWm
 
 
 #OMS开票相关参数
-omsAppId: JSXGXM
-omsAppKey: JSXGXM
+omsAppId: hyc1
+omsAppKey: hyc1
 #组织编码
-omsDeptCode: JSXG01
+omsDeptCode: 500102204228315131
 #销售方纳税人识别号
-omsSellerTaxno: 91320000746823994F
+omsSellerTaxno: 500102204228315131
 #销售方名称
 omsSellerName: 江苏兴光项目管理有限公司
 #销售方银行名称
@@ -428,7 +428,7 @@ omsBankName: 中信银行南京龙江支行
 #销售方银行账号
 omsBankAccount: 7329010182600006811
 #访问接口地址前缀
-#omsUrl: https://oms-sandbox.einvoice.js.cn:7079
-omsUrl: https://www.oms.ejinshui-cloud.com:8899
+omsUrl: https://oms-sandbox.einvoice.js.cn:7079
+#omsUrl: https://www.oms.ejinshui-cloud.com:8899
 #用于判定是否开启oms开票流程事件
-omsEnabled: true
+omsEnabled: false

+ 30 - 1
src/main/resources/mappings/modules/workinvoice/WorkInvoiceDao.xml

@@ -3408,6 +3408,35 @@
 		WHERE id = #{id}
 	</update>
 
-
+	<!-- 方案一:foreach拼接多条UPDATE -->
+	<update id="batchUpdateByForeach">
+		UPDATE work_invoice
+		<!-- trim处理SET后多余的逗号,避免语法错误 -->
+		<trim prefix="SET" suffixOverrides=",">
+			<!-- 批量更新receipt_money:匹配id才更新,无值则保留原数据 -->
+			receipt_money = CASE id
+			<foreach collection="list" item="info" separator="">
+				<if test="info.receiptMoney != null">
+					WHEN #{info.invoiceId} THEN #{info.receiptMoney}
+				</if>
+			</foreach>
+			ELSE receipt_money
+			END,
+			<!-- 批量更新receipt_money_date:null/空字符串都不更新,保留原数据 -->
+			receipt_money_date = CASE id
+			<foreach collection="list" item="info" separator="">
+				<if test="info.receiptMoneyDate != null and info.receiptMoneyDate != ''">
+					WHEN #{info.invoiceId} THEN #{info.receiptMoneyDate}
+				</if>
+			</foreach>
+			ELSE receipt_money_date
+			END
+		</trim>
+		<!-- 关键:只更新传入的id集合,避免全表更新! -->
+		WHERE id IN
+		<foreach collection="list" item="info" open="(" separator="," close=")">
+			#{info.invoiceId}
+		</foreach>
+	</update>
 
 </mapper>

+ 35 - 2
src/main/resources/mappings/modules/workinvoice/WorkInvoiceReceiptDao.xml

@@ -103,14 +103,47 @@
 		FROM work_invoice_receipt a
 		left join work_invoice_detail wid on wid.invoice_id = a.invoice_id
 		<where>
-			a.del_flag = #{DEL_FLAG_NORMAL}
+			a.del_flag = 0
 
 			<if test="list!=null and list.size!=0">
-				and wid.invoice_number in
+				and wid.number in
 				<foreach collection="list" item="invoiceNumber" separator="," open="(" close=")">
 					#{invoiceNumber}
 				</foreach>
 			</if>
 		</where>
 	</select>
+
+	<!-- 批量插入:新建id=batchInsert,保留原单条insert不变 -->
+	<insert id="batchInsert">
+		INSERT INTO work_invoice_receipt(
+		id,
+		create_by,
+		create_date,
+		update_by,
+		update_date,
+		remarks,
+		del_flag,
+		invoice_id,
+		money,
+		company_name,
+		receipt_date
+		) VALUES
+		<!-- 核心:foreach遍历list,拼接多组VALUES,分隔符为逗号 -->
+		<foreach collection="list" item="info" separator=",">
+			(
+			#{info.id},
+			#{info.createBy.id},
+			#{info.createDate},
+			#{info.updateBy.id},
+			#{info.updateDate},
+			#{info.remarks},
+			#{info.delFlag},
+			#{info.invoiceId},
+			#{info.money},
+			#{info.companyName},
+			#{info.receiptDate}
+			)
+		</foreach>
+	</insert>
 </mapper>

+ 19 - 0
src/main/resources/mappings/modules/workinvoicedetail/WorkInvoiceDetailDao.xml

@@ -626,4 +626,23 @@
 			number = #{number}
 		WHERE invoice_id = #{invoiceId} and del_flag = 0
 	</update>
+
+
+
+	<select id="getInvoiceDetailByNumber" resultType="WorkInvoiceDetail" >
+		SELECT
+		<include refid="workInvoiceDetailColumn"/>
+		FROM work_invoice_detail a
+		<where>
+			a.del_flag = 0
+			<if test="list!=null and list.size!=0">
+				and a.number in
+				<foreach collection="list" item="number" separator="," open="(" close=")">
+					#{number}
+				</foreach>
+			</if>
+		</where>
+	</select>
+
+
 </mapper>

+ 8 - 8
src/main/webapp/WEB-INF/tags/table/importExcel.tag

@@ -10,15 +10,15 @@
 		<form id="importForm" action="${url}" method="post" enctype="multipart/form-data"
 			 style="padding-left:20px;text-align:center;" onsubmit="loading('正在导入,请稍等...');"><br/>
 			<input id="uploadFile" name="file" type="file" style="width:330px"/>导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!<br/>  
-			
-			
+
+
 		</form>
 </div>
 <script type="text/javascript">
 $(document).ready(function() {
 	$("#btnImport").click(function(){
 		top.layer.open({
-		    type: 1, 
+		    type: 1,
 		    area: [500, 300],
 		    title:"导入数据",
             skin: 'three-btns with-demo',
@@ -29,18 +29,18 @@ $(document).ready(function() {
 			  },
 		    btn2: function(index, layero){
 			        var inputForm =top.$("#importForm");
-			        var top_iframe = top.getActiveTab().attr("name");//获取当前active的tab的iframe 
+			        var top_iframe = top.getActiveTab().attr("name");//获取当前active的tab的iframe
 			        inputForm.attr("target",top_iframe);//表单提交成功后,从服务器返回的url在当前tab中展示
     	       		top.$("#importForm").submit();
 				    top.layer.close(index);
 			  },
-			 
-			  btn3: function(index){ 
+
+			  btn3: function(index){
 				  top.layer.close(index);
     	       }
-		}); 
+		});
 	});
-    
+
 });
 
 </script>

+ 2 - 0
src/main/webapp/webpage/modules/workinvoice/workInvoiceAllList.jsp

@@ -651,6 +651,8 @@
 							<table:exportExcel url="${ctx}/workinvoiceAll/workInvoiceAll/export"></table:exportExcel><!-- 导出按钮 -->
 						</shiro:hasPermission>
 
+							<table:importExcel url="${ctx}/workinvoiceAll/workInvoiceAll/importReceiptDataFile"></table:importExcel><!-- 导入按钮 -->
+
 						<shiro:hasPermission name="workInvoiceTwo:workInvoiceTwo:invoiceReport">
 							<a href="javascript:void(0)" style='background-color: #FFB800' onclick="downloadDialogre('开票汇总信息导出', '${ctx}/workinvoiceTwo/workinvoiceTwo/skipDownloadInvoiceForm?view=workInvoiceAll','40%', '400px','','下载,关闭')" class="layui-btn layui-btn-sm layui-bg-blue" > 开票汇总信息导出</a>
 						</shiro:hasPermission>