浏览代码

初始化

wanghaiwu 1 月之前
当前提交
b46cf3af70
共有 100 个文件被更改,包括 25293 次插入0 次删除
  1. 2005 0
      main/java/kd/bos/web/actions/AttachmentAction.java
  2. 44 0
      main/java/kd/cosmic/Application.java
  3. 34 0
      main/java/kd/cosmic/jkjt/bos/orgview/orgf7treelist/AdminOrgF7ViewExtListFormPlugin.java
  4. 26 0
      main/java/kd/cosmic/jkjt/common/basedata/api/CustomApiResultForMy.java
  5. 357 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SupplierSynApi2Plugin.java
  6. 73 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SupplierSynReturnModel.java
  7. 342 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerApi2Plugin.java
  8. 197 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerForBankApi2Plugin.java
  9. 172 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerForBankNewApi2Plugin.java
  10. 420 0
      main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerNewApi2Plugin.java
  11. 240 0
      main/java/kd/cosmic/jkjt/common/basedata/listplugin/CustomerListPlugin.java
  12. 148 0
      main/java/kd/cosmic/jkjt/common/oauth/OauthSSOAuthHandler.java
  13. 118 0
      main/java/kd/cosmic/jkjt/common/oauth/RSAUtils.java
  14. 49 0
      main/java/kd/cosmic/jkjt/common/oauth/SHAUtils.java
  15. 128 0
      main/java/kd/cosmic/jkjt/common/oauth/SSOAuthtication.java
  16. 104 0
      main/java/kd/cosmic/jkjt/common/util/JKsmsServiceHandler.java
  17. 24 0
      main/java/kd/cosmic/jkjt/common/util/Result.java
  18. 125 0
      main/java/kd/cosmic/jkjt/common/util/SmsService.java
  19. 210 0
      main/java/kd/cosmic/jkjt/epm/eb/formplugin/bgadjust/OverallBudgetAdjustCusEditPlugin.java
  20. 32 0
      main/java/kd/cosmic/jkjt/fi/ai/formplugin/PreViewCusFormPlugin.java
  21. 790 0
      main/java/kd/cosmic/jkjt/fi/cas/common/PayBillToolUtil.java
  22. 245 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/PayBillEditPlugin.java
  23. 50 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/PayBillListPlugin.java
  24. 308 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillImageEditPlugin.java
  25. 66 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillViewVouInfoEditPlugin.java
  26. 66 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillViewVouInfoMobEditPlugin.java
  27. 52 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillVoucherViewEditPlugin.java
  28. 133 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillVoucherViewMobEditPlugin.java
  29. 37 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PaysynoApplyEditPlugin.java
  30. 39 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PaysynoApplyF7ListPlugin.java
  31. 103 0
      main/java/kd/cosmic/jkjt/fi/cas/formplugin/transferapply/TransferApplyEditPlugin.java
  32. 102 0
      main/java/kd/cosmic/jkjt/fi/cas/opplugin/PayBillForCBSOpPlugin.java
  33. 42 0
      main/java/kd/cosmic/jkjt/fi/cas/opplugin/PaymentBeforeCommitCusOpPlugin.java
  34. 83 0
      main/java/kd/cosmic/jkjt/fi/cas/opplugin/PaymentWriteFeeApplyOpPlugin.java
  35. 58 0
      main/java/kd/cosmic/jkjt/fi/cas/task/AutoAccountbankFreezeTask.java
  36. 61 0
      main/java/kd/cosmic/jkjt/fi/cas/task/AutoDownloadBankBillTask.java
  37. 416 0
      main/java/kd/cosmic/jkjt/fi/cas/task/DateUtils.java
  38. 80 0
      main/java/kd/cosmic/jkjt/fi/cas/task/PayBillReturnStatusForCBSTask.java
  39. 253 0
      main/java/kd/cosmic/jkjt/fi/cas/task/PayReturnStatusForERSystemTask.java
  40. 69 0
      main/java/kd/cosmic/jkjt/fi/gl/formplugin/VoucherCusEditPlugin.java
  41. 355 0
      main/java/kd/cosmic/jkjt/fi/gl/formplugin/voucherForImageEditPlugin.java
  42. 304 0
      main/java/kd/cosmic/jkjt/fi/gl/opplugin/VoucherForImageOpPlugin.java
  43. 163 0
      main/java/kd/cosmic/jkjt/fi/gl/opplugin/VoucherSubmitCheckNumberOpPlugin.java
  44. 262 0
      main/java/kd/cosmic/jkjt/fi/gl/report/AccBalanceFormRptExtPlugin.java
  45. 189 0
      main/java/kd/cosmic/jkjt/fi/gl/report/NavToAssRptExt.java
  46. 295 0
      main/java/kd/cosmic/jkjt/fi/gl/task/VoucherForImageArchiveTask.java
  47. 905 0
      main/java/kd/cosmic/jkjt/imc/rim/formplugin/message/service/ExcelInvoiceSaveServiceEx.java
  48. 722 0
      main/java/kd/cosmic/jkjt/imc/rim/formplugin/message/service/InvoiceImportOpenServiceEx.java
  49. 364 0
      main/java/kd/cosmic/jkjt/msg/ecology/ChangeMessageStatePlugin.java
  50. 34 0
      main/java/kd/cosmic/jkjt/msg/ecology/EarlyWarnMessageHandler.java
  51. 38 0
      main/java/kd/cosmic/jkjt/msg/ecology/EcologyListPlugin.java
  52. 121 0
      main/java/kd/cosmic/jkjt/msg/ecology/EcologyOpPlugin.java
  53. 1002 0
      main/java/kd/cosmic/jkjt/msg/ecology/FanweiCommonUtil.java
  54. 189 0
      main/java/kd/cosmic/jkjt/msg/ecology/FanweiServiceHelper.java
  55. 188 0
      main/java/kd/cosmic/jkjt/msg/ecology/HttpUtils.java
  56. 95 0
      main/java/kd/cosmic/jkjt/msg/ecology/MessageCompiler.java
  57. 20 0
      main/java/kd/cosmic/jkjt/msg/ecology/MyX509TrustManager.java
  58. 163 0
      main/java/kd/cosmic/jkjt/msg/ecology/RSAUtils.java
  59. 106 0
      main/java/kd/cosmic/jkjt/msg/ecology/RefreshFanWeiMsgStatusTask.java
  60. 64 0
      main/java/kd/cosmic/jkjt/msg/ecology/SSLClient.java
  61. 13 0
      main/java/kd/cosmic/jkjt/msg/ecology/TrustAnyHostnameVerifier.java
  62. 420 0
      main/java/kd/cosmic/jkjt/msg/feishu/ChangeMessageStatePlugin.java
  63. 245 0
      main/java/kd/cosmic/jkjt/msg/feishu/DateUtil.java
  64. 450 0
      main/java/kd/cosmic/jkjt/msg/feishu/DeleteFeiShuPlugin.java
  65. 100 0
      main/java/kd/cosmic/jkjt/msg/feishu/FeiShuAuthtication.java
  66. 1920 0
      main/java/kd/cosmic/jkjt/msg/feishu/FeiShuDaiBanServiceHandler.java
  67. 187 0
      main/java/kd/cosmic/jkjt/msg/feishu/FeiShuRetryUtil.java
  68. 545 0
      main/java/kd/cosmic/jkjt/msg/feishu/FeishuUtil.java
  69. 64 0
      main/java/kd/cosmic/jkjt/msg/feishu/SSLClient.java
  70. 420 0
      main/java/kd/cosmic/jkjt/msg/feishu/TestChangeMessageStatePlugin.java
  71. 2172 0
      main/java/kd/cosmic/jkjt/msg/feishu/TestFeiShuDaiBanServiceHandler.java
  72. 11 0
      main/java/kd/cosmic/jkjt/msg/feishu/UUIDUtil.java
  73. 56 0
      main/java/kd/cosmic/jkjt/msg/feishu/formplugin/FeiShuMessageEditPlugin.java
  74. 213 0
      main/java/kd/cosmic/jkjt/siit/HttpUtilAction.java
  75. 297 0
      main/java/kd/cosmic/jkjt/siit/SiitServerHandler.java
  76. 79 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/AccountSumCardPlugin.java
  77. 230 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/AccountbankDistributeExPlugin.java
  78. 145 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/BankAccountEdit.java
  79. 111 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/BankAcctOpenCusEditPlugin.java
  80. 107 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportAccountListPlugin.java
  81. 101 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportEditPlugin.java
  82. 54 0
      main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportListPlugin.java
  83. 199 0
      main/java/kd/cosmic/jkjt/tmc/am/mservice/UpdateAccountCBSServiceImpl.java
  84. 396 0
      main/java/kd/cosmic/jkjt/tmc/am/opplugin/bankacct/BankAccountOpPlugin.java
  85. 103 0
      main/java/kd/cosmic/jkjt/tmc/am/opplugin/bankacct/ReportOpPlugin.java
  86. 164 0
      main/java/kd/cosmic/jkjt/tmc/am/report/AcctDetailDataListDataPluginExt.java
  87. 90 0
      main/java/kd/cosmic/jkjt/tmc/am/report/AcctDetailFormListPluginExt.java
  88. 344 0
      main/java/kd/cosmic/jkjt/tmc/am/report/AcctSumDataRptPlugin.java
  89. 231 0
      main/java/kd/cosmic/jkjt/tmc/am/report/AcctSumFormListRptPlugin.java
  90. 25 0
      main/java/kd/cosmic/jkjt/tmc/am/report/AcctUsageDataListExtPlugin.java
  91. 186 0
      main/java/kd/cosmic/jkjt/tmc/am/report/ConcreteStrategyDetailEx.java
  92. 186 0
      main/java/kd/cosmic/jkjt/tmc/am/report/ConcreteStrategyJournalEx.java
  93. 39 0
      main/java/kd/cosmic/jkjt/tmc/am/servicehelper/ServiceFactory.java
  94. 349 0
      main/java/kd/cosmic/jkjt/tmc/am/task/AccountOpenCBSWarningTask.java
  95. 106 0
      main/java/kd/cosmic/jkjt/tmc/am/task/DepositAgreeForStatusTask.java
  96. 100 0
      main/java/kd/cosmic/jkjt/tmc/am/task/SynchAccount2CBSTask.java
  97. 385 0
      main/java/kd/cosmic/jkjt/tmc/api/SynBentransByBalance2Plugin.java
  98. 361 0
      main/java/kd/cosmic/jkjt/tmc/api/SynBetransDetail2Plugin.java
  99. 355 0
      main/java/kd/cosmic/jkjt/tmc/api/SynBetransDetailCur2Plugin.java
  100. 254 0
      main/java/kd/cosmic/jkjt/tmc/api/SynElectronicReceipt2Plugin.java

+ 2005 - 0
main/java/kd/bos/web/actions/AttachmentAction.java

@@ -0,0 +1,2005 @@
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by FernFlower decompiler)
+//
+
+package kd.bos.web.actions;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Maps;
+import com.grapecity.documents.excel.Workbook;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.UUID;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import kd.bos.actiondispatcher.ActionUtil;
+import kd.bos.attachment.util.AttachExceptionHandler;
+import kd.bos.attachment.util.BillFileMappingWriter;
+import kd.bos.attachment.util.FileSecurityUtil;
+import kd.bos.cache.CacheFactory;
+import kd.bos.cache.DistributeCacheHAPolicy;
+import kd.bos.cache.DistributeSessionlessCache;
+import kd.bos.cache.TempFileCache;
+import kd.bos.cache.tempfile.TempFileCacheDownloadable;
+import kd.bos.context.OperationContext;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.decode.DecodeHandlerFactory;
+import kd.bos.entity.param.AppParam;
+import kd.bos.entity.param.CustomParam;
+import kd.bos.exception.ErrorCode;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+import kd.bos.file.security.FileCheckResult;
+import kd.bos.file.security.FileChecker;
+import kd.bos.fileservice.BatchDownloadRequest;
+import kd.bos.fileservice.FileItem;
+import kd.bos.fileservice.FileItemParser;
+import kd.bos.fileservice.FileService;
+import kd.bos.fileservice.FileServiceFactory;
+import kd.bos.fileservice.enums.PreviewParams;
+import kd.bos.fileservice.utils.ExceptionUtil;
+import kd.bos.fileservice.utils.FileTimeoutUtil;
+import kd.bos.fileservice.utils.FileUtil;
+import kd.bos.fileservice.utils.JsonUtil;
+import kd.bos.fileservice.watermark.IWatermarkHandler;
+import kd.bos.form.FormConfig;
+import kd.bos.form.FormMetadataCache;
+import kd.bos.form.attachment.common.AttachmentKit;
+import kd.bos.form.attachment.util.AttachmentPreviewUtil;
+import kd.bos.form.attachment.util.ParamUtil;
+import kd.bos.form.watermark.common.WaterMarkKit;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.mservice.attachment.AttachmentType;
+import kd.bos.mservice.attachment.IAttachmentService;
+import kd.bos.msgjet.MsgSendFactory;
+import kd.bos.param.service.IParameterReaderService;
+import kd.bos.service.ServiceSvcFactory;
+import kd.bos.service.attachment.FileItemExt;
+import kd.bos.service.attachment.FileSource;
+import kd.bos.service.attachment.extend.action.FileActionExtensionFactory;
+import kd.bos.service.attachment.extend.permission.FilePermissionExtensionFactory;
+import kd.bos.service.attachment.extend.permission.FilePermissionParam;
+import kd.bos.service.attachment.extend.permission.FileTypeEnum;
+import kd.bos.service.attachment.extend.permission.IFilePermissionExtension;
+import kd.bos.service.attachment.extend.permission.PermissionResult;
+import kd.bos.servicehelper.AttachmentServiceHelper;
+import kd.bos.servicehelper.FileRecord;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
+import kd.bos.servicehelper.util.FileRecordUtil;
+import kd.bos.session.EncreptSessionUtils;
+import kd.bos.session.SystemPropertyUtils;
+import kd.bos.svc.attachment.preview.WpsPreviewHandle;
+import kd.bos.svc.attachment.wps.pri.WpsPrivateHandle;
+import kd.bos.svc.attachment.wps.pri.dto.Watermark;
+import kd.bos.svc.attachment.wps.v3.WpsV3Handle;
+import kd.bos.svc.attachment.yozo.YozoDocumentHandle;
+import kd.bos.svc.util.FileHandlerUtil;
+import kd.bos.util.ExceptionUtils;
+import kd.bos.util.FileNameUtils;
+import kd.bos.util.FileUtils;
+import kd.bos.web.DispatchServiceHelper;
+import kd.bos.web.actions.handler.HandleFactory;
+import kd.bos.web.actions.utils.AttachmentUtil;
+import kd.bos.web.actions.utils.FileOperationLog;
+import kd.bos.web.actions.utils.FilePathUtil;
+import kd.bos.web.actions.utils.FilePremissionUtil;
+import kd.bos.web.actions.utils.FileReqLimitUtil;
+import kd.bos.web.actions.utils.FileUploadUrlUtil;
+import kd.bos.web.actions.utils.FileOperationLog.FileOperationEnum;
+import kd.cosmic.jkjt.wps6.WPS4Util;
+import kd.cosmic.jkjt.wps6.wps6PerviewHandler;
+import kd.sdk.annotation.SdkPublic;
+import org.apache.commons.io.IOUtils;
+
+@SdkPublic
+public class AttachmentAction {
+    private static final String TEMPFILE_DOWNLOAD_DO_CONFIG_KEY = "tempfile/download.do?configKey";
+    private static Log log = LogFactory.getLog(AttachmentAction.class);
+    private static final String ERROR = "error";
+    private static final String FILE_NAME = "fileName";
+    private static final String BOS_WEBACTIONS = "bos-webactions";
+    private static final String BOS_ATTACHMENT = "bos-attachment";
+    private static final String USER_AGENT = "USER-AGENT";
+    private static final String CONTENT_DISPOSITION = "Content-Disposition";
+    private static final String CONTENT_LENGTH = "Content-Length";
+    private static final String UTF_8 = "UTF-8";
+    private static final String STATUS = "status";
+    private static final String DESCRIPTION = "description";
+    private static final String SUCCESS = "success";
+    private static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
+    private static final String ANDORID = "Andorid";
+    private static final String ANDROID = "Android";
+    private static final String ATTACHMENT_FILENAME = "attachment;filename=\"";
+    private static final String STRING = "attachment;filename*=UTF-8''";
+    private static final String LOCAL_ID_KEY = "AttachmentAction:uploadFile:localIds:";
+    private static final String[] IEBrowserSignals = new String[]{"MSIE", "Trident", "Edge"};
+    private static final String KINGDEEYUNPAN = "https://www.yunzhijia.com";
+    private static final String IMG_EXT = "bmp,gif,jpg,png,jpeg,dib,emf,jfif,jpe,rle,wmf,tif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,webp,avif";
+    private static final String PDF_EXT = "pdf";
+    private static DistributeSessionlessCache redisCache = CacheFactory.getCommonCacheFactory().getDistributeSessionlessCache("", new DistributeCacheHAPolicy(true, true));
+    private ErrorCode returnErrorCode = new ErrorCode("return", "do return");
+    HostnameVerifier hv = new HostnameVerifier() {
+        public boolean verify(String urlHostName, SSLSession session) {
+            AttachmentAction.log.warn("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
+            return true;
+        }
+    };
+
+    public AttachmentAction() {
+    }
+
+    protected static FileService getFileService() {
+        return FileServiceFactory.getAttachmentFileService();
+    }
+
+    protected static IFilePermissionExtension getFilePermissionExtension() {
+        return FilePermissionExtensionFactory.getFilePermissionExtension();
+    }
+
+    private String getContentType(String fileName) {
+        Optional<String> fileSuffix = this.getFileSuffix(fileName);
+        if (!fileSuffix.isPresent()) {
+            return PreviewParams.BAD_EXT.getEnumName();
+        } else {
+            boolean flag = FileUtil.isExtExist((String)fileSuffix.get());
+            flag = AttachmentKit.isCustomExtExist(flag, (String)fileSuffix.get());
+            if (!flag) {
+                return PreviewParams.BAD_EXT.getEnumName();
+            } else {
+                Optional<String> fileContentType = FileContentTypeFactory.getFileContentTypeBySuffix((String)fileSuffix.get());
+                return (String)fileContentType.orElse(null);
+            }
+        }
+    }
+
+    private Optional<String> getFileSuffix(String fileName) {
+        int index = fileName.lastIndexOf(46);
+        return index >= 0 && index != fileName.length() - 1 ? Optional.of(fileName.substring(fileName.lastIndexOf(46) + 1)) : Optional.empty();
+    }
+
+    public void preview(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        try {
+            this.doPreview(request, response);
+        } catch (KDBizException var4) {
+            if (var4.getErrorCode() != this.returnErrorCode) {
+                throw new KDBizException(var4, var4.getErrorCode(), new Object[0]);
+            }
+        }
+    }
+
+    private Map<String, Object> getSettingFromPublicParameter() {
+        Map<String, Object> setting = new HashMap();
+        Object setStr = DispatchServiceHelper.invokeBOSService("SystemParamService", "loadPublicParameterFromCache", new Object[]{"previewconfig"});
+        if (StringUtils.isNotBlank(setStr)) {
+            setting = (Map)SerializationUtils.fromJsonString(setStr.toString(), Map.class);
+        }
+
+        return (Map)setting;
+    }
+
+    private void doPreview(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        this.setOperationContext("bos", "", "", "preview", "preview");
+        request.setAttribute("permItem", "2NJ5VA7D2ONF");
+        String path = this.getPathFromRequest(request, response);
+        String fileName = this.getFileName(request, response, path);
+        String previewType = ParamUtil.getPreviewType();
+        Map<String, String> customParameterFromCache = SystemParamServiceHelper.loadCustomParameterFromCache(new CustomParam());
+        boolean spreadJsPreview = Boolean.parseBoolean((String)customParameterFromCache.get("SPREAD_JS_PREVIEW")) && this.getFileSuffix(fileName).isPresent() && (((String)this.getFileSuffix(fileName).get()).equalsIgnoreCase("xls") || ((String)this.getFileSuffix(fileName).get()).equalsIgnoreCase("xlsx"));
+        boolean yzjPreView = "1".equals(previewType);
+        boolean customPreView = "5".equals(previewType);
+        boolean wpsPreView = "21".equals(previewType);
+        boolean yozoPreView = "4".equals(previewType);
+        boolean wpsPrivatePreview = "22".equals(previewType);
+        String formId = request.getParameter("fId");
+        Watermark watermarkInfo = AttachmentUtil.getWatermarkForWpsPreview(formId);
+        Map wpsPreviewResult;
+
+        /********************私有云wps处理 turborao********************/
+        //turborao 此处判断文件类型,word、pdf、excel、ppt, 如果不是以上文件爽型,走系统本身预览
+        String type = WPS4Util.getFileType(fileName);
+        if ((wpsPreView || wpsPrivatePreview) && !"x".equals(type)) {
+            Map<String, Object> setting = this.getSettingFromPublicParameter();
+            String requestURL = request.getRequestURL().toString();
+            //turborao 增加wps私有云处理
+            wpsPreviewResult  = wps6PerviewHandler.createWpsPreViewResult(path, requestURL, setting);
+
+            if ((Boolean)wpsPreviewResult.get("support")) {
+                this.dealPreviewResult(response, wpsPreviewResult, previewType);
+                return;
+            }
+
+            previewType = "0";
+        }
+        /********************私有云wps处理 turborao********************/
+
+        if (wpsPrivatePreview) {
+            wpsPreviewResult = WpsPrivateHandle.createWpsPreViewResult(path, watermarkInfo);
+            if ((Boolean)wpsPreviewResult.get("support")) {
+                this.dealPreviewResult(response, wpsPreviewResult, previewType);
+                return;
+            }
+
+            previewType = "0";
+        }
+
+        if (wpsPreView) {
+            if (ParamUtil.isWpsV3()) {
+                wpsPreviewResult = WpsV3Handle.getPreviewResult(path, watermarkInfo);
+            } else {
+                wpsPreviewResult = WpsPreviewHandle.createWpsPreViewResult(path, ParamUtil.getWpsPublicParam(true), watermarkInfo);
+            }
+
+            if ((Boolean)wpsPreviewResult.get("support")) {
+                this.dealPreviewResult(response, wpsPreviewResult, previewType);
+                return;
+            }
+
+            previewType = "0";
+        }
+
+        Map result;
+        if (yozoPreView) {
+            wpsPreviewResult = AttachmentUtil.getWatermarkForYozoPreview(formId);
+            result = YozoDocumentHandle.createYozoPreViewResult(fileName, path, wpsPreviewResult);
+            if ((Boolean)result.get("support")) {
+                this.dealPreviewResult(response, result, previewType);
+                return;
+            }
+
+            previewType = "0";
+        }
+
+        path = this.tryEncryPath(request, path);
+        Optional<InputStream> cacheInputStream = this.createCacheInputStream(request, path);
+        String isFromCache = request.getParameter("isFromCache");
+        String userAgent = request.getHeader("USER-AGENT");
+        result = this.getPreViewResult(path, cacheInputStream, fileName, ParamUtil.getYzjParam(), isFromCache, yzjPreView, customPreView, spreadJsPreview, userAgent);
+        boolean isAddWaterMark = WaterMarkKit.dealWaterMark(request, fileName, result, true);
+        String contentType = this.getContentType(fileName);
+        if (contentType != null) {
+            this.validateContentType(response, fileName, contentType);
+            this.injectReponseCharset(request, response, result, isAddWaterMark, contentType);
+            fileName = FileSecurityUtil.getSecurityHeader(fileName);
+            response.addHeader("Content-Disposition", "inline;filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
+        }
+
+        this.dealPreviewResult(response, result, previewType);
+    }
+
+    private Map<String, Object> getPreViewResult(String path, Optional<InputStream> cacheInputStream, String fileName, Map<String, String> setting, String isFromCache, boolean yzjPreView, boolean customPreview, boolean spreadJsPreview, String userAgent) {
+        if (!yzjPreView && !customPreview) {
+            if (spreadJsPreview) {
+                Map<String, Object> result = this.spreadJsPreview(path, (InputStream)cacheInputStream.orElse(null), isFromCache);
+                if (result.get(PreviewParams.STATUS.getEnumName()).equals(PreviewParams.SPREADJS_SUCCESS.getEnumName())) {
+                    return result;
+                }
+            }
+
+            return this.YunPanCovertPreview(path, (InputStream)cacheInputStream.orElse(null), fileName, isFromCache, userAgent);
+        } else {
+            return this.preViewAboutYJZ(path, (InputStream)cacheInputStream.orElse(null), fileName, setting, isFromCache, userAgent);
+        }
+    }
+
+    private Map<String, Object> spreadJsPreview(String path, InputStream cacheInputStream, String isFromCache) {
+        Map<String, Object> result = new HashMap();
+        InputStream inputStream = Boolean.parseBoolean(isFromCache) ? cacheInputStream : FileServiceFactory.getAttachmentFileService().getInputStream(path);
+
+        try {
+            Workbook workbook = new Workbook();
+            workbook.open(inputStream);
+            String json = workbook.toJson();
+            result.put(PreviewParams.STATUS.getEnumName(), PreviewParams.SPREADJS_SUCCESS.getEnumName());
+            result.put(PreviewParams.RESULT.getEnumName(), json);
+        } catch (Exception var8) {
+            Map<String, Object> resultMsg = ExceptionUtil.setErrorInfo(var8.getMessage());
+            result.put(PreviewParams.STATUS.getEnumName(), PreviewParams.ERROR.getEnumName());
+            result.put(PreviewParams.RESULT.getEnumName(), resultMsg);
+            log.error("spreadJsPreview fail --- goto yunPanConvert", var8);
+        }
+
+        return result;
+    }
+
+    private String getPathFromRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        String path = "";
+
+        try {
+            path = HandleFactory.getHandle(request).handler(request);
+        } catch (KDException var5) {
+            if (AttachExceptionHandler.dealException(var5, response)) {
+                throw new KDBizException(var5, this.returnErrorCode, new Object[0]);
+            }
+        }
+
+        this.validatePath(request, response, path);
+        return path;
+    }
+
+    private void injectReponseCharset(HttpServletRequest request, HttpServletResponse response, Map<String, Object> result, boolean hasWaterMark, String contentType) {
+        if ("text/plain".equals(contentType)) {
+            this.injectResponseCharsetIfContentTypeIsText(request, response, result, hasWaterMark);
+        } else {
+            response.setContentType(contentType);
+        }
+
+    }
+
+    private void injectResponseCharsetIfContentTypeIsText(HttpServletRequest request, HttpServletResponse response, Map<String, Object> result, boolean hasWaterMark) {
+        String charset = (String)result.get(PreviewParams.CHARSET.getEnumName());
+        if (StringUtils.isNotEmpty(charset)) {
+            switch (charset) {
+                case "GBK":
+                    if (!this.isIEBrowser(request)) {
+                        charset = PreviewParams.ANSI.getEnumName();
+                    }
+                    break;
+                case "UTF-16LE":
+                    charset = PreviewParams.UNICODE.getEnumName();
+                    break;
+                case "UTF-16BE":
+                    charset = PreviewParams.UNICODE_BIG_ENDIAN.getEnumName();
+                    break;
+                default:
+                    charset = PreviewParams.UTF_8.getEnumName();
+            }
+
+            if (hasWaterMark) {
+                response.setContentType("application/pdf;charset=" + charset);
+            } else {
+                response.setContentType("text/plain;charset=" + charset);
+            }
+        }
+
+    }
+
+    private void validateContentType(HttpServletResponse response, String fileName, String contentType) throws IOException {
+        Map result;
+        if ("false".equals(contentType)) {
+            result = ExceptionUtil.setErrorInfo(ResManager.loadKDString("文件后缀名不存在", "AttachmentAction_2", "bos-webactions", new Object[0]));
+            this.dealResult(response, result);
+            throw new KDBizException(this.returnErrorCode, new Object[0]);
+        } else if ("badExt".equals(contentType)) {
+            result = ExceptionUtil.setErrorInfo(String.format(ResManager.loadKDString("文件类型不支持预览:%s", "AttachmentAction_3", "bos-attachment", new Object[0]), fileName.substring(fileName.lastIndexOf(46))));
+            this.dealResult(response, result);
+            throw new KDBizException(this.returnErrorCode, new Object[0]);
+        }
+    }
+
+    private Map<String, Object> preViewAboutYJZ(String path, InputStream cacheInputStream, String fileName, Map<String, String> setting, String isFromCache, String userAgent) {
+        log.info("kd.bos.web.actions.AttachmentAction.preViewAboutYJZ --- YzjConfig --- " + setting);
+        Map result;
+        if (Boolean.parseBoolean(isFromCache)) {
+            result = getFileService().previewFromCacheWPS(fileName, path, userAgent, cacheInputStream, setting);
+        } else {
+            result = getFileService().previewWPS(fileName, path, userAgent, setting);
+        }
+
+        return result;
+    }
+
+    private Map<String, Object> YunPanCovertPreview(String path, InputStream cacheInputStream, String fileName, String isFromCache, String userAgent) {
+        Map result;
+        if (Boolean.parseBoolean(isFromCache)) {
+            result = getFileService().previewFromCache(fileName, path, userAgent, cacheInputStream);
+        } else {
+            result = getFileService().preview(fileName, path, userAgent);
+        }
+
+        return result;
+    }
+
+    private String getFileName(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
+        String isFromCache = request.getParameter("isFromCache");
+        String fileName;
+        if (Boolean.parseBoolean(isFromCache)) {
+            fileName = this.getFileNameFromCache(response, path);
+        } else {
+            fileName = this.getFileNameFromPath(path);
+        }
+
+        this.validateFileName(response, fileName);
+        return fileName;
+    }
+
+    private String getFileNameFromCache(HttpServletResponse response, String path) throws IOException {
+        Map<String, Object> fileInfoFromCache = this.getFileInfoFromCache(path);
+        String errorInfo = (String)fileInfoFromCache.get("error");
+        if (StringUtils.isNotBlank(errorInfo)) {
+            Map<String, Object> result = ExceptionUtil.setErrorInfo(errorInfo);
+            this.dealResult(response, result);
+            throw new KDBizException(this.returnErrorCode, new Object[0]);
+        } else {
+            return (String)fileInfoFromCache.get("fileName");
+        }
+    }
+
+    private String getFileNameFromPath(String path) {
+        return path.substring(path.lastIndexOf(47) + 1);
+    }
+
+    private void validateFileName(HttpServletResponse response, String fileName) throws IOException {
+        if (StringUtils.isEmpty(fileName)) {
+            Map<String, Object> result = ExceptionUtil.setErrorInfo(ResManager.loadKDString("请求的path中未包含文件信息,文件名不存在", "AttachmentAction_1", "bos-webactions", new Object[0]));
+            this.dealResult(response, result);
+            throw new KDBizException(this.returnErrorCode, new Object[0]);
+        }
+    }
+
+    private boolean checkDownloadUrl(HttpServletResponse response, String downloadUrl) throws IOException {
+        if (!StringUtils.isBlank(downloadUrl) && this.getCheckuploadurl()) {
+            String yzjUrl = this.getYZJUrl();
+            if (StringUtils.isBlank(yzjUrl)) {
+                return true;
+            } else if (downloadUrl.startsWith(yzjUrl)) {
+                return true;
+            } else if (downloadUrl.startsWith("https://www.yunzhijia.com")) {
+                return true;
+            } else {
+                String errorMsg = ResManager.loadKDString("附件上传域名校验不通过,系统不允许上传。", "AttachmentAction_6", "bos-attachment", new Object[0]);
+                writeErrorMessage(response, errorMsg);
+                return false;
+            }
+        } else {
+            return true;
+        }
+    }
+
+    private String tryEncryPath(HttpServletRequest request, String path) {
+        String isFromCache = request.getParameter("isFromCache");
+        if (Boolean.parseBoolean(isFromCache)) {
+            path = EncreptSessionUtils.encryptSession(path);
+        }
+
+        return path;
+    }
+
+    private Optional<InputStream> createCacheInputStream(HttpServletRequest request, String path) throws IOException {
+        String isFromCache = request.getParameter("isFromCache");
+        if (Boolean.parseBoolean(isFromCache)) {
+            Map<String, Object> fileInfoFromCache = this.getFileInfoFromCache(path);
+            String errorInfo = (String)fileInfoFromCache.get("error");
+            if (StringUtils.isBlank(errorInfo)) {
+                return Optional.of((InputStream)fileInfoFromCache.get("in"));
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private void validatePath(HttpServletRequest request, HttpServletResponse response, String path) throws IOException {
+        if (kd.bos.util.StringUtils.isBlank(path)) {
+            Map<String, Object> result = ExceptionUtil.setErrorInfo(ResManager.loadKDString("path为空,不存在", "AttachmentAction_0", "bos-webactions", new Object[0]));
+            this.dealResult(response, result);
+            throw new KDBizException(this.returnErrorCode, new Object[0]);
+        } else {
+            log.info("[validatapath logger :]" + request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort());
+        }
+    }
+
+    public void removeExcelPreview(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        Map<String, Object> result = new HashMap();
+        String id = request.getParameter("id");
+        if (StringUtils.isEmpty(id)) {
+            result.put("status", "error");
+            result.put("description", "the param(id) can not be null!");
+        } else {
+            try {
+                getFileService().removePreview(id);
+                result.put("status", "success");
+            } catch (Exception var6) {
+                result.put("status", "error");
+                result.put("description", ExceptionUtils.getExceptionStackTraceMessage(var6));
+                log.error(var6);
+            }
+        }
+
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    private String getPathfromDownloadUrl(String url) throws IOException {
+        String path = StringUtils.substringAfter(url, "path=");
+        path = URLDecoder.decode(path, "UTF-8");
+        return FilePathUtil.dealPath(path, "attach");
+    }
+
+    private String getPathfromTempDownloadUrl(String url) throws IOException {
+        String path = "tempfile/download.do?configKey" + StringUtils.substringAfter(url, "tempfile/download.do?configKey");
+        return FilePathUtil.dealPath(path, "attach");
+    }
+
+    public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        Boolean sucess = Boolean.FALSE;
+        String fileName = "";
+
+        try {
+            String id;
+            try {
+                log.info("download-----------------------start-----------------");
+                fileName = request.getParameter("fileName");
+                request.setAttribute("permItem", "2NJ5XVVCMBCL");
+                String path = HandleFactory.getHandle(request).handler(request);
+                if (StringUtils.isBlank(fileName) && path != null) {
+                    fileName = path.substring(path.lastIndexOf(47) + 1);
+                }
+
+                if (FileReqLimitUtil.checkLimitReq("download", 0)) {
+                    writeErrorMessage(request, response, AttachmentUtil.getUploadOrDownloadLimitTips("downloadlimit"));
+                    return;
+                }
+
+                String type = request.getParameter("type");
+                id = request.getParameter("id");
+                if (StringUtils.isBlank(path) && StringUtils.isNotBlank(type) && StringUtils.isNotBlank(id)) {
+                    this.downloadFormAtt(id, type, request, response);
+                    return;
+                }
+
+                if (!StringUtils.isBlank(path)) {
+                    if (!getFileService().exists(path)) {
+                        if (AttachmentUtil.isFrom3rdPreviewOrEdit(request)) {
+                            response.setStatus(500);
+                        }
+
+                        ActionUtil.writeResponseResult(response, ResManager.loadKDString("附件不存在!", "AttachmentAction_13", "bos-webactions", new Object[0]));
+                        return;
+                    }
+
+                    FilePermissionParam filePermissionParam = new FilePermissionParam(request.getParameter("fId"), request.getParameter("appId"), path, FileTypeEnum.ATTACHMENT, request);
+                    PermissionResult permissionResult = getFilePermissionExtension().checkPermission(filePermissionParam);
+                    if (!permissionResult.getHasPermission()) {
+                        writeErrorMessage(request, response, permissionResult.getMsg());
+                        return;
+                    }
+
+                    String userAgent = request.getHeader("USER-AGENT");
+                    response.setCharacterEncoding("UTF-8");
+                    response.setContentType("application/octet-stream");
+                    fileName = FileActionExtensionFactory.getFileActionExtension().getFileName(path, fileName);
+                    fileName = FileSecurityUtil.getSecurityHeader(fileName);
+                    if (StringUtils.isNotBlank(userAgent) && (userAgent.contains("Andorid") || userAgent.contains("Android"))) {
+                        response.addHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + "\"");
+                    } else if (this.checkIsWpsEdit(request)) {
+                        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+                    } else {
+                        response.addHeader("Content-Disposition", "attachment;filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+                    }
+
+                    log.info("before-----------------------getFileService().download(path, response, userAgent)-----------------");
+                    String rangeString = request.getHeader("Range");
+                    if (rangeString != null && fileName.endsWith("mp4")) {
+                        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                        getFileService().download(path, stream, userAgent);
+                        this.breakpointContinuation(request, response, stream);
+                        sucess = Boolean.TRUE;
+                        return;
+                    }
+
+                    this.doDownload(fileName, path, request, response, userAgent);
+                    sucess = Boolean.TRUE;
+                    return;
+                }
+
+                writeErrorMessage(request, response, ResManager.loadKDString("参数错误:缺少path参数。", "AttachmentAction_17", "bos-webactions", new Object[0]));
+            } catch (KDException var19) {
+                log.error(var19);
+                if (AttachmentUtil.isFrom3rdPreviewOrEdit(request)) {
+                    response.setStatus(500);
+                }
+
+                if (AttachExceptionHandler.dealException(var19, response)) {
+                    return;
+                }
+
+                try {
+                    Map<String, String> result = new HashMap();
+                    result.put("status", "error");
+                    id = SerializationUtils.toJsonString(result);
+                    ActionUtil.writeResponseResult(response, id);
+                    return;
+                } catch (Exception var18) {
+                    log.error(var18);
+                    return;
+                }
+            } catch (Throwable var20) {
+                log.error(var20);
+                throw var20;
+            }
+        } finally {
+            this.doFileDownloadLog(sucess, request, fileName);
+        }
+
+    }
+
+    private void doDownload(String fileName, String path, HttpServletRequest request, HttpServletResponse response, String userAgent) throws IOException {
+        boolean isFromYunPan = StringUtils.equals(request.getParameter("source"), "preview");
+        userAgent = isFromYunPan ? request.getParameter("source") : userAgent;
+        boolean fromOnlyOffice = AttachmentPreviewUtil.fromOnlyOfficeDownLoad(request);
+        boolean isAddDownloadWaterMark = WaterMarkKit.isAddDownloadWaterMark(fileName, request, fromOnlyOffice);
+        if (isAddDownloadWaterMark && !isFromYunPan) {
+            this.doWatermark(path, userAgent, request, fileName, response);
+        } else {
+            this.tryAddContentLengthHeader(path, response);
+            getFileService().download(path, response, userAgent);
+        }
+    }
+
+    private void doWatermark(String path, String userAgent, HttpServletRequest request, String fileName, HttpServletResponse response) throws IOException {
+        FileOutputStream fileOutputStream = null;
+        OutputStream out = null;
+
+        try {
+            String tempFilePath = FileUtils.checkFileUrl(IWatermarkHandler.TEMP_DIR + "tempFile" + UUID.randomUUID().toString().replace("-", ""));
+            FileTimeoutUtil.removeFile(tempFilePath, (InputStream)null, 120000L);
+            fileOutputStream = new FileOutputStream(tempFilePath);
+            getFileService().download(path, fileOutputStream, userAgent);
+            InputStream tempInputStream = new FileInputStream(tempFilePath);
+            InputStream waterMarked = WaterMarkKit.addWaterMark(request, fileName, tempInputStream);
+            response.addHeader("Content-Length", String.valueOf(waterMarked.available()));
+            out = response.getOutputStream();
+            byte[] buffer = new byte[8192];
+
+            int len;
+            while((len = waterMarked.read(buffer)) != -1) {
+                out.write(buffer, 0, len);
+            }
+
+            out.flush();
+        } catch (Exception var16) {
+            log.error(var16);
+        } finally {
+            FileSecurityUtil.safeClose(fileOutputStream);
+            FileSecurityUtil.safeClose(out);
+        }
+
+    }
+
+    private void tryAddContentLengthHeader(String path, HttpServletResponse response) {
+        try {
+            long fileSize = getFileService().getFileSize(path);
+            log.info("kd.bos.web.actions.AttachmentAction.download,fileSize = " + fileSize);
+            if (fileSize > 0L) {
+                response.addHeader("Content-Length", fileSize + "");
+            }
+        } catch (Exception var5) {
+            log.error(var5);
+        }
+
+    }
+
+    private void doFileDownloadLog(Boolean sucess, HttpServletRequest request, String fileName) {
+        Object path = request.getAttribute("path");
+        if (StringUtils.isBlank(fileName) && path instanceof String && StringUtils.isNotBlank(path)) {
+            fileName = (String)path;
+            fileName = fileName.substring(fileName.lastIndexOf(47) + 1);
+        }
+
+        String entityNum = request.getParameter("fId");
+        if (sucess) {
+            FileOperationLog.getInstance().createAttachLog(request, fileName);
+        } else {
+            FileOperationLog.getInstance().createAppLog(entityNum, FileOperationEnum.DOWNLOAD_FAIL.getOpName(), String.format(FileOperationEnum.DOWNLOAD_FAIL.getOpDescFormat(), fileName));
+        }
+    }
+
+    private void breakpointContinuation(HttpServletRequest servletRequest, HttpServletResponse servletResponse, ByteArrayOutputStream outstream) throws Exception {
+        InputStream in = new ByteArrayInputStream(outstream.toByteArray());
+        long fileLength = (long)in.available();
+        long start = 0L;
+        long end = fileLength - 1L;
+        long contentLength = 0L;
+        String requestRange = "";
+        String contentRange = "bytes ";
+        BufferedInputStream bis = new BufferedInputStream(in);
+        servletResponse.setHeader("Accept-Ranges", "bytes");
+        String range = servletRequest.getHeader("Range");
+        if (StringUtils.isNotBlank(range)) {
+            servletResponse.setStatus(206);
+            requestRange = range.replaceAll("bytes=", "");
+            String[] split = requestRange.split("-");
+            if (split.length == 1) {
+                start = Long.parseLong(split[0].trim());
+            } else if (split.length > 1) {
+                start = Long.parseLong(split[0].trim());
+                long requestEnd = Long.parseLong(split[1].trim());
+                end = Math.min(end, requestEnd);
+            }
+
+            contentLength = end - start + 1L;
+        }
+
+        if (contentLength < 0L) {
+            throw new KDException(new ErrorCode("parameter is illegal", ResManager.loadKDString("参数不合法!", "AttachmentAction_12", "bos-webactions", new Object[0])), new Object[0]);
+        } else {
+            contentRange = contentRange + start + "-" + end + "/" + fileLength;
+            servletResponse.setHeader("Content-Length", String.valueOf(contentLength));
+            servletResponse.setHeader("Content-Range", contentRange);
+            servletResponse.setHeader("Content-Type", "video/mp4");
+            bis.skip(start);
+            OutputStream bos = new BufferedOutputStream(servletResponse.getOutputStream());
+
+            try {
+                long sumReadLen = 0L;
+                byte[] bytes = new byte[8192];
+                int len;
+                if (range.endsWith("-")) {
+                    while((len = bis.read(bytes)) != -1) {
+                        bos.write(bytes, 0, len);
+                    }
+                } else {
+                    while(sumReadLen <= contentLength - (long)bytes.length) {
+                        len = bis.read(bytes);
+                        sumReadLen += (long)len;
+                        bos.write(bytes, 0, len);
+                    }
+
+                    if (sumReadLen <= contentLength) {
+                        len = bis.read(bytes, 0, (int)(contentLength - sumReadLen));
+                        bos.write(bytes, 0, len);
+                    }
+                }
+
+                bos.flush();
+            } catch (Exception var25) {
+                IOUtils.close(new Closeable[]{outstream, in, bos, bis});
+            } finally {
+                IOUtils.close(new Closeable[]{outstream, in, bos, bis});
+            }
+
+        }
+    }
+
+    private void downloadFormAtt(String id, String type, HttpServletRequest request, HttpServletResponse response) throws IOException {
+        Map<String, Object> map = (Map)DispatchServiceHelper.invokeBOSService(IAttachmentService.class.getSimpleName(), "getAttachmentInfoById", new Object[]{AttachmentType.getTypeByValue(type), id});
+        String path = map.get("path").toString();
+        if (!getFileService().exists(path)) {
+            if (AttachmentUtil.isFrom3rdPreviewOrEdit(request)) {
+                response.setStatus(500);
+            }
+
+            ActionUtil.writeResponseResult(response, ResManager.loadKDString("附件不存在!", "AttachmentAction_13", "bos-webactions", new Object[0]));
+        } else {
+            String fileName = map.get("fileName").toString();
+            String userAgent = request.getHeader("USER-AGENT");
+            response.setCharacterEncoding("UTF-8");
+            response.setContentType("application/octet-stream");
+            fileName = FileSecurityUtil.getSecurityHeader(fileName);
+            if (!StringUtils.isNotBlank(userAgent) || !userAgent.contains("Andorid") && !userAgent.contains("Android")) {
+                response.addHeader("Content-Disposition", "attachment;filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+            } else {
+                response.addHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + "\"");
+            }
+
+            getFileService().download(path, response, userAgent);
+        }
+    }
+
+    public void downloadAll(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        if (FileReqLimitUtil.checkLimitReq("download", 0)) {
+            writeErrorMessage(response, AttachmentUtil.getUploadOrDownloadLimitTips("downloadlimit"));
+        } else {
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+            String fId = request.getParameter("fId");
+            String pageId = request.getParameter("pageId");
+            Object billPkId = FilePremissionUtil.genBillPkId(pageId);
+            String date = dateFormat.format(new Date());
+            String fileName = this.getDoloadAllZipFileName(fId, date);
+            boolean hasRight = FilePremissionUtil.tryGetHasRightValue(request);
+            ArrayList fileList;
+            if (!hasRight) {
+                Map<String, Object> param = new HashMap();
+                param.put("permItem", "2NJ5XVVCMBCL");
+                param.put("entityNum", fId);
+                param.put("billPkId", billPkId);
+                fileList = new ArrayList();
+                fileList.add(param);
+                Boolean hasPermission = Boolean.TRUE;
+
+                try {
+                    hasPermission = FilePremissionUtil.hasEntityPermission(fileList);
+                } catch (KDException var38) {
+                    if (AttachExceptionHandler.dealException(var38, response)) {
+                        return;
+                    }
+                }
+
+                if (!hasPermission) {
+                    writeErrorMessage(response, FilePremissionUtil.genNoPermissionTips(fId, "2NJ5XVVCMBCL"));
+                    return;
+                }
+            }
+
+            List<Map<String, String>> fileInfoList = (List)SerializationUtils.fromJsonString(request.getParameter("fileInfo"), ArrayList.class);
+            fileList = new ArrayList(fileInfoList.size());
+            Map<String, String> urlMap = new HashMap();
+            TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+            Iterator var14 = fileInfoList.iterator();
+
+            String zipForLocalUrl;
+            while(var14.hasNext()) {
+                Map<String, String> fileMap = (Map)var14.next();
+                String url = (String)fileMap.get("url");
+                String path = "";
+
+                try {
+                    if (this.isTempFile(url)) {
+                        if (!cache.exists(url)) {
+                            continue;
+                        }
+
+                        path = this.getPathfromTempDownloadUrl(url);
+                    } else {
+                        path = this.getPathfromDownloadUrl(url);
+                    }
+                } catch (KDException var37) {
+                    log.error(var37);
+                    if (AttachExceptionHandler.dealException(var37, response)) {
+                        return;
+                    }
+                }
+
+                if (!this.verifyFilePermissions(request, path)) {
+                    zipForLocalUrl = (String)fileMap.get("name");
+                    String subFile = path.substring(path.lastIndexOf(47) + 1);
+                    if (StringUtils.isNotBlank(zipForLocalUrl)) {
+                        subFile = zipForLocalUrl;
+                    }
+
+                    subFile = FileActionExtensionFactory.getFileActionExtension().getFileName(path, subFile);
+                    BatchDownloadRequest.File file = new BatchDownloadRequest.File(subFile, path);
+                    fileList.add(file);
+                    urlMap.put(subFile, path);
+                }
+            }
+
+            if (urlMap.isEmpty()) {
+                writeErrorMessage(response, ResManager.loadKDString("不支持下载已过期的临时文件。", "AttachmentAction_16", "bos-attachment", new Object[0]));
+            } else {
+                BatchDownloadRequest.File[] files = (BatchDownloadRequest.File[])fileList.toArray(new BatchDownloadRequest.File[fileList.size()]);
+                String userAgent = request.getHeader("USER-AGENT");
+                fileName = FileActionExtensionFactory.getFileActionExtension().getFileName(fileName);
+                BatchDownloadRequest batchDownloadRequest = new BatchDownloadRequest(fileName);
+                batchDownloadRequest.setFiles(files);
+                response.setCharacterEncoding("UTF-8");
+                response.setContentType("application/octet-stream");
+                fileName = FileSecurityUtil.getSecurityHeader(fileName);
+                if (!StringUtils.isNotBlank(userAgent) || !userAgent.contains("Andorid") && !userAgent.contains("Android")) {
+                    response.addHeader("Content-Disposition", "attachment;filename*=UTF-8''" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+                } else {
+                    response.addHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + "\"");
+                }
+
+                File saveDir = FileHandlerUtil.createSaveDir("download_all");
+
+                InputStream in;
+                String name;
+                for(Iterator var45 = urlMap.entrySet().iterator(); var45.hasNext(); FileHandlerUtil.flushToLocal(name, "", in, saveDir, false)) {
+                    Map.Entry<String, String> entry = (Map.Entry)var45.next();
+                    name = (String)entry.getKey();
+                    String url = (String)entry.getValue();
+                    if (this.isTempFile(url)) {
+                        in = cache.getInputStream(url);
+                    } else {
+                        in = getFileService().getInputStream(url);
+                    }
+
+                    boolean isAddDownloadWaterMark = WaterMarkKit.isAddDownloadWaterMark(name, request);
+                    if (isAddDownloadWaterMark) {
+                        in = WaterMarkKit.addWaterMark(request, name, in);
+                    }
+                }
+
+                zipForLocalUrl = FileHandlerUtil.createZipForLocal(fileName, "", saveDir, false);
+                BufferedInputStream in2 = new BufferedInputStream(new FileInputStream(zipForLocalUrl));
+                ServletOutputStream out = response.getOutputStream();
+                boolean var34 = false;
+
+                File dirFile;
+                File[] var51;
+                int var52;
+                int len;
+                label325: {
+                    try {
+                        var34 = true;
+                        byte[] buffer = new byte[8192];
+
+                        while((len = in2.read(buffer)) != -1) {
+                            out.write(buffer, 0, len);
+                        }
+
+                        out.flush();
+                        var34 = false;
+                        break label325;
+                    } catch (IOException var35) {
+                        log.error(var35);
+                        var34 = false;
+                    } finally {
+                        if (var34) {
+                            FileSecurityUtil.safeClose(out);
+                            FileSecurityUtil.safeClose(in2);
+                            File[] var26 = saveDir.listFiles();
+                            int var27 = var26.length;
+
+                            for(int var28 = 0; var28 < var27; ++var28) {
+                                File dirFile1 = var26[var28];
+                                FileSecurityUtil.safeDeleteFile(dirFile1);
+                            }
+
+                            FileSecurityUtil.safeDeleteFile(saveDir);
+                        }
+                    }
+
+                    FileSecurityUtil.safeClose(out);
+                    FileSecurityUtil.safeClose(in2);
+                    var51 = saveDir.listFiles();
+                    len = var51.length;
+
+                    for(var52 = 0; var52 < len; ++var52) {
+                        dirFile = var51[var52];
+                        FileSecurityUtil.safeDeleteFile(dirFile);
+                    }
+
+                    FileSecurityUtil.safeDeleteFile(saveDir);
+                    return;
+                }
+
+                FileSecurityUtil.safeClose(out);
+                FileSecurityUtil.safeClose(in2);
+                var51 = saveDir.listFiles();
+                len = var51.length;
+
+                for(var52 = 0; var52 < len; ++var52) {
+                    dirFile = var51[var52];
+                    FileSecurityUtil.safeDeleteFile(dirFile);
+                }
+
+                FileSecurityUtil.safeDeleteFile(saveDir);
+            }
+        }
+    }
+
+    private boolean isTempFile(String url) {
+        return url.contains("tempfile/download.do?configKey");
+    }
+
+    private boolean verifyFilePermissions(HttpServletRequest request, String path) {
+        FilePermissionParam filePermissionParam = new FilePermissionParam(request.getParameter("fId"), request.getParameter("appId"), path, FileTypeEnum.ATTACHMENT, request);
+        PermissionResult permissionResult = getFilePermissionExtension().checkPermission(filePermissionParam);
+        return !permissionResult.getHasPermission();
+    }
+
+    private String getDoloadAllZipFileName(String fId, String date) {
+        String fileName = String.format(ResManager.loadKDString("全部附件_%s.zip", "AttachmentAction_4", "bos-attachment", new Object[0]), date);
+        if (StringUtils.isBlank(fId)) {
+            return fileName;
+        } else {
+            fileName = fId + "_" + date + ".zip";
+            LocaleString displayName = null;
+            FormConfig formConfig = null;
+
+            try {
+                formConfig = FormMetadataCache.getFormConfig(fId);
+                if (formConfig != null) {
+                    displayName = formConfig.getCaption();
+                    fileName = displayName.toString() + "_" + date + ".zip";
+                }
+            } catch (Exception var7) {
+                log.error(var7);
+            }
+
+            return fileName;
+        }
+    }
+
+    public void upload(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        boolean isTempStorage = Boolean.parseBoolean(request.getParameter("tempstorage"));
+        if (isTempStorage) {
+            this.uploadFile(request, response);
+        } else {
+            FileItem fileItem = FileItemParser.parseHttpRequest(request);
+            if (!FileUploadUrlUtil.isFileUploadWithUrl()) {
+                fileItem.setPath(FileUploadUrlUtil.getAttachmentRandomUrl(fileItem.getFileName()));
+            }
+
+            this.upload0(fileItem, request, response, FileSource.UPLOAD_BUTTON);
+        }
+
+    }
+
+    public void uploadFile(HttpServletRequest request, HttpServletResponse response) {
+        String type;
+        try {
+            log.info("kd.bos.web.actions.AttachmentAction.uploadFile start ---- ");
+            String contentType = request.getContentType();
+            int timeout = 7200;
+            type = request.getParameter("type");
+            log.info("kd.bos.web.actions.AttachmentAction.uploadFile type ---- " + type);
+            String files;
+            String downloadUrl;
+            String tempFileId;
+            if ("mobile".equals(type)) {
+                if (!FilePremissionUtil.checkUploadPermission(request, response, request.getParameter("appId"), request.getParameter("fId"), "2NJ5SR4NUEJ=")) {
+                    return;
+                }
+
+                List<Map<String, Object>> listMaps = new ArrayList();
+                List<FileRecord> recordList = new ArrayList(10);
+                List<FilePermissionParam> permissionParamList = new ArrayList(10);
+                files = request.getParameter("files");
+                List<Object> jsonArray = JSON.parseArray(files);
+                log.info("kd.bos.web.actions.AttachmentAction.uploadFile mobile ---- files" + files);
+                Iterator var110 = jsonArray.iterator();
+
+                while(true) {
+                    if (var110.hasNext()) {
+                        Object object = var110.next();
+                        JSONObject jsonObject = (JSONObject)object;
+                        downloadUrl = (String)jsonObject.get("downloadUrl");
+                        log.info("kd.bos.web.actions.AttachmentAction.uploadFile mobile ---- downloadUrl " + downloadUrl);
+                        tempFileId = "";
+                        String fileName = (String)jsonObject.get("fileName");
+                        String urlLower = downloadUrl.toLowerCase(Locale.ENGLISH);
+                        if (!urlLower.startsWith("https") && urlLower.startsWith("http")) {
+                            String right = downloadUrl.substring(4);
+                            downloadUrl = "https" + right;
+                        }
+
+                        if (!this.checkDownloadUrl(response, downloadUrl)) {
+                            return;
+                        }
+
+                        InputStream in = this.getInputStreamByUrl(downloadUrl, false);
+                        Throwable var20 = null;
+
+                        try {
+                            BufferedInputStream buffInputStream = new BufferedInputStream(in);
+                            Throwable var22 = null;
+
+                            try {
+                                log.info("uploadFile InputStream.available() " + buffInputStream.available());
+                                String speSymbol = null;
+                                if (contentType != null && !contentType.contains("WebCtrlWebOffice")) {
+                                    speSymbol = checkSpeSymbol(fileName, response);
+                                }
+
+                                if (StringUtils.isNotBlank(speSymbol)) {
+                                    writeErrorMessage(response, String.format(ResManager.loadKDString("文件名不能包含下列任何字符:%s。", "AttachmentAction_15", "bos-webactions", new Object[0]), speSymbol));
+                                    return;
+                                }
+
+                                if (!this.isForbidUpload(request, response, fileName, buffInputStream)) {
+                                    if (!this.checkMaxSize(response, (long)(new URL(downloadUrl)).openConnection().getContentLength())) {
+                                        return;
+                                    }
+
+                                    if (FileReqLimitUtil.checkLimitReq("uploadFile", buffInputStream.available())) {
+                                        writeErrorMessage(response, AttachmentUtil.getUploadOrDownloadLimitTips("uploadlimit"));
+                                        return;
+                                    }
+
+                                    TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+                                    log.info("before cache.saveAsUrl InputStream.available() " + buffInputStream.available());
+                                    if (fileName.toLowerCase(Locale.ENGLISH).startsWith("timeouttest")) {
+                                        String tempTime = fileName.replace("timeouttest", "");
+                                        timeout = Integer.parseInt(tempTime.substring(0, tempTime.lastIndexOf(46))) * 60;
+                                    }
+
+                                    InputStream finalInputStream = FileChecker.addSecurityWaterMark(fileName, buffInputStream);
+                                    String tempUrl = cache.saveAsUrl(fileName, finalInputStream, timeout);
+                                    String[] split = tempUrl.split("id=");
+                                    if (split.length >= 2) {
+                                        tempFileId = split[1];
+                                    }
+
+                                    tempUrl = EncreptSessionUtils.encryptSession(tempUrl);
+                                    String address = RequestContext.get().getClientFullContextPath();
+                                    if (!address.endsWith("/")) {
+                                        address = address + "/";
+                                    }
+
+                                    tempUrl = address + tempUrl;
+                                    Map<String, Object> maps = new HashMap();
+                                    maps.put("downloadUrl", tempUrl);
+                                    maps.put("fileExt", jsonObject.get("fileExt"));
+                                    maps.put("fileId", jsonObject.get("fileId"));
+                                    maps.put("fileName", fileName);
+                                    maps.put("fileSize", jsonObject.get("fileSize"));
+                                    maps.put("fileTime", jsonObject.get("fileTime"));
+                                    Object localId = jsonObject.get("localId");
+                                    if (localId != null) {
+                                        redisCache.put("AttachmentAction:uploadFile:localIds:" + tempFileId, (String)localId, 7200);
+                                        maps.put("localId", localId);
+                                    }
+
+                                    listMaps.add(maps);
+                                    FileRecord fileRecord = new FileRecord();
+                                    fileRecord.setUrl(tempUrl);
+                                    fileRecord.setType("1");
+                                    fileRecord.setUserId(RequestContext.get().getCurrUserId());
+                                    fileRecord.setCreateTime(new Date());
+                                    recordList.add(fileRecord);
+                                    FilePermissionParam filePermissionParam = new FilePermissionParam(request.getParameter("fId"), request.getParameter("appId"), tempUrl, FileTypeEnum.TEMPFILE, request);
+                                    permissionParamList.add(filePermissionParam);
+                                    continue;
+                                }
+                            } catch (Throwable var95) {
+                                var22 = var95;
+                                throw var95;
+                            } finally {
+                                if (buffInputStream != null) {
+                                    if (var22 != null) {
+                                        try {
+                                            buffInputStream.close();
+                                        } catch (Throwable var94) {
+                                            var22.addSuppressed(var94);
+                                        }
+                                    } else {
+                                        buffInputStream.close();
+                                    }
+                                }
+
+                            }
+                        } catch (Throwable var97) {
+                            var20 = var97;
+                            throw var97;
+                        } finally {
+                            if (in != null) {
+                                if (var20 != null) {
+                                    try {
+                                        in.close();
+                                    } catch (Throwable var93) {
+                                        var20.addSuppressed(var93);
+                                    }
+                                } else {
+                                    in.close();
+                                }
+                            }
+
+                        }
+
+                        return;
+                    }
+
+                    FileRecordUtil.doLog(recordList);
+                    getFilePermissionExtension().dealPermissionParam(permissionParamList);
+                    this.writeTempUrls(response, listMaps);
+                    break;
+                }
+            } else {
+                FileItem fileItem = FileItemParser.parseHttpRequest(request);
+
+                try {
+                    InputStream inputStream = fileItem.getInputStream();
+                    BufferedInputStream buffInputStream = new BufferedInputStream(inputStream);
+                    log.info("uploadFile InputStream.available() " + buffInputStream.available());
+                    String fileName = fileItem.getFileName();
+                    log.info("kd.bos.web.actions.AttachmentAction.uploadFile fileName ---- " + fileName);
+                    if (!FilePremissionUtil.checkUploadPermission(request, response, fileItem.getAppId(), fileItem.getFId(), "2NJ5SR4NUEJ=", fileItem)) {
+                        return;
+                    }
+
+                    String speSymbol = null;
+                    if (contentType != null && !contentType.contains("WebCtrlWebOffice")) {
+                        speSymbol = checkSpeSymbol(fileName, response);
+                    }
+
+                    if (StringUtils.isNotBlank(speSymbol)) {
+                        writeErrorMessage(response, String.format(ResManager.loadKDString("文件名不能包含下列任何字符:%s。", "AttachmentAction_15", "bos-webactions", new Object[0]), speSymbol));
+                        return;
+                    }
+
+                    if (this.isForbidUpload(request, response, fileName, buffInputStream)) {
+                        return;
+                    }
+
+                    if (this.checkMaxSize(response, (long)buffInputStream.available())) {
+                        if (FileReqLimitUtil.checkLimitReq("uploadFile", buffInputStream.available())) {
+                            writeErrorMessage(response, AttachmentUtil.getUploadOrDownloadLimitTips("uploadlimit"));
+                            return;
+                        }
+
+                        TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+                        log.info("before cache.saveAsUrl InputStream.available() " + buffInputStream.available());
+                        log.info("TempFileExtension.impl.classname is " + System.getProperty("TempFileExtension.impl.classname", ""));
+                        Map<String, Object> securityParam = ((IParameterReaderService)ServiceSvcFactory.getService(IParameterReaderService.class)).loadPublicParameterFromCache("bos_filesecurityparam");
+                        Boolean checkExcel = securityParam.size() == 0 ? SystemPropertyUtils.getBoolean(RequestContext.get().getTenantId(), "check.file.security", Boolean.FALSE) : Boolean.parseBoolean(String.valueOf(securityParam.get("excelsecurity")));
+                        log.info("check.file.security is " + checkExcel);
+                        InputStream finalInputStream = FileChecker.addSecurityWaterMark(fileName, buffInputStream);
+                        downloadUrl = cache.saveAsUrl(fileItem.getFileName(), finalInputStream, timeout);
+                        if ("mobilelocal".equals(type) || "mobilelocal".equals(fileItem.getType()) || AttachmentPreviewUtil.isOnlyOfficePreView()) {
+                            downloadUrl = EncreptSessionUtils.encryptSession(downloadUrl);
+                        }
+
+                        if (AttachmentPreviewUtil.isOnlyOfficePreView()) {
+                            downloadUrl = AttachmentPreviewUtil.addFileIdParam(downloadUrl, fileItem.getFId());
+                        }
+
+                        tempFileId = RequestContext.get().getClientFullContextPath();
+                        if (!tempFileId.endsWith("/")) {
+                            tempFileId = tempFileId + "/";
+                        }
+
+                        downloadUrl = tempFileId + downloadUrl;
+                        FileRecord fileRecord = new FileRecord();
+                        fileRecord.setUrl(downloadUrl);
+                        fileRecord.setType("1");
+                        fileRecord.setUserId(RequestContext.get().getCurrUserId());
+                        fileRecord.setCreateTime(new Date());
+                        FileRecordUtil.doLog(Collections.singletonList(fileRecord));
+                        FilePermissionParam filePermissionParam = new FilePermissionParam(fileItem.getFId(), fileItem.getAppId(), downloadUrl, FileTypeEnum.TEMPFILE, request);
+                        getFilePermissionExtension().dealPermissionParam(Collections.singletonList(filePermissionParam));
+                        this.writeTempUrl(response, downloadUrl);
+                        return;
+                    }
+                } catch (Exception var99) {
+                    throw var99;
+                } finally {
+                    fileItem.close();
+                }
+
+                return;
+            }
+        } catch (Exception var101) {
+            Exception e = var101;
+
+            try {
+                log.error(e);
+                if (e instanceof KDException && AttachExceptionHandler.dealException((KDException)e, response)) {
+                    return;
+                }
+
+                Map<String, String> result = new HashMap();
+                result.put("status", "error");
+                type = ResManager.loadKDString("文件上传失败,请查日志分析。", "AttachmentAction_8", "bos-webactions", new Object[0]);
+                result.put("description", type);
+                String jsonResult = SerializationUtils.toJsonString(result);
+                ActionUtil.writeResponseJson(response, jsonResult);
+            } catch (IOException var92) {
+                log.error(var92);
+            }
+        }
+
+    }
+
+    public static long getMaxSize() {
+        Object sizeMb = DispatchServiceHelper.invokeBOSService("SystemParamService", "loadPublicParameterFromCache", new Object[]{"maxuploadsize"});
+        long mcSizeMb = getFileService().maxUploadSize() == 0L ? 52428800L : getFileService().maxUploadSize();
+        if (sizeMb != null && !"0".equals(sizeMb.toString())) {
+            long size = Long.parseLong(String.valueOf(sizeMb));
+            return Math.min(size * 1024L * 1024L, mcSizeMb);
+        } else {
+            return mcSizeMb;
+        }
+    }
+
+    public boolean getCheckuploadurl() {
+        Object checkUploadUrlStr = DispatchServiceHelper.invokeBOSService("SystemParamService", "loadPublicParameterFromCache", new Object[]{"checkuploadurl"});
+        return checkUploadUrlStr == null ? false : Boolean.parseBoolean(checkUploadUrlStr.toString());
+    }
+
+    private String getYZJUrl() {
+        AppParam appParam = new AppParam();
+        appParam.setAppId("83bfebc8000037ac");
+        appParam.setViewType("15");
+        appParam.setOrgId(OrgUnitServiceHelper.getRootOrgId());
+        appParam.setActBookId(0L);
+        Object yzjurlStr = SystemParamServiceHelper.loadAppParameterFromCache(appParam, "yzjurl");
+        return yzjurlStr == null ? "" : yzjurlStr.toString();
+    }
+
+    private boolean checkIsWpsEdit(HttpServletRequest request) {
+        String fileHeader = request.getParameter("fileHeader");
+        return StringUtils.equals("1", fileHeader);
+    }
+
+    private boolean checkMaxSize(HttpServletResponse response, long fileSize) throws IOException {
+        long maxSize = getMaxSize();
+        if (fileSize > maxSize) {
+            FileReqLimitUtil.writeOverMaxSize(response, fileSize, maxSize);
+            return false;
+        } else {
+            return FileReqLimitUtil.checkTotalFileSize(response, "uploadFile", fileSize, maxSize * 3L, "", true);
+        }
+    }
+
+    private void writeTempUrl(HttpServletResponse response, String url) throws IOException {
+        Map<String, Object> result = new HashMap();
+
+        try {
+            result.put("url", url);
+            result.put("status", "success");
+        } catch (Exception var8) {
+            result.put("status", "error");
+            StringWriter sw = new StringWriter();
+            new PrintWriter(sw);
+            String msg = sw.toString();
+            result.put("description", msg);
+            log.error(var8);
+        }
+
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    private void writeTempUrls(HttpServletResponse response, List<Map<String, Object>> listMaps) throws IOException {
+        Map<String, Object> result = new HashMap();
+
+        try {
+            result.put("files", listMaps);
+            result.put("status", "success");
+        } catch (Exception var8) {
+            result.put("status", "error");
+            StringWriter sw = new StringWriter();
+            new PrintWriter(sw);
+            String msg = sw.toString();
+            result.put("description", msg);
+            log.error(var8);
+        }
+
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    public void upload2(String fileName, String path, InputStream in, HttpServletResponse servletResponse) {
+        try {
+            FileItem fileItem = this.createFileItem(fileName, path, in, FileSource.Unknown, (String)null, (String)null);
+            this.upload0(fileItem, (HttpServletRequest)null, servletResponse, FileSource.Unknown);
+        } catch (Exception var10) {
+            Exception e = var10;
+
+            try {
+                log.error(e);
+                Map<String, String> result = new HashMap();
+                result.put("status", "error");
+                String msg = ResManager.loadKDString("文件上传失败,请查日志分析。", "AttachmentAction_8", "bos-webactions", new Object[0]);
+                result.put("description", msg);
+                String jsonResult = SerializationUtils.toJsonString(result);
+                ActionUtil.writeResponseJson(servletResponse, jsonResult);
+            } catch (IOException var9) {
+                log.error(var9);
+            }
+        }
+
+    }
+
+    private String getNewFilePath(String oldPath, String fileName) {
+        String selfPath = oldPath;
+        boolean encrptyPath = Boolean.parseBoolean(String.valueOf(DispatchServiceHelper.invokeBOSService(IAttachmentService.class.getSimpleName(), "isEncrptyPath", new Object[0])));
+        if (oldPath == null || !oldPath.contains("/")) {
+            selfPath = this.generateAttPath(fileName);
+        }
+
+        return !encrptyPath ? selfPath : this.generateAttPath(fileName);
+    }
+
+    private String generateAttPath(String fileName) {
+        String rootPath = System.getProperty("attachment.rootpath", "/");
+        if (!rootPath.startsWith("/")) {
+            rootPath = "/" + rootPath;
+        }
+
+        if (rootPath.endsWith("/")) {
+            rootPath = rootPath.substring(0, rootPath.length() - 1);
+        }
+
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        RequestContext rc = RequestContext.get();
+        return rootPath + FileNameUtils.getAttachmentFileName(rc.getTenantCode(), rc.getAccountId(), uuid, fileName);
+    }
+
+    private void upload0(FileItem fileItem, HttpServletRequest request, HttpServletResponse response, FileSource fileSource) throws IOException {
+        Map<String, Object> result = new HashMap();
+        String fileName = fileItem.getFileName();
+        if (FilePremissionUtil.checkUploadPermission(request, response, fileItem.getAppId(), fileItem.getFId(), "2NJ5SR4NUEJ=", fileItem)) {
+            String speSymbol = checkSpeSymbol(fileName, response);
+            if (StringUtils.isNotBlank(speSymbol)) {
+                writeErrorMessage(response, String.format(ResManager.loadKDString("文件名不能包含下列任何字符:%s。", "AttachmentAction_15", "bos-webactions", new Object[0]), speSymbol));
+            } else {
+                InputStream inputStream = fileItem.getInputStream();
+                Throwable var9 = null;
+
+                try {
+                    BufferedInputStream buffInputStream = new BufferedInputStream(inputStream);
+                    Throwable var11 = null;
+
+                    try {
+                        log.info("upload0 InputStream.available() " + buffInputStream.available());
+                        if (FileReqLimitUtil.checkLimitReq("uploadFile", buffInputStream.available())) {
+                            writeErrorMessage(response, AttachmentUtil.getUploadOrDownloadLimitTips("uploadlimit"));
+                        } else if (!this.isForbidUpload(request, response, fileName, buffInputStream)) {
+                            if (this.checkMaxSize(response, (long)buffInputStream.available())) {
+                                String path;
+                                try {
+                                    path = this.getNewFilePath(fileItem.getPath(), fileItem.getFileName());
+
+                                    try {
+                                        path = FilePathUtil.dealPath(path, "attach");
+                                    } catch (KDException var138) {
+                                        if (AttachExceptionHandler.dealException(var138, response)) {
+                                            return;
+                                        }
+                                    }
+
+                                    InputStream finalInputStream = FileChecker.addSecurityWaterMark(fileName, buffInputStream);
+                                    fileItem = this.createFileItem(fileItem.getFileName(), path, finalInputStream, fileSource, fileItem.getAppId(), fileItem.getFId());
+                                    if (path.contains("+")) {
+                                        path = path.replace('+', '_');
+                                        fileItem = this.createFileItem(fileItem.getFileName(), path, finalInputStream, fileSource, fileItem.getAppId(), fileItem.getFId());
+                                    }
+
+                                    log.info("before FileService.upload InputStream.available() " + finalInputStream.available());
+                                    log.info("fileservice.ext.impl.classname is " + System.getProperty("fileservice.ext.impl.classname"));
+                                    Map<String, Object> securityParam = ((IParameterReaderService)ServiceSvcFactory.getService(IParameterReaderService.class)).loadPublicParameterFromCache("bos_filesecurityparam");
+                                    Boolean checkExcel = securityParam.size() == 0 ? SystemPropertyUtils.getBoolean(RequestContext.get().getTenantId(), "check.file.security", Boolean.FALSE) : Boolean.parseBoolean(String.valueOf(securityParam.get("excelsecurity")));
+                                    log.info("check.file.security is " + checkExcel);
+                                    String url = getFileService().upload(fileItem);
+                                    result.put("url", url);
+                                    result.put("status", "success");
+                                    FileRecord fileRecord = new FileRecord();
+                                    fileRecord.setUrl(url);
+                                    fileRecord.setType("1");
+                                    fileRecord.setUserId(RequestContext.get().getCurrUserId());
+                                    fileRecord.setCreateTime(new Date());
+                                    FileRecordUtil.doLog(Collections.singletonList(fileRecord));
+                                    if (request != null) {
+                                        FilePermissionParam filePermissionParam = new FilePermissionParam(fileItem.getFId(), fileItem.getAppId(), url, FileTypeEnum.ATTACHMENT, request);
+                                        getFilePermissionExtension().dealPermissionParam(Collections.singletonList(filePermissionParam));
+                                    }
+                                } catch (Exception var139) {
+                                    Exception e = var139;
+                                    if (var139 instanceof KDException && AttachExceptionHandler.dealException((KDException)var139, response)) {
+                                        return;
+                                    }
+
+                                    result.put("status", "error");
+                                    StringWriter sw = new StringWriter();
+                                    Throwable var14 = null;
+
+                                    try {
+                                        PrintWriter pw = new PrintWriter(sw);
+                                        Throwable var16 = null;
+
+                                        try {
+                                            String msg = sw.toString();
+                                            log.error("description:" + msg);
+                                            result.put("description", msg);
+                                            log.error(e);
+                                        } catch (Throwable var134) {
+                                            var16 = var134;
+                                            throw var134;
+                                        } finally {
+                                            if (pw != null) {
+                                                if (var16 != null) {
+                                                    try {
+                                                        pw.close();
+                                                    } catch (Throwable var133) {
+                                                        var16.addSuppressed(var133);
+                                                    }
+                                                } else {
+                                                    pw.close();
+                                                }
+                                            }
+
+                                        }
+                                    } catch (Throwable var136) {
+                                        var14 = var136;
+                                        throw var136;
+                                    } finally {
+                                        if (sw != null) {
+                                            if (var14 != null) {
+                                                try {
+                                                    sw.close();
+                                                } catch (Throwable var132) {
+                                                    var14.addSuppressed(var132);
+                                                }
+                                            } else {
+                                                sw.close();
+                                            }
+                                        }
+
+                                    }
+                                } finally {
+                                    fileItem.close();
+                                }
+
+                                path = SerializationUtils.toJsonString(result);
+                                ActionUtil.writeResponseJson(response, path);
+                            }
+                        }
+                    } catch (Throwable var141) {
+                        var11 = var141;
+                        throw var141;
+                    } finally {
+                        if (buffInputStream != null) {
+                            if (var11 != null) {
+                                try {
+                                    buffInputStream.close();
+                                } catch (Throwable var131) {
+                                    var11.addSuppressed(var131);
+                                }
+                            } else {
+                                buffInputStream.close();
+                            }
+                        }
+
+                    }
+                } catch (Throwable var143) {
+                    var9 = var143;
+                    throw var143;
+                } finally {
+                    if (inputStream != null) {
+                        if (var9 != null) {
+                            try {
+                                inputStream.close();
+                            } catch (Throwable var130) {
+                                var9.addSuppressed(var130);
+                            }
+                        } else {
+                            inputStream.close();
+                        }
+                    }
+
+                }
+            }
+        }
+    }
+
+    public void remove(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        Map<String, Object> result = new HashMap();
+
+        try {
+            request.setAttribute("permItem", "2NJ5YJOIS+MJ");
+            String path = HandleFactory.getHandle(request).handler(request);
+            getFileService().delete(path);
+            BillFileMappingWriter.removeMappingRecord(path, "attach");
+            result.put("status", "success");
+        } catch (Exception var9) {
+            log.error(var9);
+            if (var9 instanceof KDException && AttachExceptionHandler.dealException((KDException)var9, response)) {
+                return;
+            }
+
+            result.put("status", "error");
+            StringWriter sw = new StringWriter();
+            new PrintWriter(sw);
+            String msg = sw.toString();
+            result.put("description", msg);
+        }
+
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    private void dealResult(HttpServletResponse response, Map<String, Object> result) throws IOException {
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    private void dealPreviewResult(HttpServletResponse response, Map<String, Object> result, String previewType) throws IOException {
+        String status = (String)result.get(PreviewParams.STATUS.getEnumName());
+        Map<String, Object> mapResult = new HashMap();
+        InputStream in = null;
+        boolean yzjPreview = "1".equals(previewType);
+        boolean wpsPreview = "21".equals(previewType);
+        boolean wpsPrivatePreview = "22".equals(previewType);
+        boolean yozoPreview = "4".equals(previewType);
+        String jsonResult;
+        if (yzjPreview && PreviewParams.ERROR.getEnumName().equals(status)) {
+            mapResult.put(PreviewParams.STATUS.getEnumName(), PreviewParams.ERROR.getEnumName());
+            mapResult.put(PreviewParams.DESCRIPTION.getEnumName(), result.get(PreviewParams.DESCRIPTION.getEnumName()));
+            jsonResult = SerializationUtils.toJsonString(mapResult);
+            JsonUtil.writeResponseJson(response, jsonResult);
+        } else if (!PreviewParams.ERROR.getEnumName().equals(status) && !PreviewParams.XLSX_SUCCESS.getEnumName().equals(status)) {
+            if (!PreviewParams.PDF_SUCCESS.getEnumName().equals(status) && !PreviewParams.NOT_NEED_CHANGE.getEnumName().equals(status)) {
+                if (PreviewParams.URL.getEnumName().equals(status)) {
+                    mapResult.put("url", result.get(PreviewParams.RESULT.getEnumName()));
+                    mapResult.put(PreviewParams.STATUS.getEnumName(), PreviewParams.SUCCESS.getEnumName());
+                    jsonResult = SerializationUtils.toJsonString(mapResult);
+                    JsonUtil.writeResponseJson(response, jsonResult);
+                } else if (!wpsPreview && !wpsPrivatePreview && !yozoPreview) {
+                    if (PreviewParams.SPREADJS_SUCCESS.getEnumName().equals(status)) {
+                        mapResult.put(PreviewParams.STATUS.getEnumName(), PreviewParams.SUCCESS.getEnumName());
+                        mapResult.put("data", JSON.parseObject(String.valueOf(result.get(PreviewParams.RESULT.getEnumName())), HashMap.class));
+                        JsonUtil.writeResponseJson(response, SerializationUtils.toJsonString(mapResult));
+                    }
+                } else {
+                    mapResult.putAll(result);
+                    jsonResult = SerializationUtils.toJsonString(mapResult);
+                    JsonUtil.writeResponseJson(response, jsonResult);
+                }
+            } else {
+                in = (InputStream)result.get(PreviewParams.RESULT.getEnumName());
+                JsonUtil.writeResponseStream(response, in);
+            }
+        } else {
+            mapResult = (Map)result.get(PreviewParams.RESULT.getEnumName());
+            jsonResult = SerializationUtils.toJsonString(mapResult);
+            JsonUtil.writeResponseJson(response, jsonResult);
+        }
+
+    }
+
+    private Map<String, Object> getFileInfoFromCache(String path) throws IOException {
+        String param_path = path.substring(path.lastIndexOf(63) + 1, path.length());
+        String[] params = param_path.split("&");
+        HashMap<String, String> map = Maps.newHashMapWithExpectedSize(params.length);
+        HashMap<String, Object> result = Maps.newHashMapWithExpectedSize(2);
+        String[] var6 = params;
+        int var7 = params.length;
+
+        String id;
+        for(int var8 = 0; var8 < var7; ++var8) {
+            id = var6[var8];
+            String[] parameters = id.split("=");
+            map.put(parameters[0], parameters[1]);
+        }
+
+        TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+        if (!(cache instanceof TempFileCacheDownloadable)) {
+            result.put("error", cache.getClass().getName() + " must implements " + TempFileCacheDownloadable.class.getName());
+            return result;
+        } else {
+            TempFileCacheDownloadable downloader = (TempFileCacheDownloadable)cache;
+            String configKey = (String)map.get("configKey");
+            id = (String)map.get("id");
+            if (!StringUtils.isEmpty(configKey) && !StringUtils.isEmpty(id)) {
+                TempFileCacheDownloadable.Content content = downloader.get(configKey, id);
+                if (content == null) {
+                    result.put("error", ResManager.loadKDString("从临时文件缓存中获取文件失败,缓存文件可能失效,请重新上传", "AttachmentAction_10", "bos-webactions", new Object[0]));
+                    return result;
+                } else {
+                    result.put("fileName", content.getFilename());
+                    result.put("in", content.getInputStream());
+                    return result;
+                }
+            } else {
+                result.put("error", ResManager.loadKDString("请求路径中configKey或id不存在,访问临时文件缓存失败", "AttachmentAction_9", "bos-webactions", new Object[0]));
+                return result;
+            }
+        }
+    }
+
+    private boolean isForbidUpload(HttpServletRequest request, HttpServletResponse response, String fileName, BufferedInputStream buffInputStream) throws IOException {
+        List<String> controlWhiteTypeList = this.tryGetControlWhiteTypeList(request);
+        FileCheckResult fileCheckResult = FileChecker.isForbidUpload(buffInputStream, controlWhiteTypeList, fileName, false);
+        if (!fileCheckResult.isCheckReuslt()) {
+            this.writeForbidUploadErrorMessage(response, fileCheckResult);
+        }
+
+        return !fileCheckResult.isCheckReuslt();
+    }
+
+    private void writeForbidUploadErrorMessage(HttpServletResponse response, FileCheckResult fileCheckResult) {
+        Map<String, String> result = new HashMap();
+        result.put("status", "error");
+        String msg = fileCheckResult.getErrorMsg();
+        String jsonResult;
+        if (StringUtils.isBlank(msg)) {
+            jsonResult = fileCheckResult.getRealFileName();
+            String fileExt = jsonResult.substring(jsonResult.lastIndexOf(".") + 1);
+            msg = String.format(ResManager.loadKDString("系统不允许上传该文件类型:%s,可前往公共设置-系统参数-附件图片参数-附件上传限制-禁止上传附件类型或文件服务器的附件上传黑名单修改设置值。", "AttachmentAction_14", "bos-attachment", new Object[0]), fileExt);
+        }
+
+        result.put("description", msg);
+
+        try {
+            jsonResult = SerializationUtils.toJsonString(result);
+            ActionUtil.writeResponseJson(response, jsonResult);
+        } catch (IOException var7) {
+            log.error(var7);
+        }
+
+    }
+
+    private List<String> tryGetControlWhiteTypeList(HttpServletRequest request) {
+        String awet = request.getHeader("awet");
+        List<String> controlWhiteTypeList = new ArrayList(10);
+
+        try {
+            if (StringUtils.isNotBlank(awet)) {
+                awet = DecodeHandlerFactory.getDecodeHandler("base64").decode(awet);
+                String[] controlWhiteTypeArray = awet.split(",");
+                String[] var5 = controlWhiteTypeArray;
+                int var6 = controlWhiteTypeArray.length;
+
+                for(int var7 = 0; var7 < var6; ++var7) {
+                    String controlWhiteType = var5[var7];
+                    controlWhiteTypeList.add(controlWhiteType.replace(".", ""));
+                }
+            }
+
+            log.info("tryGetControlWhiteTypeList result is " + SerializationUtils.toJsonString(controlWhiteTypeList));
+            return controlWhiteTypeList;
+        } catch (Exception var9) {
+            log.error("tryGetControlWhiteTypeList --- failed to decrypt , source awet is " + awet);
+            return controlWhiteTypeList;
+        }
+    }
+
+    public static String checkSpeSymbol(String fileFullName, HttpServletResponse response) {
+        log.info("kd.bos.web.actions.AttachmentAction.checkSpeSymbol fileName ---- " + fileFullName);
+        if (!fileFullName.contains(".")) {
+            try {
+                writeErrorMessage(response, String.format(ResManager.loadKDString("%s文件无后缀名,不支持上传。", "AttachmentAction_15", "bos-attachment", new Object[0]), fileFullName));
+            } catch (IOException var3) {
+                log.error(var3.getMessage());
+            }
+        }
+
+        return AttachmentServiceHelper.checkSpeSymbol(fileFullName);
+    }
+
+    public static void writeErrorMessage(HttpServletResponse response, String errorMessage) throws IOException {
+        Map<String, String> result = new HashMap();
+        result.put("status", "error");
+        result.put("description", errorMessage);
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    public static void writeErrorMessage(HttpServletRequest request, HttpServletResponse response, String errorMessage) throws IOException {
+        Map<String, Object> result = new HashMap();
+        if (AttachmentUtil.isFrom3rdPreviewOrEdit(request)) {
+            response.setStatus(500);
+            result.put("result", 20501007);
+            result.put("msg", errorMessage);
+        }
+
+        result.put("status", "error");
+        result.put("description", errorMessage);
+        String jsonResult = SerializationUtils.toJsonString(result);
+        ActionUtil.writeResponseJson(response, jsonResult);
+    }
+
+    public boolean isIEBrowser(HttpServletRequest request) {
+        String userAgent = request.getHeader("User-Agent");
+        String[] var3 = IEBrowserSignals;
+        int var4 = var3.length;
+
+        for(int var5 = 0; var5 < var4; ++var5) {
+            String signal = var3[var5];
+            if (userAgent.contains(signal)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void setOperationContext(String appId, String formId, String formname, String method, String opKey) {
+        OperationContext oc = new OperationContext();
+        oc.setAppId(appId);
+        oc.setFormId(formId);
+        oc.setFormName(formname);
+        oc.setOpKey(opKey);
+        oc.setOpMethod(method);
+        OperationContext.set(oc);
+    }
+
+    private InputStream getInputStreamByUrl(String strUrl, boolean trustSSL) {
+        HttpURLConnection conn = null;
+        InputStream in = null;
+
+        try {
+            if (trustSSL || "true".equals(System.getProperty("https.ignore.ssl", "false"))) {
+                trustAllHttpsCertificates();
+                HttpsURLConnection.setDefaultHostnameVerifier(this.hv);
+            }
+
+            URL url = new URL(strUrl);
+            conn = (HttpURLConnection)url.openConnection();
+            conn.setRequestMethod("GET");
+            conn.setConnectTimeout(20000);
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            in = conn.getInputStream();
+            IOUtils.copy(in, output);
+            ByteArrayInputStream var7 = new ByteArrayInputStream(output.toByteArray());
+            return var7;
+        } catch (Exception var17) {
+            log.error("getInputStreamByUrl 异常,exception is ", var17);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+
+                if (conn != null) {
+                    conn.disconnect();
+                }
+            } catch (Exception var16) {
+            }
+
+        }
+
+        return null;
+    }
+
+    private static void trustAllHttpsCertificates() throws Exception {
+        TrustManager[] trustAllCerts = new TrustManager[1];
+        TrustManager tm = new miTM();
+        trustAllCerts[0] = tm;
+        SSLContext sc = SSLContext.getInstance("TLS");
+        sc.init((KeyManager[])null, trustAllCerts, (SecureRandom)null);
+        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+    }
+
+    public void fileInfoForWps(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        String fileInfo;
+        String fileId;
+        String isPreview;
+        if ("1".equals(request.getParameter("isPrivate"))) {
+            fileId = request.getHeader("X-Weboffice-File-Id");
+            isPreview = request.getParameter("isPreview");
+            log.info("fileInfoForWpsPrivate  ... get fileId is " + fileId + ",and get isPreview is:" + isPreview);
+            fileInfo = WpsPrivateHandle.getFileInfoById(fileId, isPreview);
+            log.info("fileInfoForWpsPrivate  ... get fileInfo is " + fileInfo);
+        } else {
+            fileId = request.getParameter("_w_id");
+            isPreview = request.getParameter("isPreview");
+            log.info("fileInfoForWps  ... get _w_id is " + fileId + ",and get isPreview is:" + isPreview);
+            fileInfo = WpsPreviewHandle.getFileInfoById(fileId, isPreview);
+            log.info("fileInfoForWps  ... get fileInfo is " + fileInfo);
+        }
+
+        ActionUtil.writeResponseJson(response, fileInfo);
+    }
+
+    public void onNotifyForWps(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        log.info("onNotifyForWps  ... get get get getRequestURL() is " + request.getRequestURL().toString());
+        log.info("onNotifyForWps  ... get get get paramMap is " + SerializationUtils.toJsonString(request.getParameterMap()));
+    }
+
+    private FileItem createFileItem(String fileName, String path, InputStream in, FileSource fileSource, String appId, String fId) {
+        FileItemExt fileItemExt = new FileItemExt(fileName, path, in);
+        fileItemExt.setSource(fileSource);
+        fileItemExt.setAppId(appId);
+        fileItemExt.setFId(fId);
+        return fileItemExt;
+    }
+
+    public void onlyOfficeEditCallback(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        log.info("enter onlyOffice edit callback");
+        ServletOutputStream outputStream = response.getOutputStream();
+        InputStream in = null;
+
+        try {
+            Scanner scanner = (new Scanner(request.getInputStream())).useDelimiter("\\A");
+            Throwable var6 = null;
+
+            try {
+                String body = scanner.hasNext() ? scanner.next() : "";
+                log.info("request param:" + body);
+                JSONObject jsonObject = JSON.parseObject(body);
+                if ("2".equals(jsonObject.getString("status")) || "6".equals(jsonObject.getString("status"))) {
+                    log.info("onlyOffice edit callback status is save");
+                    String fieldKey = request.getParameter("f");
+                    String key = jsonObject.getString("key");
+                    if (StringUtils.isBlank(fieldKey) || StringUtils.isBlank(key)) {
+                        outputStream.print("{\"error\":request param error}");
+                        log.error("request param error");
+                        return;
+                    }
+
+                    byte[] keyByte = key.getBytes();
+                    int sum = 0;
+
+                    for(int i = 0; i < keyByte.length; ++i) {
+                        sum += keyByte[i] * i;
+                    }
+
+                    if (!fieldKey.equals(String.valueOf(sum))) {
+                        outputStream.print("{\"error\":check sign error}");
+                        log.error("check sign error");
+                        return;
+                    }
+
+                    String downloadUri = jsonObject.getString("url");
+                    in = this.getInputStreamByUrl(downloadUri, true);
+                    TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
+                    String tempUrl = cache.saveAsFullUrl("edit.docx", in, 7200);
+                    String identifyType = request.getParameter("p");
+                    if (StringUtils.isNotBlank(identifyType)) {
+                        log.info("enter send ws message");
+                        String message = SerializationUtils.toJsonString(AttachmentUtil.assembleParam(tempUrl, key));
+                        log.info("identifyType:" + identifyType + ",send message:" + message);
+                        MsgSendFactory.getSender().send(identifyType, message);
+                    }
+                }
+
+                outputStream.print("{\"error\":0}");
+            } catch (Throwable var38) {
+                var6 = var38;
+                throw var38;
+            } finally {
+                if (scanner != null) {
+                    if (var6 != null) {
+                        try {
+                            scanner.close();
+                        } catch (Throwable var37) {
+                            var6.addSuppressed(var37);
+                        }
+                    } else {
+                        scanner.close();
+                    }
+                }
+
+            }
+        } catch (Exception var40) {
+            outputStream.print("{\"error\":1}");
+            log.error(var40);
+        } finally {
+            FileSecurityUtil.safeClose(outputStream);
+            FileSecurityUtil.safeClose(in);
+        }
+
+    }
+
+    static class miTM implements TrustManager, X509TrustManager {
+        miTM() {
+        }
+
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+
+        public boolean isServerTrusted(X509Certificate[] certs) {
+            return true;
+        }
+
+        public boolean isClientTrusted(X509Certificate[] certs) {
+            return true;
+        }
+
+        public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
+        }
+
+        public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
+        }
+    }
+}

+ 44 - 0
main/java/kd/cosmic/Application.java

@@ -0,0 +1,44 @@
+package kd.cosmic;
+
+import kd.cosmic.server.Launcher;
+
+/**
+ * 启动本地应用程序(微服务节点)
+ * @author turbo
+ */
+public class Application {
+
+
+    public static void main(String[] args) {
+        Launcher cosmic = new Launcher();
+
+//        cosmic.setClusterNumber("jingkong-uat");
+//        cosmic.setTenantNumber("jingkong-uat");
+//        cosmic.setServerIP("10.197.3.93");
+//        cosmic.setAppName("cosmic-Administrator-iIDfFTY4");
+////        cosmic.setWebPath("E:/kdcloud/jxjk/kd/cosmic-server/webapp");
+//        cosmic.setWebPath("E:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+//        cosmic.setWebAppPath("E:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+//        cosmic.setConfigUrl("10.197.3.95:2181?user=zookeeper&password=d@f*g:SGVsbG8==YBhrV1WOHk8yUia+fNkobDf4vWiMpipjZCleBi3aYPE8a2RwYXNzd29yZA==");
+
+//        cosmic.setClusterNumber("jingkonggroup");
+//        cosmic.setTenantNumber("jingkonggroup");
+//        cosmic.setServerIP("10.197.3.81");
+//        cosmic.setAppName("cosmic-Administrator-iIDfFTY4");
+//        cosmic.setWebPath("E:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+//        cosmic.setWebAppPath("E:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+//        cosmic.setConfigUrl("10.197.3.83:2181?user=zookeeper&password=d@f*g:SGVsbG8==kZfsOdMKSsE6zE2hzTORYDfK6dnH1fjBrg5F5TE8i7Eba2RwYXNzd29yZA==");
+
+        cosmic.setClusterNumber("jkjt-uat");
+        cosmic.setTenantNumber("jkjt-uat");
+        cosmic.setServerIP("10.150.0.78");
+        cosmic.setAppName("cosmic-Administrator-iIDfFTY4");
+        cosmic.setWebPath("D:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+        cosmic.setWebAppPath("D:/kdcloud/jxjk/kd/cosmic-server/static-file-service");
+        cosmic.setConfigUrl("10.150.0.78:2181?user=zookeeper&password=d@f*g:SGVsbG8==wkFltud6GUy8iOLXXj20jDg5MKyX0iCqjyLldmlFEhjta2RwYXNzd29yZA==");
+
+
+        cosmic.setStartWithQing(false);
+        cosmic.start();
+    }
+}

+ 34 - 0
main/java/kd/cosmic/jkjt/bos/orgview/orgf7treelist/AdminOrgF7ViewExtListFormPlugin.java

@@ -0,0 +1,34 @@
+package kd.cosmic.jkjt.bos.orgview.orgf7treelist;
+
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.form.field.FieldEdit;
+import kd.bos.list.ListShowParameter;
+import kd.bos.orgview.orgf7treelist.AdminOrgF7ViewListFormPlugin;
+import java.util.Map;
+
+/**
+ * @description 行政组织f7树形列表(nckd_bos_adminorg_tre_ext),放出【显示禁用】选择框
+ * @author wanghaiwu_kd
+ * @date 2025/01/24
+ */
+public class AdminOrgF7ViewExtListFormPlugin extends AdminOrgF7ViewListFormPlugin {
+    @Override
+    public void showDisabledOrg(ListShowParameter listShowParameter, String orgFuncId) {
+        super.showDisabledOrg(listShowParameter, orgFuncId);
+
+        String parentFormId = this.getView().getFormShowParameter().getParentFormId();
+
+        FieldEdit showDisableEdit = (FieldEdit)this.getControl("isshowdisabled");
+        if (showDisableEdit != null) {
+            Map<String, Object> customParams = listShowParameter.getCustomParams();
+            Object isShowDisabled = customParams.get("isShowDisabled");
+//            if (isShowDisabled != null && Boolean.parseBoolean(isShowDisabled.toString())) {
+                this.getView().setVisible(Boolean.TRUE, new String[]{"isshowdisabled"});
+                if ("01".equals(orgFuncId)) {
+//                    showDisableEdit.setCaption(new LocaleString(ResManager.loadKDString("显示封存", "OrgViewTreeListPlugin_7", "bos-org-formplugin", new Object[0])));
+                }
+//            }
+        }
+    }
+}

+ 26 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/CustomApiResultForMy.java

@@ -0,0 +1,26 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import kd.bos.openapi.common.result.CustomApiResult;
+
+/**
+ * @author turborao
+ */
+public class CustomApiResultForMy<T> extends CustomApiResult {
+
+
+    public static <T> CustomApiResultForMy<T> success2(T data) {
+        CustomApiResultForMy<T> result = new CustomApiResultForMy();
+        result.setStatus(true);
+        result.setData(data);
+        return result;
+    }
+
+    public static <T> CustomApiResultForMy<T> fail(T data) {
+        CustomApiResultForMy<T> result = new CustomApiResultForMy();
+        result.setData(data);
+        result.setStatus(false);
+        result.setErrorCode("500");
+        result.setMessage("失败");
+        return result;
+    }
+}

+ 357 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SupplierSynApi2Plugin.java

@@ -0,0 +1,357 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.*;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import org.springframework.validation.annotation.Validated;
+import kd.bos.context.RequestContext;
+
+import javax.validation.Valid;
+import java.util.*;
+
+
+/**
+ * @author turborao
+ * 供应商同步类,处理供应商同步信息,数据来源于金控CRM系统
+ * 管控策略为 自由分配, 全局共享不能有个性化数据
+ */
+@ApiController(value = "jkjt_basedata", desc = "供应商信息同步API")
+@ApiMapping("/supplier")
+public class SupplierSynApi2Plugin {
+    private static final Log logger = LogFactory.getLog(SupplierSynApi2Plugin.class);
+    protected static final String ENTITY_NAME = "bd_supplier";
+    /**
+     * 默认集团组织创建
+     * */
+    protected static final Long DEFAULT_ORG_ID = 100000L;
+    /**
+     * 默认分类
+     * */
+    protected static final Long DEFAULT_SUPPLIE_RGROUP = 1L;
+
+    @Validated
+    @ApiPostMapping("/sync")
+    public CustomApiResultForMy<@ApiResponseBody("响应") SupplierSynReturnModel> supplierSynCusApi(
+            @Valid @ApiParam(value = "组织编码") String org_number
+            ,@Valid @ApiParam(value = "供应商编码") String supp_number
+            ,@Valid @ApiParam(value = "供应商名称") String supp_name
+            ,@Valid @ApiParam(value = "供应商类型") String supp_type
+            ,@Valid @ApiParam(value = "供应商分类") String supp_group
+            ,@Valid @ApiParam(value = "社会信用代码") String society_credit_code
+            ,@Valid @ApiParam(value = "身份证号") String supp_card
+    ) {
+
+
+        DynamicObject supplier = null;
+        Long supplierid = 0L;
+        Date curDate = new Date();
+
+        System.out.println("SupplierSynApi2Plugin:" + "-" + org_number + "-" + supp_number + "-" + supp_name + "-" + supp_type + "-" + supp_group);
+        logger.info("SupplierSynApi2Plugin:" + "-" + org_number + "-" + supp_number + "-" + supp_name + "-" + supp_type);
+
+        SupplierSynReturnModel supplierModel = new SupplierSynReturnModel();
+        supplierModel.setSupplierNum(supp_number);
+        supplierModel.setSupplierName(supp_name);
+
+        /**
+         1、区分个人与企业
+         2、判断供应商是否存在,企业以统一信用码、个人以身份证为唯一判断
+         3、处理状态(暂时不处理)
+         4、处理分类,分为外部、内部
+         5、处理组织对象
+         6、处理行政区划
+         7、处理自由分配组织
+         */
+
+        /**
+         * 默认在集团下创建
+         * 创建组织查询
+         */
+        DynamicObject defaultOrg = BusinessDataServiceHelper.loadSingle(DEFAULT_ORG_ID, "bos_org");
+        /**
+         * 获取分配组织
+         */
+        if (org_number == null || "".equals(org_number)) {
+            supplierModel.setCode("500");
+            supplierModel.setMessage("组织编码不能为空!");
+            return CustomApiResultForMy.fail(supplierModel);
+        }
+        QFilter filterForOrg = new QFilter("number", QCP.equals, org_number);
+        QFilter[] filtersForOrg = new QFilter[]{filterForOrg};
+        DynamicObject[] assignOrgColl = BusinessDataServiceHelper.load("bos_org", "id,number,name", filtersForOrg);
+        Long assignOrgId = 0L;
+        String assignOrgName = "";
+        if (assignOrgColl.length > 0) {
+            assignOrgId = assignOrgColl[0].getLong("id");
+            assignOrgName = assignOrgColl[0].getString("name");
+        }else{
+            supplierModel.setCode("500");
+            supplierModel.setMessage("组织编码不存在!");
+            return CustomApiResultForMy.fail(supplierModel);
+        }
+
+        boolean isUpdate = false;
+        /**
+         * 1、法人企业
+         * 2、非法人企业
+         * 3、个人
+         */
+        if (supp_type == null || "".equals(supp_type)) {
+            supplierModel.setCode("500");
+            supplierModel.setMessage("供应商类型不能为空!");
+            return CustomApiResultForMy.fail(supplierModel);
+        }
+        if ("3".equals(supp_type)) {
+            /**
+             * 个人供应商处理   通过集团查询供应商数据是否存在
+             * */
+            if (supp_card == null || "".equals(supp_card)) {
+                supplierModel.setCode("500");
+                supplierModel.setMessage("供应商为个人时,身份证号不能为空!");
+                return CustomApiResultForMy.fail(supplierModel);
+            } else {
+                QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+                QFilter filter2 = new QFilter("idno", QCP.equals, supp_card);
+                QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+                DynamicObject[] supplierCol = BusinessDataServiceHelper.load(ENTITY_NAME, "id,number,name,status", filters);
+                if (supplierCol.length <= 0) {
+                    supplier = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME);
+                    supplier.set("status", "A");
+                } else {
+                    supplierid = supplierCol[0].getLong("id");
+                    supplier = BusinessDataServiceHelper.loadSingle(supplierid, ENTITY_NAME);
+                    isUpdate = true;
+                }
+                supplier.set("idno", supp_card);
+                /**
+                 * 法人企业	1
+                 * 非法人企业	2
+                 * 非企业单位	3
+                 * 个人	    4
+                 * 个体户	5
+                 * */
+                supplier.set("type", 4);
+            }
+            /**
+             * 企业供应商处理   通过集团查询供应商数据是否存在
+             * */
+        } else {
+            if (society_credit_code == null || "".equals(society_credit_code)) {
+                supplierModel.setCode("500");
+                supplierModel.setMessage("供应商为企业时,社会信用代码不能为空!");
+                return CustomApiResultForMy.fail(supplierModel);
+            } else {
+                QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+                QFilter filter2 = new QFilter("societycreditcode", QCP.equals, society_credit_code);
+                QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+                DynamicObject[] supplierCol = BusinessDataServiceHelper.load(ENTITY_NAME, "id,number,name,status", filters);
+                if (supplierCol.length <= 0) {
+                    supplier = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME);
+                    supplier.set("status", "A");
+                } else {
+                    supplierid = supplierCol[0].getLong("id");
+                    supplier = BusinessDataServiceHelper.loadSingle(supplierid, ENTITY_NAME);
+                    isUpdate = true;
+                }
+                supplier.set("societycreditcode", society_credit_code);
+                /**
+                 * 法人企业	1
+                 * 非法人企业	2
+                 * 非企业单位	3
+                 * 个人	    4
+                 * 个体户	5
+                 * */
+                supplier.set("type", 1);
+            }
+        }
+
+        supplier.set("number", supp_number);
+        supplier.set("name", supp_name);
+
+        /**
+         * 管控策略
+         * 1 逐级分配
+         * 2 自由分配
+         * 5 全局共享
+         * 6 管控范围内共享
+         * 7 私有
+         * */
+        supplier.set("ctrlstrategy", 2);
+        /**
+         * 使用状态  0 禁用 1 可用
+         * */
+        supplier.set("enable", "1");
+        /**
+         * 业务职能
+         * 采购	1
+         * 结算	2
+         * 收款	3
+         * 供货	4
+         * */
+        supplier.set("bizfunction", ",1,2,4,");
+        /**
+         * 供应商状态
+         * QZGYS	潜在
+         * REGGYS	注册
+         * TCGYS	退出
+         * YCGYS	异常
+         * ZCGYS	正常
+         * */
+        QFilter filterForStatus = new QFilter("number", QCP.equals, "ZCGYS");
+        QFilter[] filtersForStatus = new QFilter[]{filterForStatus};
+        DynamicObject[] supplierstatusColl = BusinessDataServiceHelper.load("bd_supplierstatus", "id,number,name", filtersForStatus);
+        supplier.set("supplier_status", supplierstatusColl[0]);
+
+        RequestContext rc = RequestContext.get();
+        DynamicObject user = BusinessDataServiceHelper.loadSingle(rc.getCurrUserId(), "bos_user");
+        supplier.set("creator", user);
+        supplier.set("createorg", defaultOrg);
+        supplier.set("org", defaultOrg);
+        supplier.set("createtime", curDate);
+
+        /**
+         * 默认分类(外部客户),通过名称找出是否是内部客户
+         */
+        DynamicObject suppliergroup = BusinessDataServiceHelper.loadSingle(DEFAULT_SUPPLIE_RGROUP, "bd_suppliergroup");
+        supplier.set("group", suppliergroup);
+        /**
+         * 子分录--默认分类(支持多分类)
+         */
+        DynamicObjectCollection groupstandardCol = supplier.getDynamicObjectCollection("entry_groupstandard");
+        DynamicObjectType billtype = groupstandardCol.getDynamicObjectType();
+        DynamicObject groupdetail = new DynamicObject(billtype);
+        groupdetail.set("standardid", suppliergroup.getDynamicObject("standard"));
+        groupdetail.set("groupid", suppliergroup);
+        groupstandardCol.add(groupdetail);
+        supplier.set("entry_groupstandard", groupstandardCol);
+
+
+        /**
+         * 判断是否更新与新增,通过不同方法处理,保存操作会变更状态为保存
+         */
+        if(isUpdate) {
+            SaveServiceHelper.update(new DynamicObject[]{supplier});
+            supplierid = supplier.getLong("id");
+        }else {
+            /**
+             * 新增保存供应商对象
+             */
+            OperationResult result = SaveServiceHelper.saveOperate(ENTITY_NAME, new DynamicObject[]{supplier}, OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (result.getSuccessPkIds().size() <= 0) {
+                for (int i = 0; i < result.getAllErrorOrValidateInfo().size(); i++) {
+                    String message = result.getAllErrorOrValidateInfo().get(i).getMessage();
+                    err.append("/").append(message);
+                }
+                supplierModel.setCode("500");
+                supplierModel.setMessage(err.toString());
+                return CustomApiResultForMy.fail(supplierModel);
+            } else {
+                supplierid = (Long) result.getSuccessPkIds().get(0);
+            }
+            DynamicObject supplierObj = BusinessDataServiceHelper.loadSingle(supplierid, ENTITY_NAME);
+            OperationServiceHelper.executeOperate("submit", ENTITY_NAME, new DynamicObject[]{supplierObj}, OperateOption.create());
+            OperationServiceHelper.executeOperate("audit", ENTITY_NAME, new DynamicObject[]{supplierObj}, OperateOption.create());
+        }
+
+        StringBuilder message = new StringBuilder();
+        message.append("1、更新成功");
+
+        String assignResult = assignOrgForSupplier(supplierid, assignOrgId);
+        if (assignResult.isEmpty()){
+            message.append(" 2、分配成功");
+            supplierModel.setSupplierId(supplierid.toString());
+            supplierModel.setCode("200");
+            supplierModel.setMessage(message.toString());
+        }else{
+            message.append(" 2、分配失败,");
+            message.append("组织:").append(assignOrgName);
+            message.append(assignResult);
+            supplierModel.setSupplierId(supplierid.toString());
+            supplierModel.setCode("500");
+            supplierModel.setMessage(message.toString());
+        }
+        return CustomApiResultForMy.success2(supplierModel);
+    }
+
+    /**
+     * 自动分配
+     * 要判断下分配组织 是不是CU,CU才能被分配,
+     * @return 检查失败的待分配基础资料及组织信息map, key是基础资料ID,value是目标组织ID集合
+     */
+    public String  assignOrgForSupplier(Long supplierId,Long assignOrgId) {
+
+        String result = "";
+
+        Set<Long> orgIds = new HashSet();
+        orgIds.add(assignOrgId);
+
+        ArrayList<Long> dataIds = new ArrayList();
+        dataIds.add(supplierId);
+        /**
+         *描述:获取分配目标组织。
+         * 1、按当前基础数据的创建组织查找基础数据对应的基础数据管控策略,获取相关参数;
+         * 2、当控制策略=按管控单元逐级分配时,从基础数据的管控视图中获取当前组织的直接下级控制单元;
+         * 当控制策略=按组织逐级分配时,从基础数据的管控视图中获取当前组织的直接下级组织;
+         * 当控制策略=按管控单元自由分配时,从基础数据的管控视图中获取当前组织以外的所有控制单元;
+         * 当控制策略=按组织自由分配时,从基础数据的管控视图中获取当前组织以外的所有组织;
+         * 3、根据组织在当前基础数据的使用关系表中获取已分配组织;
+         * 4、将第2步的数据排除第3步的数据得到待分配组织;
+         *
+         * @param srcOrgID 分配源组织
+         * @param ctrlType 控制类型
+         * @param orgViewID 组织视图ID
+         * @return List 分配目标组织
+         */
+        List<Long> assignOrgList = BaseDataServiceHelper.getAssignDesOrgs(DEFAULT_ORG_ID,"2",16L);
+
+
+        boolean isExist = false;
+        for(Long orgId :  assignOrgList) {
+            if (orgId.equals(assignOrgId)) {
+                isExist = true;
+                /**
+                 * 描述:获取分配目标组织。
+                 * 1、按当前基础数据的创建组织查找基础数据对应的基础数据管控策略,获取相关参数;
+                 * 2、当控制策略=按管控单元逐级分配时,从基础数据的管控视图中获取当前组织的直接下级控制单元;
+                 * 当控制策略=按组织逐级分配时,从基础数据的管控视图中获取当前组织的直接下级组织;
+                 * 当控制策略=按管控单元自由分配时,从基础数据的管控视图中获取当前组织以外的所有控制单元;
+                 * 当控制策略=按组织自由分配时,从基础数据的管控视图中获取当前组织以外的所有组织;
+                 * 3、根据组织在当前基础数据的使用关系表中获取已分配组织;
+                 * 4、将第2步的数据排除第3步的数据得到待分配组织;
+                 *
+                 * @param srcOrgID 分配源组织
+                 * @param ctrlType 控制类型
+                 * @param orgViewID 组织视图ID
+                 * @return List 分配目标组织
+                 */
+                Map<Long, Map> resultValue = BaseDataServiceHelper.batchAssignWithDetail(ENTITY_NAME, DEFAULT_ORG_ID, dataIds, new ArrayList(orgIds));
+                if (resultValue.isEmpty()){
+                    result= "";
+                }else{
+                    result = "组织分配错误";
+                }
+                return result;
+            }
+        }
+        if(!isExist) {
+            result = ",不能分配";
+        }
+        return result;
+
+    }
+
+}

+ 73 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SupplierSynReturnModel.java

@@ -0,0 +1,73 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import kd.bos.openapi.common.custom.annotation.ApiModel;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+
+import java.io.Serializable;
+
+
+/**
+ * 供应商同步 自定义API 返回参数 class
+ *
+ * @author turborao
+ * @date 2023/2/13
+ */
+@ApiModel
+public class SupplierSynReturnModel implements Serializable {
+
+    @ApiParam(value = "供应商ID", position = 1)
+    String supplierId;
+
+    @ApiParam(value = "供应商编码", position = 2)
+    String supplierNum;
+
+    @ApiParam(value = "供应商名称", position = 3)
+    String supplierName;
+
+    @ApiParam(value = "错误代码", position = 4)
+    String code;
+
+    @ApiParam(value = "错误信息", position = 5)
+    String message;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getSupplierId() {
+        return supplierId;
+    }
+
+    public void setSupplierId(String supplierId) {
+        this.supplierId = supplierId;
+    }
+
+    public String getSupplierNum() {
+        return supplierNum;
+    }
+
+    public void setSupplierNum(String supplierNum) {
+        this.supplierNum = supplierNum;
+    }
+
+    public String getSupplierName() {
+        return supplierName;
+    }
+
+    public void setSupplierName(String supplierName) {
+        this.supplierName = supplierName;
+    }
+
+}

+ 342 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerApi2Plugin.java

@@ -0,0 +1,342 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+
+import javax.validation.Valid;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_jd
+ * @date 2023/02/15
+ * @description
+ *          1、区分个人与企业
+ *          2、判断供应商是否存在,企业以统一信用码、个人以身份证为唯一判断
+ *          3、处理状态(暂时不处理)
+ *          4、处理分类,分为外部、内部
+ *          5、处理组织对象
+ *          6、处理行政区划
+ *          7、处理自由分配组织
+ */
+@ApiController(
+        desc = "同步客户信息OpenAPI",
+        value = "jkjt_basedata"
+)
+public class SynCustomerApi2Plugin implements Serializable {
+    private static final long serialVersionUID = -2911637392811406861L;
+    private static final Log logger = LogFactory.getLog(SynCustomerApi2Plugin.class);
+    private static final String ENTITY_NAME_CUSTOMER = "bd_customer";
+    protected static final Long DEFAULT_ORG_ID = 100000L;
+    protected static final Long DEFALUT_CUSTOMER_GROUP_ID = 1L;
+
+    @ApiPostMapping("synCustomer")
+    public CustomApiResult<JSONObject> synCustomer(
+            @Valid @ApiParam(value = "组织编码") String org_number
+            , @Valid @ApiParam(value = "客户编码") String cus_number
+            , @Valid @ApiParam(value = "客户名称") String cus_name
+            , @Valid @ApiParam(value = "客户类型") String cus_type
+            , @Valid @ApiParam(value = "客户分类") String cus_group
+            , @Valid @ApiParam(value = "社会信用代码") String society_credit_code
+            , @Valid @ApiParam(value = "身份证号") String cus_card
+            , @Valid @ApiParam(value = "是否关联方") String is_related_jxjk) {
+
+        String logInfo = "";
+        DynamicObject customer = null;
+        Long customerId = 0L;
+        Date curDate = new Date();
+        boolean isUpdate = false;
+
+        logInfo = "SynCustomerApi2Plugin:" + "-" + org_number + "-" + cus_number + "-" + cus_name + "-" + cus_type + "-" + cus_group;
+        logger.info(logInfo);
+
+        if (StringUtils.isEmpty(org_number)) {
+            return returnResult("500", "组织编码不能为空!");
+        }
+
+        //默认创建组织
+        DynamicObject defaultOrg = BusinessDataServiceHelper.loadSingle(DEFAULT_ORG_ID, "bos_org");
+
+        QFilter filterForOrg = new QFilter("number", QCP.equals, org_number);
+        QFilter[] filtersForOrg = new QFilter[]{filterForOrg};
+        DynamicObject assignOrg = BusinessDataServiceHelper.loadSingleFromCache("bos_org", "id,number,name", filtersForOrg);
+
+        Long assignOrgId = 0L;
+        String assignOrgName = "";
+
+        if (assignOrg == null) {
+            return returnResult("500", "业务单元(" + org_number +")在星瀚系统不存在!");
+        }
+
+        assignOrgId = assignOrg.getLong("id");
+        assignOrgName = assignOrg.getString("name");
+
+        if(!assignOrgId.equals(DEFAULT_ORG_ID)) {
+            List<Long> assignOrgList = BaseDataServiceHelper.getAssignDesOrgs(DEFAULT_ORG_ID,"2",16L);
+            if (!assignOrgList.contains(assignOrgId)) {
+                return returnResult("500", "新增失败!组织(" + assignOrgName + ")不是CU,不能新增");
+            }
+        }
+
+        //1、法人企业;2、非法人企业;3、个人
+        if (StringUtils.isEmpty(cus_type)) {
+            return returnResult("500", "客户类型不能为空!");
+        }
+
+        if("4".equals(cus_type)){
+            QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+            QFilter filter2 = null;
+            if (StringUtils.isNotEmpty(society_credit_code)) {
+                filter2 = new QFilter("societycreditcode", QCP.equals, society_credit_code);
+            } else if (StringUtils.isNotEmpty(cus_card)) {
+                filter2 = new QFilter("idno", QCP.equals, cus_card);
+            } else {
+                filter2 = new QFilter("number", QCP.equals, cus_number);
+            }
+
+            QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+            customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, filters);
+            if (customer == null) {
+                customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                customer.set("status", "A");
+            } else {
+                customerId = customer.getLong("id");
+                isUpdate = true;
+            }
+
+            if (StringUtils.isNotEmpty(society_credit_code)) {
+                customer.set("societycreditcode", society_credit_code);
+                customer.set("type", 1);
+            } else if (StringUtils.isNotEmpty(cus_card)) {
+                customer.set("idno", cus_card);
+                customer.set("type", 4);
+            } else {
+                if(cus_name.length() > 5){
+                    customer.set("type", 1);
+                } else {
+                    customer.set("type", 4);
+                }
+                customer.set("societycreditcode", cus_number);
+                customer.set("idno", cus_number);
+            }
+        } else if ("3".equals(cus_type)) {
+            if (StringUtils.isEmpty(cus_card)) {
+                return returnResult("500", "客户为个人时,身份证号不能为空!");
+            }
+
+            QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+            QFilter filter2 = new QFilter("idno", QCP.equals, cus_card);
+            QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+            customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, filters);
+            if (customer == null) {
+                customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                customer.set("status", "A");
+            } else {
+                customerId = customer.getLong("id");
+                isUpdate = true;
+            }
+            customer.set("idno", cus_card);
+
+            //1、法人企业;2、非法人企业;3、非企业单位;4、个人;5、个体户
+            customer.set("type", 4);
+        } else {
+            if (society_credit_code == null || "".equals(society_credit_code)) {
+                return returnResult("500", "客户为企业时,社会信用代码不能为空!");
+            } else {
+                QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+                QFilter filter2 = new QFilter("societycreditcode", QCP.equals, society_credit_code);
+                QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+                customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, filters);
+                if (customer == null) {
+                    customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                    customer.set("status", "A");
+                } else {
+                    isUpdate = true;
+                }
+                customer.set("societycreditcode", society_credit_code);
+
+                //1、法人企业;2、非法人企业;3、非企业单位;4、个人;5、个体户
+                customer.set("type", cus_type);
+            }
+        }
+
+        customer.set("number", cus_number);
+        customer.set("name", cus_name);
+
+//        String isRelated = "1".equals(is_related_jxjk) ? "1" : "0";
+        customer.set("nckd_checkboxfield", is_related_jxjk);
+
+        //管控策略:1、逐级分配;2、自由分配;5、全局共享;6、管控范围内共享;7、私有
+        customer.set("ctrlstrategy", 2);
+
+        //使用状态:0、禁用;1、可用
+        customer.set("enable", "1");
+
+        //业务职能:1、销售;2、结算;3、付款;4、收货
+        customer.set("bizfunction", ",1,2,3,");
+
+        //客户状态:1、潜在 QZKH;2、正常 ZSKH
+        QFilter filterForStatus = new QFilter("number", QCP.equals, "ZSKH");
+        QFilter[] filtersForStatus = new QFilter[]{filterForStatus};
+        DynamicObject customerstatus = BusinessDataServiceHelper.loadSingleFromCache("bd_customerstatus", "id,number,name", filtersForStatus);
+        if(customerstatus != null) {
+            customer.set("customer_status", customerstatus.getString("name"));
+        }
+
+        RequestContext rc = RequestContext.get();
+        DynamicObject user = BusinessDataServiceHelper.loadSingle(rc.getCurrUserId(), "bos_user");
+        customer.set("creator", user);
+        customer.set("createorg", defaultOrg);
+        customer.set("org", defaultOrg);
+        customer.set("createtime", curDate);
+
+        //分类:默认为外部客户,如果通过名称可以找到内部业务单元,则分类设置为内部客户,并设置内部业务单元
+        QFilter filterForNumber = new QFilter("number", QCP.equals, "PARAM_0001");
+        DynamicObject cusParam = BusinessDataServiceHelper.loadSingle("nckd_commonparams", new QFilter[]{filterForNumber});
+        DynamicObject defaultWBGroup = cusParam.getDynamicObject("nckd_customergroup");
+        DynamicObject defaultNBGroup = cusParam.getDynamicObject("nckd_innercusgroup");
+        Object groupId = DEFALUT_CUSTOMER_GROUP_ID;
+        if(defaultWBGroup != null){
+            groupId = defaultWBGroup.getPkValue();
+        }
+
+        if(defaultNBGroup != null){
+            QFilter filterName = new QFilter("name", QCP.equals, cus_name);
+            QFilter filteraType = new QFilter("fisadministrative", QCP.equals, "1");
+            QFilter[] filters = new QFilter[]{filterName, filteraType};
+            DynamicObject nbOrg = BusinessDataServiceHelper.loadSingleFromCache("bos_org", "id,number,name", filters);
+
+            if(nbOrg != null){
+                groupId = defaultNBGroup.getPkValue();
+                //内部业务单元
+                //取消内部业务单元的赋值
+//                customer.set("internal_company", nbOrg);
+            }
+        }
+
+        DynamicObject customergroup = BusinessDataServiceHelper.loadSingle(groupId, "bd_customergroup");
+        customer.set("group", customergroup);
+
+        //子分录--默认分类(支持多分类)
+        DynamicObjectCollection groupstandardCol = customer.getDynamicObjectCollection("entry_groupstandard");
+        DynamicObjectType billtype = groupstandardCol.getDynamicObjectType();
+        DynamicObject groupdetail = new DynamicObject(billtype);
+        groupdetail.set("standardid", customergroup.getDynamicObject("standard"));
+        groupdetail.set("groupid", customergroup);
+        groupstandardCol.add(groupdetail);
+        customer.set("entry_groupstandard", groupstandardCol);
+
+        //判断是否更新与新增,通过不同方法处理,保存操作会变更状态为保存
+        if(isUpdate) {
+            SaveServiceHelper.update(new DynamicObject[]{customer});
+            customerId = customer.getLong("id");
+        }else {
+            //新增保存供应商对象
+            OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
+                    err.append("/").append(message);
+                }
+
+                return returnResult("500", err.toString());
+            } else {
+                customerId = (Long) resultSave.getSuccessPkIds().get(0);
+            }
+            DynamicObject customerObj = BusinessDataServiceHelper.loadSingle(customerId, ENTITY_NAME_CUSTOMER);
+            OperationServiceHelper.executeOperate("submit", ENTITY_NAME_CUSTOMER, new DynamicObject[]{customerObj}, OperateOption.create());
+            OperationServiceHelper.executeOperate("audit", ENTITY_NAME_CUSTOMER, new DynamicObject[]{customerObj}, OperateOption.create());
+        }
+
+        StringBuilder message = new StringBuilder();
+        message.append("1、更新成功");
+
+        String assignResult = assignOrgForCustomer(customerId, assignOrgId);
+        String code = "";
+        if (assignResult.isEmpty()){
+            code = "200";
+            message.append(" 2、分配成功");
+        }else{
+            code = "200";
+            message.append(" 2、分配失败,");
+            message.append("组织:").append(assignOrgName);
+            message.append(assignResult);
+        }
+
+        return returnResult(code, message.toString());
+    }
+
+    /**
+     * 客户分配组织
+     * @param customerId
+     * @param assignOrgId
+     * @return
+     */
+    public String assignOrgForCustomer(Long customerId,Long assignOrgId) {
+        String result = "";
+
+        Set<Long> orgIds = new HashSet();
+        orgIds.add(assignOrgId);
+
+        ArrayList<Long> dataIds = new ArrayList();
+        dataIds.add(customerId);
+
+        if(assignOrgId.equals(DEFAULT_ORG_ID)) {
+            return "";
+        }
+
+        List<Long> assignOrgList = BaseDataServiceHelper.getAssignDesOrgs(DEFAULT_ORG_ID,"2",16L);
+
+        boolean isExist = false;
+        if (assignOrgList.contains(assignOrgId)) {
+            isExist = true;
+            Map<Long, Map> resultValue = BaseDataServiceHelper.batchAssignWithDetail(ENTITY_NAME_CUSTOMER, DEFAULT_ORG_ID, dataIds, new ArrayList(orgIds));
+            if (resultValue.isEmpty()){
+                result= "";
+            }else{
+                result = "组织分配错误";
+            }
+            return result;
+        }
+
+        if(!isExist) {
+            result = "不是CU,不能分配";
+        }
+        return result;
+    }
+
+    /**
+     * 自定义返回data对象
+     * @param code
+     * @param message
+     * @return
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+        CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
+
+        return result;
+    }
+}

+ 197 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerForBankApi2Plugin.java

@@ -0,0 +1,197 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+
+
+import javax.validation.Valid;
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * @author turborao
+ * @date  2023/04/23/10:32 上午
+ * @description    客户银行信息,不是个性化数据
+ *          1、判断是否存在客户
+ *          2、判断银行账号是否存在
+ *          3、判断币别
+ *          4、判断开户行联行号
+ *          5、银行账号存在   更新
+ *          6、银行账号不存在  新增
+ *          7、保存客户对象
+ **/
+
+ @ApiController(
+     desc = "同步客户信息--银行信息 OpenAPI",
+     value = "jkjt_basedata"
+ )
+public class SynCustomerForBankApi2Plugin implements Serializable {
+    private static final long serialVersionUID = 1649494277596744511L;
+    private static final Log logger = LogFactory.getLog(SynCustomerForBankApi2Plugin.class);
+    protected static final Long DEFAULT_ORG_ID = 100000L;
+    private static final String ENTITY_NAME_CUSTOMER = "bd_customer";
+
+    @ApiPostMapping("synCustomerForBank")
+    public CustomApiResult<JSONObject> synCustomerForBank(
+            @Valid @ApiParam(value = "客户编码") String cus_number
+            , @Valid @ApiParam(value = "银行信息") List<JSONObject> entryInfos) {
+        String logInfo;
+        DynamicObject customer;
+
+        logInfo = "SynCustomerApi2Plugin:" + "-" + cus_number + "-" + entryInfos.toString();
+        logger.info(logInfo);
+
+        if (StringUtils.isEmpty(cus_number)) {
+            return returnResult("500", "客户编码不能为空!");
+        }
+        //判断客户
+        QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+        QFilter filter2 = new QFilter("number", QCP.equals, cus_number);
+        QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+        customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, filters);
+        if (customer == null) {
+            return returnResult("500", "客户不存在,请核对客户编码是否正确!");
+        }
+        String bankaccount = "";
+        String accountname = "";
+        String ibankaccount = "";
+        String bankunioncode = "";
+        String bankname = "";
+        String currency = "";
+        String isdefault = "";
+
+        //判断银行账号是否存在
+        DynamicObjectCollection bankEntry  =  customer.getDynamicObjectCollection("entry_bank");
+        DynamicObjectType type = bankEntry.getDynamicObjectType();
+
+        bankEntry.clear();
+
+        for(JSONObject entrybank : entryInfos){
+            bankaccount = entrybank.getString("bankaccount");
+            accountname = entrybank.getString("accountname");
+            ibankaccount = entrybank.getString("ibankaccount");
+            bankunioncode = entrybank.getString("bankunioncode");
+            bankname = entrybank.getString("bankname");
+            currency = entrybank.getString("currency");
+            isdefault = entrybank.getString("isdefault");
+
+            if (StringUtils.isEmpty(bankaccount)) {
+                return returnResult("500", "银行账号不能为空!");
+            }
+            if (StringUtils.isEmpty(accountname)) {
+                return returnResult("500", "账户名称不能为空!");
+            }
+            if (StringUtils.isEmpty(bankunioncode)) {
+                return returnResult("500", "开户行联行号不能为空!");
+            }
+            if (StringUtils.isEmpty(bankname)) {
+                return returnResult("500", "开户行名称不能为空!");
+            }
+            if (StringUtils.isEmpty(currency)) {
+                return returnResult("500", "币别编码不能为空!");
+            }
+            if (StringUtils.isEmpty(isdefault)) {
+                return returnResult("500", "是否默认不能为空!");
+            }
+
+            boolean isUpdate = false;
+
+            //币别
+            QFilter filtercurrency = new QFilter("number", QCP.equals, currency);
+            QFilter[] filtercurrencys = new QFilter[]{filtercurrency};
+            DynamicObject currencyEntity = BusinessDataServiceHelper.loadSingle("bd_currency", filtercurrencys);
+            if (currencyEntity == null) {
+                return returnResult("500", "币别不存在,请核对币别是否正确!");
+            }
+
+            //开户银行
+            QFilter filterbebank = new QFilter("union_number", QCP.equals, bankunioncode);
+            QFilter filterbebankname = new QFilter("name", QCP.equals, bankname);
+            QFilter[] filterbebanks = new QFilter[]{filterbebank, filterbebankname};
+            DynamicObject bebankEntity = BusinessDataServiceHelper.loadSingle("bd_bebank", filterbebanks);
+            if (bebankEntity == null) {
+                return returnResult("500", "开户银行不存在,请核对开户行联行号及名称是否正确!");
+            }
+
+//            for(DynamicObject bank : bankEntry){
+//                String fbankaccount =  bank.getString("bankaccount");
+//                if("1".equals(isdefault)){
+//                    bank.set("isdefault_bank", "0");
+//                }
+//                if(fbankaccount.equals(bankaccount)){
+//                    isUpdate = true;
+//                    bank.set("accountname",accountname);
+//                    bank.set("iban",ibankaccount);
+//                    bank.set("isdefault_bank", isdefault);
+//                    bank.set("bank",bebankEntity);
+//                    bank.set("currency",currencyEntity);
+//                }
+//            }
+//            if(!isUpdate){
+                DynamicObject bank = new DynamicObject(type);
+                bank.set("bankaccount",bankaccount);
+                bank.set("accountname",accountname);
+                bank.set("iban",ibankaccount);
+                bank.set("isdefault_bank", isdefault);
+                bank.set("bank",bebankEntity);
+                bank.set("currency",currencyEntity);
+
+                bankEntry.add(bank);
+//            }
+        }
+
+
+        customer.set("entry_bank",bankEntry);
+//        OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+//        SaveServiceHelper.update(new DynamicObject[]{customer});
+//        if(isUpdate) {
+//            SaveServiceHelper.update(new DynamicObject[]{customer});
+//        } else {
+            OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
+                    err.append("/").append(message);
+                }
+
+                return returnResult("500", err.toString());
+            }
+//        }
+
+        return returnResult("200", "银行账户保存成功!");
+    }
+
+
+    /**
+     * 自定义返回data对象
+     * @param code  编码
+     * @param message  错误信息
+     * @return   CustomApiResult  返回
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+
+        return CustomApiResult.success(reslutData);
+    }
+
+}

+ 172 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerForBankNewApi2Plugin.java

@@ -0,0 +1,172 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+
+import javax.validation.Valid;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date  2023/12/06
+ **/
+ @ApiController(
+     desc = "同步客户信息--银行信息New OpenAPI",
+     value = "jkjt_basedata"
+ )
+public class SynCustomerForBankNewApi2Plugin implements Serializable {
+    private static final long serialVersionUID = -6383907497494893819L;
+    private static final Log logger = LogFactory.getLog(SynCustomerForBankNewApi2Plugin.class);
+    protected static final Long DEFAULT_ORG_ID = 100000L;
+    private static final String ENTITY_NAME_CUSTOMER = "bd_customer";
+
+    @ApiPostMapping("synCustomerForBankNew")
+    public CustomApiResult<JSONObject> synCustomerForBankNew(
+            @Valid @ApiParam(value = "客户编码") String unCustCode
+            , @Valid @ApiParam(value = "银行信息") JSONArray entryInfos) {
+        String logInfo;
+        DynamicObject customer;
+
+        logInfo = "SynCustomerForBankNewApi2Plugin:" + "-" + unCustCode + "-" + entryInfos.toString();
+        logger.info(logInfo);
+
+        if (StringUtils.isEmpty(unCustCode)) {
+            return returnResult("500", "客户编码不能为空!");
+        }
+        //判断客户
+        QFilter filter1 = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+        QFilter filter2 = new QFilter("number", QCP.equals, unCustCode);
+        QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+        customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, filters);
+        if (customer == null) {
+            return returnResult("500", "客户不存在,请核对客户编码是否正确!");
+        }
+        String bankAccount = "";
+        String accountName = "";
+        String ibankAccount = "";
+        String bankUnionCode = "";
+        String bankName = "";
+        String currency = "";
+        String isDefault = "1";
+
+        //判断银行账号是否存在
+        DynamicObjectCollection bankEntry  =  customer.getDynamicObjectCollection("entry_bank");
+        DynamicObjectType type = bankEntry.getDynamicObjectType();
+
+        //先清除银行账号分录
+        bankEntry.clear();
+
+//        int index = 0;
+
+        for(int index = 0; index < entryInfos.size(); index++){
+            JSONObject entrybank = entryInfos.getJSONObject(index);
+            bankAccount = entrybank.getString("bankAccount");
+            accountName = entrybank.getString("accountName");
+            ibankAccount = entrybank.getString("ibankAccount");
+            bankUnionCode = entrybank.getString("bankUnionCode");
+            bankName = entrybank.getString("bankName");
+            currency = entrybank.getString("currency");
+//            isDefault = entrybank.getString("isDefault");
+
+            if (StringUtils.isEmpty(bankAccount)) {
+                return returnResult("500", "银行账号不能为空!");
+            }
+            if (StringUtils.isEmpty(accountName)) {
+                return returnResult("500", "账户名称不能为空!");
+            }
+            if (StringUtils.isEmpty(bankUnionCode)) {
+                return returnResult("500", "开户行联行号不能为空!");
+            }
+            if (StringUtils.isEmpty(bankName)) {
+                return returnResult("500", "开户行名称不能为空!");
+            }
+            if (StringUtils.isEmpty(currency)) {
+                return returnResult("500", "币别编码不能为空!");
+            }
+//            if (StringUtils.isEmpty(isDefault)) {
+//                return returnResult("500", "是否默认不能为空!");
+//            }
+
+            boolean isUpdate = false;
+
+            //币别
+            QFilter filtercurrency = new QFilter("number", QCP.equals, currency);
+            QFilter[] filtercurrencys = new QFilter[]{filtercurrency};
+            DynamicObject currencyEntity = BusinessDataServiceHelper.loadSingle("bd_currency", filtercurrencys);
+            if (currencyEntity == null) {
+                return returnResult("500", "币别不存在,请核对币别是否正确!");
+            }
+
+            //开户银行
+            QFilter filterbebank = new QFilter("union_number", QCP.equals, bankUnionCode);
+            QFilter filterbebankname = new QFilter("name", QCP.equals, bankName);
+            QFilter[] filterbebanks = new QFilter[]{filterbebank, filterbebankname};
+            DynamicObject bebankEntity = BusinessDataServiceHelper.loadSingle("bd_bebank", filterbebanks);
+            if (bebankEntity == null) {
+                return returnResult("500", "开户银行不存在,请核对开户行联行号及名称是否正确!");
+            }
+
+            DynamicObject bank = new DynamicObject(type);
+            bank.set("bankaccount",bankAccount);
+            bank.set("accountname",accountName);
+            bank.set("iban",ibankAccount);
+            bank.set("isdefault_bank", isDefault);
+            bank.set("bank",bebankEntity);
+            bank.set("currency",currencyEntity);
+
+            bankEntry.add(bank);
+
+            isDefault = "0";
+        }
+
+
+        customer.set("entry_bank",bankEntry);
+
+        OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+
+        StringBuilder err = new StringBuilder();
+        if (resultSave.getSuccessPkIds().size() <= 0) {
+            for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
+                String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
+                err.append("/").append(message);
+            }
+
+            return returnResult("500", err.toString());
+        }
+
+        return returnResult("200", "银行账户保存成功!");
+    }
+
+
+    /**
+     * 自定义返回data对象
+     * @param code  编码
+     * @param message  错误信息
+     * @return   CustomApiResult  返回
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+
+        return CustomApiResult.success(reslutData);
+    }
+}

+ 420 - 0
main/java/kd/cosmic/jkjt/common/basedata/api/SynCustomerNewApi2Plugin.java

@@ -0,0 +1,420 @@
+package kd.cosmic.jkjt.common.basedata.api;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.util.StringUtils;
+
+import javax.validation.Valid;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/11/29
+ */
+@ApiController(
+        desc = "同步客户信息接口New OpenAPI",
+        value = "jkjt_basedata"
+)
+public class SynCustomerNewApi2Plugin implements Serializable {
+    private static final long serialVersionUID = 8968109114813243351L;
+
+    private static final Log logger = LogFactory.getLog(SynCustomerNewApi2Plugin.class);
+    private static final String ENTITY_NAME_CUSTOMER = "bd_customer";
+    protected static final Long DEFAULT_ORG_ID = 100000L;
+    protected static final Long DEFALUT_CUSTOMER_GROUP_ID = 1L;
+
+    /**
+     *
+     * @param fmsOrgCode
+     * @param unCustCode
+     * @param custName
+     * @param custType 1:政府, 2:个人, 3:企业, 4:其他
+     * @param creditCode 当客户类型为:1政府:统一社会信用码,非必填;2个人:身份证号,必填;3企业:统一社会信用码,必填;4其他:统一社会信用码,非必填。
+     * @param isRelatedJxjk
+     * @return
+     */
+    @ApiPostMapping("synCustomerNew")
+    public CustomApiResult<JSONObject> synCustomerNew(
+            @Valid @ApiParam(value = "客户编码") String unCustCode
+            , @Valid @ApiParam(value = "客户名称") String custName
+            , @Valid @ApiParam(value = "客户类型") String custType
+            , @Valid @ApiParam(value = "行政组织PK") String orgId
+            , @Valid @ApiParam(value = "核算组织编码") String fmsOrgCode
+            , @Valid @ApiParam(value = "统一识别码") String creditCode
+            , @Valid @ApiParam(value = "是否关联方") String isRelatedJxjk) {
+
+        String logInfo = "";
+        DynamicObject customer = null;
+        Long customerId = 0L;
+        Date curDate = new Date();
+        boolean isUpdate = false;
+        String entityName = "";
+        String selectProperties = "";
+
+//        logInfo = "SynCustomerNewApi2Plugin:" + "-" + unCustCode + "-" + custName + "-" + custType + "-" + orgId;
+//        logger.info(logInfo);
+
+        if (StringUtils.isEmpty(fmsOrgCode) && StringUtils.isEmpty(orgId)) {
+            return returnResult("500", "行政组织PK和核算组织编码不能同时为空!");
+        }
+        boolean isAssignAll = false;
+        //传入核算组织为空时,找到行政组织委托的核算组织
+        if(StringUtils.isEmpty(fmsOrgCode)){
+            isAssignAll = true;
+
+            entityName = "bos_adminorg";
+            QFilter qFilter = new QFilter("nckd_oauthpk", QCP.equals, orgId);
+            DynamicObject dept = BusinessDataServiceHelper.loadSingle(entityName, qFilter.toArray());
+            if(dept == null){
+                return returnResult("500", "行政组织PK:!" + orgId + ", 在金蝶系统无对应的行政组织!");
+            }
+
+            entityName = "bos_org_orgrelation_dept";
+            qFilter = new QFilter("toorg", QCP.equals, dept.getPkValue());
+
+            DynamicObject relation = BusinessDataServiceHelper.loadSingle(entityName, qFilter.toArray());
+            if(relation == null){
+                return returnResult("500", "行政组织" + dept.getString("name") + "未维护委托业务单元!");
+            }
+            DynamicObject fmsOrg = relation.getDynamicObject("fromorg");
+            fmsOrgCode = fmsOrg.getString("number");
+            String fmsOrgName = fmsOrg.getString("name");
+
+            //如果是本部,则往上找一级
+            if(fmsOrgName.contains("本部")){
+                QFilter filterView = new QFilter("view.id", QCP.equals, 10L);
+                QFilter filteraOrg = new QFilter("org.id", QCP.in, fmsOrg.getLong("id"));
+                QFilter[] filters = new QFilter[]{filterView, filteraOrg};
+                DynamicObject orgStructure = BusinessDataServiceHelper.loadSingle("bos_org_structure", "id, parent.*", filters);
+                if(orgStructure != null) {
+                    fmsOrgCode = orgStructure.getDynamicObject("parent").getString("number");
+                }
+            }
+        }
+
+        //默认创建组织
+        DynamicObject defaultOrg = BusinessDataServiceHelper.loadSingle(DEFAULT_ORG_ID, "bos_org");
+
+        QFilter filterForOrg = new QFilter("number", QCP.equals, fmsOrgCode);
+        QFilter[] filtersForOrg = new QFilter[]{filterForOrg};
+        DynamicObject assignOrg = BusinessDataServiceHelper.loadSingleFromCache("bos_org", "id,number,name", filtersForOrg);
+
+        Long assignOrgId = 0L;
+        String assignOrgName = "";
+
+        if (assignOrg == null) {
+            return returnResult("500", "业务单元(" + fmsOrgCode +")在星瀚系统不存在!");
+        }
+
+        //被分配组织id
+        assignOrgId = assignOrg.getLong("id");
+        //被分配组织名称
+        assignOrgName = assignOrg.getString("name");
+
+        if(!assignOrgId.equals(DEFAULT_ORG_ID)) {
+            List<Long> assignOrgList = BaseDataServiceHelper.getAssignDesOrgs(DEFAULT_ORG_ID,"2",16L);
+            if (!assignOrgList.contains(assignOrgId)) {
+                return returnResult("500", "新增失败!组织(" + assignOrgName + ")不是CU,不能新增");
+            }
+        }
+
+        List<Long> ids = new ArrayList<>();
+        ids.add(assignOrgId);
+
+        List<Long> assignOrgIds = new ArrayList<>();
+        boolean isIncludeCurOrg = DEFAULT_ORG_ID.equals(assignOrgId) ? false : true;
+        if(isAssignAll){
+
+            assignOrgIds = OrgUnitServiceHelper.getAllSubordinateOrgs("10", ids, isIncludeCurOrg);
+        } else {
+            if(isIncludeCurOrg) {
+                assignOrgIds.add(assignOrgId);
+            }
+        }
+
+        String kdCusType = "";
+        String errorMsg = "";
+        List listCusType = Arrays.asList("1", "2", "3", "4");
+
+        //传入客户类型:1:政府, 2:个人, 3:企业, 4:其他
+        //金蝶客户类型:1:法人企业, 2:非法人企业, 3:非企业单位, 4:个人, 5:个体户
+        if (StringUtils.isEmpty(custType)) {
+            return returnResult("500", "客户类型不能为空!");
+        }
+
+        if(!listCusType.contains(custType)){
+            errorMsg = String.format("客户类型【s%】不正确,请传入正确的客户类型!", custType);
+            return returnResult("500", errorMsg);
+        }
+
+        //2:个人, 3:企业
+        if ("2".equals(custType) || "3".equals(custType)) {
+            String custTypeName = "3".equals(custType) ? "企业" : "个人";
+            String fieldName = "3".equals(custType) ? "社会信用代码" : "身份证号";
+            String field = "3".equals(custType) ? "societycreditcode" : "idno";
+            kdCusType = "3".equals(custType) ? "1" : "4";
+
+            if (StringUtils.isEmpty(creditCode)) {
+                errorMsg = String.format("客户为s%时,s%不能为空!", custTypeName, fieldName);
+                return returnResult("500", errorMsg);
+            }
+
+            QFilter qFilter = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+            qFilter.and(new QFilter(field, QCP.equals, creditCode));
+
+            customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, qFilter.toArray());
+            if (customer == null) {
+                customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                customer.set("status", "A");
+            } else {
+                customerId = customer.getLong("id");
+                isUpdate = true;
+            }
+            customer.set(field, creditCode);
+            customer.set("type", kdCusType);
+        }
+        //1:政府, 4:其他
+        else if("1".equals(custType) || "4".equals(custType)){
+            if (StringUtils.isNotEmpty(creditCode)) {
+                kdCusType = "1";
+
+                QFilter qFilter = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+                qFilter.and(new QFilter("societycreditcode", QCP.equals, creditCode));
+
+                customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, qFilter.toArray());
+                if (customer == null) {
+                    customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                    customer.set("status", "A");
+                } else {
+                    customerId = customer.getLong("id");
+                    isUpdate = true;
+                }
+                customer.set("societycreditcode", creditCode);
+                customer.set("type", kdCusType);
+            } else {
+                kdCusType = "2";
+
+                QFilter qFilter = new QFilter("createorg", QCP.equals, DEFAULT_ORG_ID);
+                qFilter.and(new QFilter("number", QCP.equals, unCustCode));
+
+                customer = BusinessDataServiceHelper.loadSingleFromCache(ENTITY_NAME_CUSTOMER, qFilter.toArray());
+                if (customer == null) {
+                    customer = BusinessDataServiceHelper.newDynamicObject(ENTITY_NAME_CUSTOMER);
+                    customer.set("status", "A");
+                } else {
+                    customerId = customer.getLong("id");
+                    isUpdate = true;
+                }
+                customer.set("societycreditcode", unCustCode);
+                customer.set("type", kdCusType);
+            }
+        }
+
+        customer.set("number", unCustCode);
+        customer.set("name", custName);
+
+//        String isRelated = "1".equals(isRelatedJxjk) ? "1" : "0";
+        customer.set("nckd_checkboxfield", isRelatedJxjk);
+
+        //管控策略:1、逐级分配;2、自由分配;5、全局共享;6、管控范围内共享;7、私有
+        customer.set("ctrlstrategy", 2);
+
+        //使用状态:0、禁用;1、可用
+        customer.set("enable", "1");
+
+        //业务职能:1、销售;2、结算;3、付款;4、收货
+        customer.set("bizfunction", ",1,2,3,");
+
+        //客户状态:1、潜在 QZKH;2、正常 ZSKH
+        QFilter filterForStatus = new QFilter("number", QCP.equals, "ZSKH");
+        QFilter[] filtersForStatus = new QFilter[]{filterForStatus};
+        DynamicObject customerstatus = BusinessDataServiceHelper.loadSingleFromCache("bd_customerstatus", "id,number,name", filtersForStatus);
+        if(customerstatus != null) {
+            customer.set("customer_status", customerstatus.getString("name"));
+        }
+
+        RequestContext rc = RequestContext.get();
+        DynamicObject user = BusinessDataServiceHelper.loadSingle(rc.getCurrUserId(), "bos_user");
+        customer.set("creator", user);
+        customer.set("createorg", defaultOrg);
+        customer.set("org", defaultOrg);
+        customer.set("createtime", curDate);
+
+        //分类:默认为外部客户,如果通过名称可以找到内部业务单元,则分类设置为内部客户,并设置内部业务单元
+        QFilter filterForNumber = new QFilter("number", QCP.equals, "PARAM_0001");
+        DynamicObject cusParam = BusinessDataServiceHelper.loadSingle("nckd_commonparams", new QFilter[]{filterForNumber});
+        DynamicObject defaultWBGroup = cusParam.getDynamicObject("nckd_customergroup");
+        DynamicObject defaultNBGroup = cusParam.getDynamicObject("nckd_innercusgroup");
+        Object groupId = DEFALUT_CUSTOMER_GROUP_ID;
+        if(defaultWBGroup != null){
+            groupId = defaultWBGroup.getPkValue();
+        }
+
+        if(defaultNBGroup != null){
+            QFilter filterName = new QFilter("name", QCP.equals, custName);
+            QFilter filteraType = new QFilter("fisadministrative", QCP.equals, "1");
+            QFilter[] filters = new QFilter[]{filterName, filteraType};
+            DynamicObject nbOrg = BusinessDataServiceHelper.loadSingleFromCache("bos_org", "id,number,name", filters);
+
+            if(nbOrg != null){
+                groupId = defaultNBGroup.getPkValue();
+                //内部业务单元
+                //取消内部业务单元的赋值
+//                customer.set("internal_company", nbOrg);
+            }
+        }
+
+        DynamicObject customergroup = BusinessDataServiceHelper.loadSingle(groupId, "bd_customergroup");
+        customer.set("group", customergroup);
+
+        //子分录--默认分类(支持多分类)
+        DynamicObjectCollection groupstandardCol = customer.getDynamicObjectCollection("entry_groupstandard");
+        DynamicObjectType billtype = groupstandardCol.getDynamicObjectType();
+        DynamicObject groupdetail = new DynamicObject(billtype);
+        groupdetail.set("standardid", customergroup.getDynamicObject("standard"));
+        groupdetail.set("groupid", customergroup);
+        groupstandardCol.add(groupdetail);
+        customer.set("entry_groupstandard", groupstandardCol);
+
+        //判断是否更新与新增,通过不同方法处理,保存操作会变更状态为保存
+        if(isUpdate) {
+//            SaveServiceHelper.update(new DynamicObject[]{customer});
+            OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
+                    err.append("/").append(message);
+                }
+
+                return returnResult("500", err.toString());
+            }
+            customerId = customer.getLong("id");
+        }else {
+            //新增保存供应商对象
+            OperationResult resultSave = SaveServiceHelper.saveOperate(ENTITY_NAME_CUSTOMER, new DynamicObject[]{customer}, OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
+                    err.append("/").append(message);
+                }
+
+                return returnResult("500", err.toString());
+            } else {
+                customerId = (Long) resultSave.getSuccessPkIds().get(0);
+            }
+            DynamicObject customerObj = BusinessDataServiceHelper.loadSingle(customerId, ENTITY_NAME_CUSTOMER);
+            OperationServiceHelper.executeOperate("submit", ENTITY_NAME_CUSTOMER, new DynamicObject[]{customerObj}, OperateOption.create());
+            OperationServiceHelper.executeOperate("audit", ENTITY_NAME_CUSTOMER, new DynamicObject[]{customerObj}, OperateOption.create());
+        }
+
+        StringBuilder message = new StringBuilder();
+        message.append("1、更新成功");
+        String code = "200";
+        if(assignOrgIds.size() > 0) {
+            String assignResult = assignOrgForCustomer(customerId, assignOrgIds);
+
+            if (assignResult.isEmpty()) {
+                code = "200";
+                message.append(" 2、分配成功");
+            } else {
+                code = "200";
+                message.append(" 2、分配失败,");
+                message.append("组织:").append(assignOrgName);
+                message.append(assignResult);
+            }
+        }
+
+        return returnResult(code, message.toString());
+    }
+
+
+
+    /**
+     * 客户分配组织
+     * @param customerId
+     * @param assignOrgIds
+     * @return
+     */
+    public String assignOrgForCustomer(Long customerId, List<Long> assignOrgIds) {
+        String result = "";
+
+        ArrayList<Long> dataIds = new ArrayList();
+        dataIds.add(customerId);
+
+
+        List<Long> assignOrgList = BaseDataServiceHelper.getAssignDesOrgs(DEFAULT_ORG_ID,"2",16L);
+
+//        boolean isExist = false;
+//        if (assignOrgList.contains(assignOrgId)) {
+//            isExist = true;
+        //取交集
+        Set<Long> intersectOrgs = intersectionLong(assignOrgList, assignOrgIds);
+        Map<Long, Map> resultValue = BaseDataServiceHelper.batchAssignWithDetail(ENTITY_NAME_CUSTOMER, DEFAULT_ORG_ID, dataIds, new ArrayList(intersectOrgs));
+        if (resultValue.isEmpty()){
+            result= "";
+        }else{
+            result = "组织分配错误";
+        }
+        return result;
+//        }
+
+//        if(!isExist) {
+//            result = "不是CU,不能分配";
+//        }
+
+//        return result;
+    }
+
+    public static Set<Long> intersectionLong(Collection<Long> a, Collection<Long> b) {
+        Set<Long> unite = unionLong(a, b);
+        Set<Long> cloneFromB = new LinkedHashSet(b);
+        unite.removeAll(a);
+        cloneFromB.removeAll(unite);
+        return cloneFromB;
+    }
+
+    public static Set<Long> unionLong(Collection<Long> a, Collection<Long> b) {
+        Set<Long> unite = new LinkedHashSet();
+        unite.addAll(a);
+        unite.addAll(b);
+        return unite;
+    }
+
+    /**
+     * 自定义返回data对象
+     * @param code
+     * @param message
+     * @return
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+        CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
+
+        return result;
+    }
+}

+ 240 - 0
main/java/kd/cosmic/jkjt/common/basedata/listplugin/CustomerListPlugin.java

@@ -0,0 +1,240 @@
+package kd.cosmic.jkjt.common.basedata.listplugin;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.exception.KDException;
+import kd.bos.form.IClientViewProxy;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.list.IListView;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.HttpClientUtils;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.exception.WFErrorCode;
+import kd.cosmic.jkjt.msg.ecology.SSLClient;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.rmi.ConnectException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * @author turbo
+ *
+ * 客户列表插件
+ */
+public class CustomerListPlugin extends AbstractListPlugin {
+
+    public static Boolean ENVIRONMENT_DEV = true;
+    public CustomerListPlugin() {
+        /**
+         * 是否开发测试 debug
+         */
+        String environment  = System.getProperty("mc.environment.dev");
+        if("true".toString().equals(environment)){
+            ENVIRONMENT_DEV = true;
+        }else{
+            ENVIRONMENT_DEV = false;
+        }
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+        String key = evt.getItemKey();
+
+        if("nckd_btnbank".equals(key) || "nckd_btn_create".equals(key)){
+            ListSelectedRowCollection selectedRows = ((IListView)this.getView()).getSelectedRows();
+
+            if("nckd_btnbank".equals(key) && selectedRows.size() != 1){
+                this.getView().showConfirm("新增银行账户每次只能选择一条记录!", MessageBoxOptions.OK);
+                return;
+            }
+
+            /**
+             * 获取当前用户名
+             */
+            Long userId  = UserServiceHelper.getCurrentUserId();
+            DynamicObject userInfo = UserServiceHelper.getUserInfoByID(userId, "username");
+            String username = userInfo.getString("username");
+            if(ENVIRONMENT_DEV) {
+                username = "JK000191";
+            }
+
+            /**
+             * 获取常用查询中的组织
+             */
+            Long useOrgId = Long.valueOf(this.getPageCache().get("verifyOrg"));
+            //构造查询字段
+            List<String> orgParameters = new ArrayList<>();
+            orgParameters.add("fisaccounting");
+            orgParameters.add("number");
+            orgParameters.add("name");
+            //查询组织的相关字段
+            Map<String,Object> orgParameterMap = OrgUnitServiceHelper.getOrgProperty(useOrgId, orgParameters);
+            Boolean isAccounting = (Boolean)orgParameterMap.get("fisaccounting");
+            if(!isAccounting){
+                this.getView().showConfirm("选择的业务组织不是核算组织!", MessageBoxOptions.OK);
+                return;
+            }
+
+            String companyNo =  orgParameterMap.get("number").toString();
+            String companyName =  orgParameterMap.get("name").toString();
+
+            //CRM地址从二开公共参数基础资料上获取
+            String crmurl = "";
+            String crmtokenurl = "";
+            String crmappid = "";
+            String crmappsecret = "";
+            String customApi = "";
+            String accountApi = "";
+
+            String selectProperties = "nckd_crmtokenurl, nckd_crmurl, nckd_crmappid, nckd_crmappsecret, nckd_customapi, nckd_accountapi";
+            DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectProperties, null);
+            if(commonParams.length > 0){
+                crmtokenurl = commonParams[0].getString("nckd_crmtokenurl");
+                crmurl = commonParams[0].getString("nckd_crmurl");
+                crmappid = commonParams[0].getString("nckd_crmappid");
+                crmappsecret = commonParams[0].getString("nckd_crmappsecret");
+                customApi = commonParams[0].getString("nckd_customapi");
+                accountApi = commonParams[0].getString("nckd_accountapi");
+            }
+
+            if(StringUtils.isEmpty(crmurl)){
+                this.getView().showConfirm("请联系管理员维护CRM地址!", MessageBoxOptions.OK);
+                return;
+            }
+
+            if(StringUtils.isEmpty(crmappid)){
+                this.getView().showConfirm("请联系管理员维护CRM应用ID!", MessageBoxOptions.OK);
+                return;
+            }
+
+            if(StringUtils.isEmpty(crmappsecret)){
+                this.getView().showConfirm("请联系管理员维护CRM秘钥!", MessageBoxOptions.OK);
+                return;
+            }
+
+//                username = "JK000967";
+
+            String tokenUrl = crmtokenurl + "/api/oauth/ssoLogin";
+            tokenUrl += "?account=" + username;
+            tokenUrl += "&appId=" + crmappid;
+            tokenUrl += "&appSecret=" + crmappsecret;
+
+            JSONObject result = null;
+            try {
+                result = doGetByHttpClient(tokenUrl, null);
+            } catch (ConnectException e) {
+                throw new RuntimeException(e);
+            }
+            if (result == null) {
+                throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+            }
+
+            String token = "";
+            if("200".equals(result.getString("code"))){
+                token = result.getString("data");
+            } else {
+                String errorMsg = result.getString("msg") == null ? "" : result.getString("msg");
+                this.getView().showConfirm("获取CRM系统TOKEN失败!" + errorMsg, MessageBoxOptions.OK);
+                return;
+            }
+
+            /**
+             * 跳转CRM页面
+             */
+            String url = "";
+            StringBuilder openurl = new StringBuilder();
+            if("nckd_btn_create".equals(key)){
+                url = crmurl + customApi;
+                openurl.append(url);
+                openurl.append("?appId=").append(crmappid);
+                openurl.append("&workCode=").append(username);
+
+                if("/cust/add".equals(customApi)){
+                    openurl.append("&fmsOrgId=").append(companyNo);
+                    openurl.append("&fmsOrgName=").append(companyName);
+                } else if("/finance/add".equals(customApi)){
+                    openurl.append("&orgNumber=").append(companyNo);
+                    openurl.append("&orgName=").append(companyName);
+                }
+            } else if("nckd_btnbank".equals(key)){
+                String cusNumber = selectedRows.get(0).getNumber();
+
+                url = crmurl + accountApi;
+                openurl.append(url);
+                openurl.append("?appId=").append(crmappid);
+                openurl.append("&workCode=").append(username);
+
+                if("/cust/addBank".equals(customApi)){
+                    openurl.append("&unCustCode=").append(cusNumber);
+                } else if("/finance/addBank".equals(customApi)){
+                    openurl.append("&cusNumber=").append(cusNumber);
+                }
+            }
+            openurl.append("&token=").append(token);
+
+            // this.getView().openUrl(openurl);
+            IClientViewProxy proxy = this.getView().getService(IClientViewProxy.class);
+            Map<String, String> mpUrl = new HashMap();
+            mpUrl.put("url", openurl.toString());
+            proxy.addAction("openUrl", mpUrl);
+        }
+    }
+
+    public static JSONObject doGetByHttpClient(String url, String data) throws ConnectException {
+        CloseableHttpClient httpClient = null;
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            if (url.toLowerCase().startsWith("https://")) {
+                httpClient = new SSLClient();
+            } else {
+                httpClient = HttpClients.createDefault();
+            }
+            HttpGet httpget = new HttpGet(url);
+
+            HttpResponse response = httpClient.execute(httpget);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+
+            String body = EntityUtils.toString(response.getEntity());
+
+            JSONObject result = (JSONObject)JSONObject.parse(body);
+            return result;
+        } catch (SocketTimeoutException | ConnectTimeoutException ex){
+            throw new ConnectException("请求接口超时");
+        }catch (Exception e) {
+            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+            try {
+                httpClient.close();
+            } catch (IOException e) {
+                httpClient = null;
+            }
+        }
+    }
+
+}

+ 148 - 0
main/java/kd/cosmic/jkjt/common/oauth/OauthSSOAuthHandler.java

@@ -0,0 +1,148 @@
+package kd.cosmic.jkjt.common.oauth;
+
+import com.alibaba.fastjson.JSONObject;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import kd.bos.login.thirdauth.ThirdSSOAuthHandler;
+import kd.bos.login.thirdauth.UserAuthResult;
+import kd.bos.login.thirdauth.UserProperType;
+import kd.bos.url.UrlService;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023-02-08
+ * @description 苍穹oauth2认证登录插件(申石统一认证平台)
+ */
+public class OauthSSOAuthHandler implements ThirdSSOAuthHandler {
+    //统一认证平台自建应用编码
+    private static final String PARAM_CILENT_ID = "FMS";
+//    //统一认证平台自建应用客户端密钥
+//    private static final String PARAM_CLIENT_SECRET = "c1938c46-0bef-461a-8fa8-32be2fd0";
+//    //统一认证平台服务器
+//    private static final String PARAM_OAUTH_URL = "http://192.168.250.41:8882";
+
+    //生产配置
+    private static final String PARAM_CLIENT_SECRET = "0200d240-f074-4c49-8fb2-d47a661e";
+    private static final String PARAM_OAUTH_URL = "https://tysf.jxfhgc.com:8882";
+
+    /**
+     * 用户没有登录的时候,需要跳转到认证中心的登录地址
+     * @param request
+     * @param response
+     * @param backUrl
+     */
+    @Override
+    public void callTrdSSOLogin(HttpServletRequest request, HttpServletResponse response, String backUrl) {
+        String path = request.getRequestURI();
+        String code = request.getParameter("code");
+        if (path.contains("/auth/logout.do")) {
+            //String loginUrl = PARAM_OAUTH_URL + "/sso/oauth/logout";
+            String loginUrl = PARAM_OAUTH_URL + "/portal/sso/logout.html?redirectUri=http%3A%2F%2Flocalhost%3A8080%2Fierp" ;
+            try {
+                response.sendRedirect(loginUrl);
+            } catch (IOException var10) {
+                var10.printStackTrace();
+            }
+            //donothing
+        } else if ((code == null) || (code == "")) {
+            String loginUrl = null;
+            try {
+                loginUrl = PARAM_OAUTH_URL + "/sso/oauth/authorize?response_type=code&client_id=" + PARAM_CILENT_ID;
+                loginUrl += "&redirect_uri=" + URLEncoder.encode(UrlService.getDomainContextUrl(), "utf-8");
+            } catch (UnsupportedEncodingException var9) {
+                var9.printStackTrace();
+            }
+            try {
+                response.sendRedirect(loginUrl);
+            } catch (IOException var8) {
+                var8.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 调用认证系统的接口解析认证凭据返回用户信息,按数据格式返回认证结果
+     * @param request
+     * @param response
+     * @return
+     */
+    @Override
+    public UserAuthResult getTrdSSOAuth(HttpServletRequest request, HttpServletResponse response) {
+        UserAuthResult result = new UserAuthResult();
+        result.setSucess(false);
+        String loginName = "";
+        try {
+            loginName = getLoginUser(request);
+        } catch (IOException var6) {
+            var6.printStackTrace();
+        }
+        if ((loginName != null) && (!loginName.equals(""))) {
+            result.setUserType(UserProperType.UserName);
+            result.setUser(loginName);
+            result.setSucess(true);
+        }
+        return result;
+    }
+
+    /**
+     * 返回认证系统用户信息
+     * @param request
+     * @return
+     * @throws IOException
+     */
+    public String getLoginUser(HttpServletRequest request)
+            throws IOException {
+        String code = request.getParameter("code");
+        String tokenUrl = PARAM_OAUTH_URL + "/sso/oauth/accessToken?grant_type=authorization_code&client_id=" + PARAM_CILENT_ID;
+        tokenUrl += "&client_secret=" + PARAM_CLIENT_SECRET + "&code=" + code + "&redirect_uri=" + URLEncoder.encode(UrlService.getDomainContextUrl(), "utf-8");
+
+        CloseableHttpClient client = HttpClients.custom().build();
+        HttpPost tokenPost = new HttpPost(tokenUrl);
+        String token = (String) client.execute(tokenPost, new myResponse());
+        String access_token = JSONObject.parseObject(token).getString("access_token");
+
+        System.out.println("sso tokenUrl is " + tokenUrl);
+        if(code == null) {
+            System.out.println("sso code is null!");
+        }else{
+            System.out.println("sso code is " + code);
+        }
+        if(access_token == null) {
+            System.out.println("sso access_token is null!");
+        }else{
+            System.out.println("sso access_token is " + access_token);
+        }
+        HttpPost userInfoPost = new HttpPost(PARAM_OAUTH_URL + "/sso/oauth/userInfo?access_token=" + access_token);
+        String userInfo = (String) client.execute(userInfoPost, new myResponse());
+
+        //申石默认的是loginName, 江西金控使用的是uid
+        String useName = JSONObject.parseObject(userInfo).getString("uid");
+
+        System.out.println("sso useName is " + useName);
+
+        return useName;
+    }
+
+    static class myResponse implements ResponseHandler<String> {
+        @Override
+        public String handleResponse(HttpResponse httpResponse) throws IOException {
+            HttpEntity entity = httpResponse.getEntity();
+            if (entity != null) {
+                String s = EntityUtils.toString(entity);
+                return s;
+            }
+            return null;
+        }
+    }
+}

+ 118 - 0
main/java/kd/cosmic/jkjt/common/oauth/RSAUtils.java

@@ -0,0 +1,118 @@
+package kd.cosmic.jkjt.common.oauth;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public class RSAUtils {
+
+    private static Provider provider;
+
+    static {
+        provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
+        Security.addProvider(provider);
+    }
+
+    /**
+     * 产生key pair,提供provider和random
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        // 产生用于安全加密的随机数
+        SecureRandom random = new SecureRandom();
+
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
+        Integer length = 2048;//加密算法位数
+        generator.initialize(length, random);
+        return generator.generateKeyPair();
+    }
+
+    /**
+     * 返回一个keyBytes生成的PublicKey对象
+     * @param keyBytes
+     * @return
+     * @throws NoSuchAlgorithmException
+     * @throws InvalidKeySpecException
+     */
+    public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey publicKey = keyFactory.generatePublic(keySpec);
+        return publicKey;
+    }
+
+    public static byte[] encryptByKey(Key keys, byte[] byteArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+        // Cipher: 提供加密和解密功能的实例
+        // transformation: "algorithm/mode/padding"
+        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
+        // PrivateKey privateKey = keyPair.getPrivate();
+        // 初始化
+        cipher.init(Cipher.ENCRYPT_MODE, keys);
+        // doFinal(): 加密或者解密数据
+        byte[] plainText = cipher.doFinal(byteArray);
+        return plainText;
+    }
+
+    /**
+     * 返回一个keyBytes生成的privateKey对象
+     * @param keyBytes
+     * @return
+     * @throws NoSuchAlgorithmException
+     * @throws InvalidKeySpecException
+     */
+    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+        return privateKey;
+    }
+
+    /**
+     * 用相应的key解密对应加密的问题
+     *
+     * @param keys
+     * @param byteArray
+     * @return
+     */
+    public static byte[] decryptByKey(Key keys, byte[] byteArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+        // Cipher: 提供加密和解密功能的实例
+        // transformation: "algorithm/mode/padding"
+        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
+
+        // 初始化
+        cipher.init(Cipher.DECRYPT_MODE, keys);
+        // doFinal(): 加密或者解密数据
+        byte[] plainText = cipher.doFinal(byteArray);
+        return plainText;
+    }
+
+    /**
+     * 产生public key
+     *
+     * @return public key字符串
+     */
+    public static String generateBase64Key(byte[] keys) {
+        // encodeBase64(): Encodes binary data using the base64
+        // algorithm but does not chunk the output.
+        // getEncoded():返回key的原始编码形式
+        return new String(Base64.encodeBase64(keys));
+    }
+}

+ 49 - 0
main/java/kd/cosmic/jkjt/common/oauth/SHAUtils.java

@@ -0,0 +1,49 @@
+package kd.cosmic.jkjt.common.oauth;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+public class SHAUtils {
+
+
+    public static String getSHA256Hex(String input) {
+        try {
+            // 创建一个 MessageDigest 实例,并指定算法为 SHA-256
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+
+            // 将输入字符串转换为字节数组
+            byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+
+            // 将字节数组转换为十六进制字符串
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : hash) {
+                String hex = Integer.toHexString(0xff & b);
+                if (hex.length() == 1) hexString.append('0');
+                hexString.append(hex);
+            }
+
+            return hexString.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 检查时间戳是否一致,默认允许5分钟误差
+     * @param timeStamp
+     * @return
+     */
+    public static boolean checkDateTime(String timeStamp){
+        long now = System.currentTimeMillis();
+        if(StringUtils.isEmpty(timeStamp) || (StringUtils.isNotEmpty(timeStamp) && StringUtils.isNumeric(timeStamp) && Math.abs(now - Long.parseLong(timeStamp)) / 1000 > 5 * 60)){
+            return false;
+        }
+        return true;
+    }
+
+}

+ 128 - 0
main/java/kd/cosmic/jkjt/common/oauth/SSOAuthtication.java

@@ -0,0 +1,128 @@
+package kd.cosmic.jkjt.common.oauth;
+
+import kd.bos.dc.api.model.Account;
+import kd.bos.entity.AppInfo;
+import kd.bos.entity.AppMetadataCache;
+import kd.bos.entity.param.AppParam;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.login.thirdauth.app.AppAuthResult;
+import kd.bos.login.thirdauth.app.ThirdAppAuthtication;
+import kd.bos.login.thirdauth.app.UserType;
+import kd.bos.servicehelper.parameter.SystemParamServiceHelper;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2025/01/17
+ * @description 单点登录拦截,加固单点校验方式
+ */
+public class SSOAuthtication extends ThirdAppAuthtication {
+    private static Log logger = LogFactory.getLog(SSOAuthtication.class);
+    /**
+     * 判断该接口请求是否需要通过此插件认证
+     */
+    @Override
+    public boolean isNeedHandle(HttpServletRequest request, Account account) {
+        //加密的数据信息
+        String code = request.getParameter("kd_code");
+        if(StringUtils.isNotBlank(code)){
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *用户身份解析
+     */
+    @Override
+    public AppAuthResult appAuthtication(HttpServletRequest request, Account account) {
+        AppAuthResult result = new AppAuthResult();
+        result.setSucceed(false);
+        try{
+            //加密的数据信息
+            String code = request.getParameter("kd_code");
+            if(StringUtils.isBlank(code)){
+                logger.info("SSOAuthtication:kd_code is null");
+
+                result.setErrorMessage("sso:kd_code is null");
+                return result;
+            }
+
+            //获取上下文中的当前登陆的业务单元id
+            long orgId = 100000L;
+
+            AppInfo appPm = AppMetadataCache.getAppInfo("secm");
+            //应用APPID
+            String appId = appPm.getId();
+            AppParam apm = new AppParam();
+            apm.setAppId(appId);
+            apm.setOrgId(orgId);
+            Map<String, Object> parameters = SystemParamServiceHelper.loadAppParameterFromCache(apm);
+
+            if (null == parameters) {
+                logger.info("SSOAuthtication:third parameters is null");
+
+                result.setErrorMessage("sso:third parameters is null");
+                return result;
+            } else {
+                if (null == parameters.get("nckd_thirdprivatekey") || "".equals(parameters.get("nckd_thirdprivatekey"))) {
+                    logger.info("SSOAuthtication:nckd_thirdprivatekey is null");
+
+                    result.setErrorMessage("sso:nckd_thirdprivatekey is null");
+                    return result;
+                }
+
+                if (null == parameters.get("nckd_thirdsecret") || "".equals(parameters.get("nckd_thirdsecret"))) {
+                    logger.info("SSOAuthtication:nckd_thirdsecret is null");
+
+                    result.setErrorMessage("sso:nckd_thirdsecret is null");
+                    return result;
+                }
+            }
+            //和第三方约定
+            String secret = parameters.get("nckd_thirdsecret").toString();
+            //私钥
+            String privateKey = parameters.get("nckd_thirdprivatekey").toString();
+
+            //可以先使用SHAUtils.getSHA256Hex实现签名验证再数据解密
+            String timeStamp = request.getParameter("timeStamp");
+            String signture = request.getParameter("signture");
+            if(!SHAUtils.checkDateTime(timeStamp)) {
+                logger.info("SSOAuthtication:timeStamp check fail");
+
+                result.setErrorMessage("sso:timeStamp check fail");
+                return result;
+            }
+
+            String sign = SHAUtils.getSHA256Hex(code + timeStamp + secret);
+
+            //签名认证
+            if(StringUtils.isBlank(signture) || !signture.equals(sign)) {
+                logger.info("SSOAuthtication:sign check fail");
+
+                result.setErrorMessage("sso:sign check fail");
+                return result;
+            }
+
+            String user = new String(RSAUtils.decryptByKey(RSAUtils.getPrivateKey(Base64.decodeBase64(privateKey))
+                                        , Base64.decodeBase64(code.getBytes("UTF-8"))));
+            if(StringUtils.isBlank(user)){
+                logger.info("SSOAuthtication:user is null");
+
+                result.setErrorMessage("sso:user is null");
+                return result;
+            }
+
+            result.setUserFlag(user);
+            result.setUserType(UserType.USER_NAME);
+            result.setSucceed(true);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return result;
+    }
+}

+ 104 - 0
main/java/kd/cosmic/jkjt/common/util/JKsmsServiceHandler.java

@@ -0,0 +1,104 @@
+package kd.cosmic.jkjt.common.util;
+
+import kd.bos.context.RequestContext;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.message.api.ShortMessageInfo;
+import kd.bos.message.service.handler.MessageHandler;
+import kd.bos.workflow.engine.WfConfigurationUtil;
+import kd.bos.workflow.engine.WfUtils;
+import kd.bos.workflow.engine.msg.AbstractMessageServiceHandler;
+import kd.bos.workflow.engine.msg.MessageServiceUtil;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.bos.workflow.engine.msg.handler.SMSServiceHandler;
+import kd.bos.workflow.engine.msg.info.MessageInfo;
+import kd.bos.workflow.engine.msg.info.ToDoInfo;
+import kd.bos.workflow.engine.msg.util.MessageUtils;
+import kd.bos.workflow.engine.msg.util.yzj.YunzhijiaToDoUtil;
+import kd.bos.workflow.exception.WFErrorCode;
+import kd.bos.workflow.exception.WFMessageServiceException;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class JKsmsServiceHandler extends AbstractMessageServiceHandler {
+
+    private static Log logger = LogFactory.getLog(SMSServiceHandler.class);
+    public static final String TYPE = "sms";
+    private static final int CONTENTMAXLENGTH = 450;
+
+
+    @Override
+    public void createToDo(MessageContext messageContext, ToDoInfo info) {
+
+    }
+
+    @Override
+    public void dealToDo(MessageContext messageContext, ToDoInfo info) {
+        if (WfConfigurationUtil.isEnabled("sms")) {
+            logger.info(String.format("nothing to do for[%s] when dealing sms todo", info.getTaskId()));
+        }
+    }
+
+    @Override
+    public void deleteToDo(MessageContext messageContext, ToDoInfo info) {
+        if (WfConfigurationUtil.isEnabled("sms")) {
+            logger.info(String.format("nothing to do for[%s] when deleting sms todo", info.getTaskId()));
+        }
+    }
+
+    @Override
+    public void sendMessage(MessageContext ctx, MessageInfo message) {
+//        if (WfConfigurationUtil.isEnabled("sms")) {
+//
+//            try {
+//                MessageServiceUtil.updateToDoMsgContent(this.config, message);
+//                MessageUtils.wrapMessageContent(message, "sms");
+//                List<MessageInfo> messages = YunzhijiaToDoUtil.rebuildMessage(message);
+//                if (MessageUtils.isBlankMessage(messages)) {
+//                    logger.info("SMSServiceHandler-- messagecontent is blank");
+//                    throw new WFMessageServiceException((Throwable)null, WFErrorCode.sendMsgWithoutContentError(), new Object[0]);
+//                } else {
+//                    Iterator var4 = messages.iterator();
+//
+//                    while(var4.hasNext()) {
+//                        MessageInfo messageInfo = (MessageInfo)var4.next();
+//                        String msgContent = this.getMessageUrlForFixed(messageInfo.getContent());
+//                        messageInfo.setContent(msgContent);
+//                        Map<String, Object> map = MessageUtils.getPhonesByParamsForSms((ToDoInfo)null, messageInfo);
+//                        Object phonesObj = null;
+//                        Object useridAndPhone = null;
+//                        if (WfUtils.isNotEmptyForMap(map)) {
+//                            phonesObj = map.get("phones");
+//                            useridAndPhone = map.get("useridAndPhone");
+//                        }
+//
+//                        List<String> phones = phonesObj != null ? (List)phonesObj : null;
+//                        Map<String, Object> useridAndPhoneMap = useridAndPhone != null ? (Map)useridAndPhone : null;
+//                        if (phones == null || phones.size() == 0) {
+//                            throw new WFMessageServiceException((Throwable)null, WFErrorCode.smsSendMsgError(), new Object[]{"sms receiver is empty"});
+//                        }
+//
+//                        ShortMessageInfo shortMessageInfo = this.buildShortMessageInfo(messageInfo, phones);
+//                        shortMessageInfo.setUseridAndPhone(useridAndPhoneMap);
+//                        shortMessageInfo.setSignature(MessageUtils.getSingture((String)messageInfo.getParams().get("lang")));
+//                        logger.info(String.format("短信服务:发送消息:ctx:%s", ctx == null ? "" : ctx.toString()));
+//                        Map<String, Object> res = MessageHandler.sendShortMessage(shortMessageInfo, message.getParams());
+//                        if (!(Boolean)res.get("result")) {
+//                            throw new WFMessageServiceException((Throwable)null, WFErrorCode.smsSendMsgError(), new Object[]{"take MessageHandler has exception " + res.get("description")});
+//                        }
+//
+//                        logger.info(String.format("[%s]发送短信消息成功", RequestContext.get().getTraceId()));
+//                    }
+//
+//                }
+//            } catch (Exception var14) {
+//                logger.info(WfUtils.getExceptionStacktrace(var14));
+//                throw new WFMessageServiceException(var14, WFErrorCode.smsSendMsgError(), new Object[]{var14.getMessage()});
+//            }
+//        }
+    }
+
+
+}

+ 24 - 0
main/java/kd/cosmic/jkjt/common/util/Result.java

@@ -0,0 +1,24 @@
+package kd.cosmic.jkjt.common.util;
+
+/**
+ * @author turbo
+ * for short massage
+ */
+public class Result {
+
+    private int code = 0;
+    private String msg = null;
+
+    public Result(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public static Result error(int i, String s) {
+        return new Result(i, s);
+    }
+
+    public static Result success() {
+        return new Result(200, "success");
+    }
+}

+ 125 - 0
main/java/kd/cosmic/jkjt/common/util/SmsService.java

@@ -0,0 +1,125 @@
+package kd.cosmic.jkjt.common.util;
+
+import kd.cosmic.jkjt.common.util.Result;
+import kd.fi.bcm.formplugin.dimension.batchimp.helper.ImportResultEntry;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author wangqiang
+ * @create 2022-08-22 17:53
+ */
+public class SmsService {
+    private final HttpClient httpClient;
+    private String url = "http://sms.api.ums86.com:8899/sms/Api/Send.do";
+    private String spCode = "243649";
+    private String loginName = "jx_jr";
+    private String password = "jxjkyxt8638";
+
+    public SmsService() {
+        this.httpClient = HttpClients.createDefault();
+    }
+
+    public Result sendSMS(String smsId, String number, String msg) {
+        String filter = filter(msg);
+
+        number = number.replace("|", ",");
+        try {
+            send(filter, number);
+        } catch (Exception e) {
+            return Result.error(500, "发送短信出现异常!");
+        }
+
+        return Result.success();
+    }
+
+    public Result sendSMS(String number, String msg) {
+        number = number.replace("|", ",");
+        try {
+            send("您的验证码为" + msg, number);
+        } catch (Exception e) {
+            return Result.error(500, "发送短信出现异常!");
+        }
+
+        return Result.success();
+    }
+
+    public void send(String message, String numbers) throws Exception {
+        ArrayList parameterList = new ArrayList();
+        parameterList.add(new BasicNameValuePair("SpCode", this.spCode));
+        parameterList.add(new BasicNameValuePair("LoginName", this.loginName));
+        parameterList.add(new BasicNameValuePair("Password", this.password));
+        parameterList.add(new BasicNameValuePair("MessageContent", message));
+        parameterList.add(new BasicNameValuePair("UserNumber", numbers));
+        parameterList.add(new BasicNameValuePair("SerialNumber", String.valueOf(System.currentTimeMillis())));
+        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(parameterList, "GBK");
+
+        HttpPost httpPost = new HttpPost(this.url);
+        httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=gbk");
+        httpPost.setEntity(urlEncodedFormEntity);
+
+        HttpResponse response = this.httpClient.execute(httpPost);
+        String responseStr = EntityUtils.toString(response.getEntity(), "GBK");
+
+        if (response.getStatusLine().getStatusCode() != 200) {
+            throw new Exception("状态码非200");
+        }
+
+        if (responseStr.charAt(7) != '0')
+            throw new Exception("短信发送失败," + responseStr);
+    }
+
+    private String filter(String content) {
+        Pattern compile = Pattern.compile("E-cology短信随机验证码:(?<captcha>.*),请登录后及时修改!");
+
+        Matcher matcher = compile.matcher(content);
+        if (!(matcher.find())) {
+            return content;
+        }
+
+        String captcha = matcher.group("captcha");
+
+        return "您的验证码为" + captcha;
+    }
+
+    public String getUrl() {
+        return this.url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getSpCode() {
+        return this.spCode;
+    }
+
+    public void setSpCode(String spCode) {
+        this.spCode = spCode;
+    }
+
+    public String getLoginName() {
+        return this.loginName;
+    }
+
+    public void setLoginName(String loginName) {
+        this.loginName = loginName;
+    }
+
+    public String getPassword() {
+        return this.password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

+ 210 - 0
main/java/kd/cosmic/jkjt/epm/eb/formplugin/bgadjust/OverallBudgetAdjustCusEditPlugin.java

@@ -0,0 +1,210 @@
+package kd.cosmic.jkjt.epm.eb.formplugin.bgadjust;
+
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.db.DB;
+import kd.bos.db.DBRoute;
+import kd.bos.form.ConfirmCallBackListener;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.MessageBoxResult;
+import kd.bos.form.control.events.BeforeItemClickEvent;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.events.MessageBoxClosedEvent;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import java.util.EventObject;
+
+/**
+ * 插件说明:表单插件
+ * 表单标识:预算调整单(nckd_bgm_adjustbill_ext)
+ * @author wanghaiwu_kd
+ * @date 2024/02/26
+ */
+public class OverallBudgetAdjustCusEditPlugin extends AbstractBillPlugIn {
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        this.addItemClickListeners(new String[]{"toolbarap"});
+//        this.addItemClickListeners(new String[]{"bar_save"});
+    }
+
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+    }
+
+    @Override
+    public void beforeItemClick(BeforeItemClickEvent evt) {
+        super.beforeItemClick(evt);
+
+        String operateKey = evt.getOperationKey();
+
+        if (StringUtils.equals(operateKey, "submit")) {
+            String checkMsg = checkAdjustNumbers();
+
+            if(StringUtils.isNotEmpty(checkMsg)){
+                checkMsg += "\r\n调整将计入考核,注意资金预算的准确性!";
+                checkMsg += "\r\n请确认是否提交?";
+
+                evt.setCancel(true);
+                this.getView().showConfirm(checkMsg, MessageBoxOptions.OKCancel, new ConfirmCallBackListener("NCKD_CHECKNUMBERS", this));
+            }
+        }
+    }
+
+    public void confirmCallBack(MessageBoxClosedEvent messageBoxClosedEvent) {
+        super.confirmCallBack(messageBoxClosedEvent);
+        if ("NCKD_CHECKNUMBERS".equals(messageBoxClosedEvent.getCallBackId())) {
+            if (MessageBoxResult.Yes.equals(messageBoxClosedEvent.getResult())) {
+                this.getView().invokeOperation("submit");
+            }
+        }
+    }
+
+    /**
+     * 检查已调整的次数
+     * @return
+     */
+    private String checkAdjustNumbers(){
+        DynamicObject adjustRule = (DynamicObject) this.getModel().getValue("adjustrule");
+        if(adjustRule == null){
+            return "";
+        }
+
+        adjustRule = BusinessDataServiceHelper.loadSingle(adjustRule.getPkValue(), adjustRule.getDynamicObjectType().getName());
+        String remark = adjustRule.getString("remark");
+        if(StringUtils.isEmpty(remark)){
+            return "";
+        }
+
+        if(!remark.contains("资金计划调整规则")){
+            return "";
+        }
+
+        String year = getYear();
+        String orgIds = getOrgIds();
+
+        if("".equals(year) || "".equals(orgIds)){
+            return "";
+        }
+
+        String msg = "";
+
+        Object id = this.getModel().getValue("id");
+        Long billId = Long.valueOf(String.valueOf(id));
+
+        String sql = builderSQL(billId, year, orgIds);
+        DataSet dataSet = DB.queryDataSet(this.getClass().getName(), DBRoute.of("fi"), sql);
+
+        for (Row row : dataSet) {
+            String orgName = row.getString("fname");
+            int numbers = row.getInteger("fnumbers");
+
+            if(StringUtils.isNotEmpty(msg)){
+                msg += "\r\n";
+            }
+            msg += orgName + "已是本年度第" + (numbers + 1)  + "次调整";
+        }
+
+        return msg;
+    }
+
+    /**
+     * 获取期间的年份
+     * @return
+     */
+    private String getYear(){
+        String year = "";
+        //预算期间
+        DynamicObjectCollection periods = (DynamicObjectCollection) this.getModel().getValue("budgetperiods");
+        if(periods.size() > 0){
+            for (DynamicObject period : periods) {
+                DynamicObject baseData = period.getDynamicObject("fbasedataid");
+                year = baseData.getString("number").split("\\.")[0];
+                year = year + ".M%";
+
+                break;
+            }
+        }
+
+        return year;
+    }
+
+    /**
+     * 获取调整分录的组织
+     * @return
+     */
+    private String getOrgIds(){
+        String orgIds = "";
+        DynamicObjectCollection adjustEntrys = this.getModel().getEntryEntity("adjdetailentity");
+        if(adjustEntrys.size() > 0){
+            for(DynamicObject entry : adjustEntrys){
+                if(entry.getDynamicObject("entity") != null) {
+                    if(StringUtils.isNotEmpty(orgIds)){
+                        orgIds += ",";
+                    }
+                    orgIds += entry.getDynamicObject("entity").getLong("id");
+                }
+            }
+        }
+
+        return orgIds;
+    }
+
+    /**
+     * 组合统计SQL
+     * @param year
+     * @param orgIds
+     * @return
+     */
+    private String builderSQL(Long billId, String year, String orgIds){
+        StringBuilder sb = new StringBuilder();
+        sb.append("/*dialect*/");
+        sb.append("select adjust.forgid, org.fname, count(*) fnumbers \n");
+        sb.append("from ( \n");
+        sb.append(" select distinct bill.fid, entry.fentity forgid \n");
+        sb.append(" from t_eb_adjustbill bill \n");
+        sb.append(" inner join t_eb_adjustrule adrule on bill.fadjustrule = adrule.fid \n");
+        sb.append(" inner join t_eb_adjdetail entry on bill.fid = entry.fid \n");
+        sb.append(" inner join ( \n");
+        sb.append("  select distinct fid \n");
+        sb.append("  from t_eb_adjustbill_bp \n");
+        sb.append("  where fbasedataid in( \n");
+        sb.append("   select fid from t_eb_structofbperiod where fnumber like '").append(year).append("' \n");
+        sb.append("  ) \n");
+        sb.append(" ) period on bill.fid = period.fid \n");
+        sb.append(" where adrule.fremark like '%资金计划调整规则%' and bill.fid <> ").append(billId).append(" \n");
+        sb.append(") adjust \n");
+        sb.append("inner join t_eb_structofent_l org on adjust.forgid = org.fid and org.flocaleid = 'zh_CN' \n");
+        sb.append("where adjust.forgid in (").append(orgIds).append(") \n");
+        sb.append("group by adjust.forgid, org.fname ");
+
+        return sb.toString();
+    }
+
+    /**
+     * 获取多选基础资料返回值
+     * @param baseField
+     * @return
+     */
+    private Object[] getCurrBaseIds(String baseField) {
+        DynamicObjectCollection bases = (DynamicObjectCollection) this.getModel().getValue(baseField);
+        return bases.stream().map((o) -> {
+            return o.getDynamicObject("fbasedataid").getLong("fbasedataid_id");
+        }).toArray();
+    }
+}

+ 32 - 0
main/java/kd/cosmic/jkjt/fi/ai/formplugin/PreViewCusFormPlugin.java

@@ -0,0 +1,32 @@
+package kd.cosmic.jkjt.fi.ai.formplugin;
+
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import java.util.EventObject;
+import java.util.Map;
+
+/**
+ * 插件说明:表单插件
+ * 表单标识:凭证预览(nckd_ai_previewvouche_ext)
+ * @author wanghaiwu_kd
+ * @date 2024/01/22
+ */
+public class PreViewCusFormPlugin extends AbstractFormPlugin {
+    private static final Log logger = LogFactory.getLog(PreViewCusFormPlugin.class);
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Map<String, Object> customParams = this.getView().getFormShowParameter().getCustomParams();
+        String isDefaultViewVoucher = customParams.get("nckd_openviewvoucher") == null ? "" : customParams.get("nckd_openviewvoucher").toString();
+
+        //隐藏控件
+        if("1".equals(isDefaultViewVoucher)){
+            //修改按钮
+            this.getView().setVisible(false, "save");
+            //凭证模版字段
+            this.getView().setVisible(false, "flexpanelap7");
+        }
+    }
+}

+ 790 - 0
main/java/kd/cosmic/jkjt/fi/cas/common/PayBillToolUtil.java

@@ -0,0 +1,790 @@
+package kd.cosmic.jkjt.fi.cas.common;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+public class PayBillToolUtil {
+
+    public static final String ENTITY_NAME = "cas_paybill";
+
+    /**
+     * CBS支付单同步
+     * @params billEntities
+     * @returns 返回报错信息
+     *
+     */
+    public static String payBillForCBSSync(DynamicObject[] billEntities) {
+        StringBuffer errMsg = new StringBuffer();
+        for (DynamicObject dataEntity : billEntities) {
+            boolean isSuccess = false;
+            boolean isCBS = true;
+
+            Map<String, String> cbsObject = new HashMap<>(20);
+
+            DynamicObject payBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), ENTITY_NAME);
+
+            //add by wanghaiwu 2023/10/12
+            //检查付款处理单是否有上游单据,并且已生成提交及之后状态的凭证。
+            QFilter qFilter = new QFilter("tbillid",QCP.equals, payBillEntity.getPkValue());
+            //查询业务付款处理单的上游单据
+            DynamicObject billRelation = BusinessDataServiceHelper.loadSingle("botp_billtracker", new QFilter[]{qFilter});
+            if(billRelation != null){
+                Long sourceBillId = billRelation.getLong("sbillid");
+
+                qFilter = new QFilter("sourcebillid",QCP.equals, sourceBillId);
+                //查询上游单据下游凭证
+                DynamicObject dapRelation = BusinessDataServiceHelper.loadSingle("ai_daptracker", new QFilter[]{qFilter});
+
+                if(dapRelation == null){
+                    errMsg.append("单据号:").append(payBillEntity.getString("billno")).append("的上游单据未生成凭证!\r\n");
+                    isCBS = false;
+                    continue;
+                }
+
+                Long voucherId = dapRelation.getLong("voucherid");
+                qFilter = new QFilter("id",QCP.equals, voucherId);
+                //获取下游的凭证
+                DynamicObject voucher = BusinessDataServiceHelper.loadSingle("gl_voucher", new QFilter[]{qFilter});
+                if(voucher == null){
+                    errMsg.append("单据号:").append(payBillEntity.getString("billno")).append("的上游单据的下游凭证不存在!\r\n");
+                    isCBS = false;
+                    continue;
+                }
+
+                String voucherStatus = voucher.getString("billstatus");
+                if(!voucherStatus.matches("B|C")){
+                    errMsg.append("单据号:").append(payBillEntity.getString("billno")).append("的上游单据的下游凭证不是已提交或已审核状态!\r\n");
+                    isCBS = false;
+                    continue;
+                }
+            }
+
+            ///多CBS处理  获取
+            String cbsUrlBill = ParamsUtil.getCBSURL(payBillEntity.getDynamicObject("openorg").getLong("id"));
+
+            /**
+              *   判断是否走CBS
+              *   1、结算方式为电汇与转账
+              *   2、付款单为审核状态
+              *   3、CBS支付状态 为 1 未支付 4 支付失败
+              *   4、付款账号基础资料中,必须包含可CBS支付
+             */
+            String billNum = payBillEntity.getString("billno");
+            String billstatusString = payBillEntity.getString("billstatus");
+            String cbsstatusString = payBillEntity.getString("nckd_cbsstatus");
+
+            //结算方式  0 其它 1 现金 2 转账 3 电汇 4 汇票 5 内转
+            DynamicObject settletypeEntity = payBillEntity.getDynamicObject("settletype");
+            String settletypeString = settletypeEntity.getString("number");
+            if (!"JSFS04".equals(settletypeString)) {
+                errMsg.append("单据号:").append(billNum).append(",结算方式为CBS,才能提交到CBS支付!\r\n");
+                isCBS = false;
+            }
+            if (!"C".equals(billstatusString)) {
+                errMsg.append("单据号:").append(billNum).append(",付款单为审核状态,才能提交到CBS支付!\r\n");
+                isCBS = false;
+            }
+            if (!"1".equals(cbsstatusString)) {
+                errMsg.append("单据号:").append(billNum).append(",CBS支付状态为 未支付,才能提交到CBS支付!\r\n");
+                isCBS = false;
+            }
+            if(StringUtils.isEmpty(cbsUrlBill)){
+                errMsg.append("单据号:").append(billNum).append(",该单据组织未配置CBS环境,请配置好参数后再提交到CBS支付!\r\n");
+                isCBS = false;
+            }
+            /**
+             * 当重新支付时,版本号+1
+             * CBS的业务号
+             */
+            StringBuffer billNumCBS = new StringBuffer();
+            billNumCBS.append(billNum);
+            /**
+             * 版本支付处理
+             *
+             *
+            if ("4".equals(cbsstatusString)) {
+                payBillEntity.set("nckd_payversion",payBillEntity.getInt("nckd_payversion")+1);
+                billNumCBS.append("~").append(payBillEntity.getInt("nckd_payversion"));
+                errMsg.append("单据号:").append(billNumCBS.toString()).append(",支付失败后重新支付~\r\n");
+            }
+            */
+            cbsObject.put("REFNBR", billNumCBS.toString());
+
+            if(payBillEntity.getDynamicObject("payeracctbank") == null) {
+                isCBS = false;
+            }else{
+                Object bankPk = payBillEntity.getDynamicObject("payeracctbank").getPkValue();
+
+                DynamicObject payeracctbankEntity = BusinessDataServiceHelper.loadSingle(bankPk, "am_accountbank");
+                String cbsBankFunc = payeracctbankEntity.getString("nckd_cbsbebankfunc");
+                ///付款银行账号
+                cbsObject.put("CLTACC", payeracctbankEntity.getString("bankaccountnumber"));
+                if (cbsBankFunc.indexOf("pay") < 0) {
+                    errMsg.append("单据号:").append(billNum).append(",付款账号属性中未开通CBS支付功能,开通后才能提交哦~\r\n");
+                    isCBS = false;
+                }
+                //收款银行类型
+                if (payBillEntity.getDynamicObject("payeebank") != null) {
+                    cbsObject.put("BNKTYP", getBankType(payBillEntity.getDynamicObject("payeebank").getPkValue()));
+                } else {
+                    cbsObject.put("BNKTYP", "");
+                }
+            }
+
+            //推送CBS判断
+            if(!isCBS){
+                continue;
+            }
+
+            //收款方行名行号、省份、城市
+            String unioncode = "";
+            String provincetxt = "";
+            String citytxt = "";
+            //如果付款处理的收款银行有值,直接去行名行号的联行号
+            if (payBillEntity.getDynamicObject("payeebank") != null){
+                DynamicObject bebank = BusinessDataServiceHelper.loadSingle(payBillEntity.getLong("payeebank.id"), "bd_bebank");
+                unioncode = bebank.getString("union_number");
+                provincetxt = bebank.getString("provincetxt");
+                citytxt = bebank.getString("citytxt");
+            }
+            //根据收款银行账号获取
+            else {
+                String banknumber = payBillEntity.getString("payeebanknum");
+                QFilter qFilterno = new QFilter("bankaccountnumber", QCP.equals, banknumber);
+                DynamicObject accountInfo = BusinessDataServiceHelper.loadSingleFromCache("am_accountbank", new QFilter[]{qFilterno});
+                if(accountInfo != null){
+                    //合作金融机构
+                    DynamicObject bank = accountInfo.getDynamicObject("bank");
+                    bank = BusinessDataServiceHelper.loadSingle(bank.getLong("id"), "bd_finorginfo");
+
+                    //行名行号
+                    if(bank.getDynamicObject("bebank") != null) {
+                        DynamicObject bebank = BusinessDataServiceHelper.loadSingle(bank.getDynamicObject("bebank").getLong("id"), "bd_bebank");
+                        unioncode = bebank.getString("union_number");
+                        provincetxt = bebank.getString("provincetxt");
+                        citytxt = bebank.getString("citytxt");
+                    }
+                }
+            }
+            cbsObject.put("unioncode", unioncode);
+            cbsObject.put("provincetxt", provincetxt);
+            cbsObject.put("citytxt", citytxt);
+
+            //处理同城参数 add by wanghaiwu_kd 2024/04/11
+            DynamicObject payerbank = payBillEntity.getDynamicObject("payerbank");
+            if(payerbank != null){
+                payerbank = BusinessDataServiceHelper.loadSingle(payerbank.getPkValue(), "bd_finorginfo");
+                //银行类别
+                DynamicObject bankCate = payerbank.getDynamicObject("bank_cate");
+                DynamicObject bebank = payerbank.getDynamicObject("bebank");
+                if(bankCate != null && bebank != null) {
+                    //银行类型
+                    bankCate = BusinessDataServiceHelper.loadSingle(bankCate.getPkValue(), "bd_bankcgsetting");
+                    boolean needCtyFlag = bankCate.getBoolean("nckd_needctyflag");
+                    if(needCtyFlag){
+                        bebank = BusinessDataServiceHelper.loadSingle(bebank.getPkValue(), "bd_bebank");
+                        String paycitytxt = bebank.getString("citytxt");
+
+                        if(citytxt.equals(paycitytxt)){
+                            cbsObject.put("ctyflg", "0");
+                        } else {
+                            cbsObject.put("ctyflg", "1");
+                        }
+                    }
+                }
+            }
+
+            //处理对照字段信息
+            //币别 10 人民币  21 港币  32 美元  65 日元 43 英镑 35 欧元
+            DynamicObject currencyEntity = payBillEntity.getDynamicObject("currency");
+            switch (currencyEntity.getString("number")) {
+                case "CNY":
+                    cbsObject.put("CCYNBR", "10");
+                    break;
+                case "HKD":
+                    cbsObject.put("CCYNBR", "21");
+                    break;
+                case "JPY":
+                    cbsObject.put("CCYNBR", "65");
+                    break;
+                case "USD":
+                    cbsObject.put("CCYNBR", "32");
+                    break;
+                case "EUR":
+                    cbsObject.put("CCYNBR", "35");
+                    break;
+                case "GBP":
+                    cbsObject.put("CCYNBR", "43");
+                    break;
+                default:
+                    cbsObject.put("CCYNBR", "10");
+            }
+
+            //支付时间 默认当前时间
+            cbsObject.put("EPTTIM", DateFormatUtils.format(new Date(), "HH:mm:ss"));
+
+            //结算方式  0 其它 1 现金 2 转账 3 电汇 4 汇票 5 内转
+            //默认转账处理"
+            cbsObject.put("PAYTYP", "2");
+
+            //是否加急
+            cbsObject.put("PAYSON", payBillEntity.getString("priority").equals("prior") ? "Y" : "N");
+
+            //支付日期   提交银行的付款日期 大于等于当前交日期
+            Date expectdate = payBillEntity.getDate("expectdate");
+            if(expectdate == null){
+                expectdate = new Date();
+                payBillEntity.set("expectdate",expectdate);
+            }
+            cbsObject.put("EPTDAT", DateFormatUtils.format(expectdate, "yyyy-MM-dd"));
+
+            //用途不能为空
+            if (payBillEntity.getString("description").isEmpty()) {
+                cbsObject.put("TRSUSE", "银行转账");
+            } else {
+                cbsObject.put("TRSUSE", payBillEntity.getString("description"));
+            }
+            //金额
+            BigDecimal amount = payBillEntity.getBigDecimal("actpayamt");
+            cbsObject.put("TRSAMT", amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString());
+
+            String payInfoBody = buildPayInfoBody(payBillEntity, cbsObject);
+            String paramBody = CBSToolUtil.getBodyString(payInfoBody);
+
+            //接口开始时间
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERPAYSAV开始时间:" + sTime);
+
+            System.out.println("调用CBS接口ERPAYSAV 地址:" + cbsUrlBill);
+
+            //CBS请求日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrlBill, "payment", paramBody);
+
+            String cbsReturn = CBSToolUtil.sendPost(cbsUrlBill, paramBody);
+            System.out.println("调用CBS接口ERPAYSAV paramBody 请求值:" + paramBody);
+            //接口结束时间
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERPAYSAV结束时间:" + eTime);
+
+            //CBS返回日志
+            ParamsUtil.updateCBSLogData(uuid, cbsReturn);
+            ////CBS返回信息
+            String cbssr = CBSToolUtil.parseXMLcbs(cbsReturn);
+            System.out.println("调用CBS接口ERPAYSAV 返回XML:" + cbssr);
+
+            JSONObject cbsReturnJson = XmlUtils.documentToJSONObject(cbssr);
+            JSONArray cbsErrorJsonArray = cbsReturnJson.getJSONArray("APPAYSAVZ");
+            JSONObject cbsErrorJson = cbsErrorJsonArray.getJSONObject(0);
+            //错误码  0000000表示成功; ERR0001一般是由于链故障导致发送失败或者没有收到正确应
+            String errcode = cbsErrorJson.getString("ERRCOD");
+            if ("0000000".equals(errcode)) {
+                isSuccess = true;
+                errMsg.append("单据号:").append(billNum).append(",推送CBS成功!");
+                payBillEntity.set("nckd_iscbs", true);
+                payBillEntity.set("nckd_cbsreturnmsg", "推送CBS成功!");
+                //CBS支付状态 1 未支付 2 支付处理中 3 支付成功  4 支付失败 5 银行退票
+                payBillEntity.set("nckd_cbsstatus", 2);
+                /**
+                 * 付款单状态
+                 * A	暂存
+                 * B	已提交
+                 * C	已审核
+                 * D	已付款
+                 * E	付款处理中
+                 * F	银行退票
+                 * G	已退单
+                 * H	已作废
+                 * I	退款
+                 * J	票据处理中
+                 */
+                payBillEntity.set("billstatus", "E");
+                //提交银企时间
+                payBillEntity.set("commitbetime", new Date());
+                /**
+                 * 银行付款单状态
+                 * OP	准备提交
+                 * OS	银企处理中
+                 * BP	银行处理中
+                 * TS	交易成功
+                 * TF	交易失败
+                 * NC	交易未确认
+                 * OF	银企异常
+                 */
+                payBillEntity.set("bankpaystatus", "OS");
+                //业务流水号
+                payBillEntity.set("batchseqid", cbsErrorJson.getString("BUSNBR"));
+            } else {
+                isSuccess = false;
+                errMsg.append("单据号:").append(billNum).append(",推送CBS失败,");
+                errMsg.append("错误号:").append(errcode);
+                errMsg.append(",错误原因:").append(cbsErrorJson.getString("ERRMSG"));
+                errMsg.append("\r\n");
+            }
+
+            ////返回日志处理
+            System.out.println("调用CBS接口ERPAYSAV cbsReturnJson 返回值:" + cbsReturnJson.toString());
+
+            if (isSuccess) {
+                SaveServiceHelper.update(new DynamicObject[]{payBillEntity});
+            }
+        }
+        return errMsg.toString();
+
+    }
+
+    /**
+     * CBS支付单状态同步
+     * @params billMap
+     * @returns 返回报错信息
+     *
+     */
+    public static void payStatusForCBSSync(String cbsUrl,Map<String, String> billMap) {
+
+        String url = "http://192.168.250.225:8887";
+
+        //modify by wanghaiwu_kd 2023/08/30
+        if(StringUtils.isNotEmpty(cbsUrl)) {
+            url = cbsUrl;
+        }
+
+        if (billMap.size() > 0) {
+
+            String receiptQueryBody = getQueryBody(billMap);
+
+            String paramQueryBody = CBSToolUtil.getBodyString(receiptQueryBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERPAYSTA开始时间:" + sTime);
+
+            //请求日志处理
+            System.out.println("调用CBS接口ERPAYSTA paramQueryBody 请求值:" + paramQueryBody);
+            //CBS请求日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "payment", paramQueryBody);
+
+            String cbsReturn = CBSToolUtil.sendPost( url, paramQueryBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERPAYSTA结束时间:" + eTime);
+
+            //CBS返回日志
+            ParamsUtil.updateCBSLogData(uuid, cbsReturn);
+            ////CBS返回信息
+            String cbssr = CBSToolUtil.parseXMLcbs(cbsReturn);
+            System.out.println("调用CBS接口ERPAYSTA 返回XML:" + cbssr);
+            JSONObject cbsReturnJson = XmlUtils.documentToJSONObject(cbssr);
+            System.out.println("调用CBS接口ERPAYSTA 返回Json:" + cbsReturnJson.toString());
+
+            JSONArray cbsErrorJsonArray = cbsReturnJson.getJSONArray("ERPAYSTAZ");
+
+            Map<String, String> payStatusInfoMap = CBSToolUtil.getPayStatusMap();
+
+            for (int i = 0; i < cbsErrorJsonArray.size(); i++) {
+                JSONObject item = cbsErrorJsonArray.getJSONObject(i);
+
+                String errcode = item.getString("ERRCOD");
+                if(!"0000000".equals(errcode)){
+                    break;
+                }
+
+                //付款单号
+                String billno = item.getString("REFNBR");
+
+                //去除版本号信息
+                billno  = splitBillNo(billno,"~");
+
+                QFilter filterPay = new QFilter("billno", QCP.equals, billno);
+                QFilter[] filterPays = new QFilter[]{filterPay};
+                DynamicObject cbsPayBillEntity = BusinessDataServiceHelper.loadSingle(ENTITY_NAME, filterPays);
+
+                /**
+                 * 0查无此记录(状态可疑)
+                 * 1:支付成功
+                 * 2:支付失败
+                 * 3:未完成
+                 * 4:银行退票
+                 */
+                String payStatus = item.getString("STATUS");
+                String payRemark = item.getString("REMARK");
+                switch (payStatus) {
+                    case "0":
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "查无此记录(状态可疑)");
+                        break;
+                    case "1":
+                        //CBS支付状态 1 未支付 2 支付处理中 3 支付成功  4 支付失败 5 银行退票
+                        cbsPayBillEntity.set("nckd_cbsstatus", 3);
+                        /**
+                         * 付款单状态
+                         * A	暂存
+                         * B	已提交
+                         * C	已审核
+                         * D	已付款
+                         * E	付款处理中
+                         * F	银行退票
+                         * G	已退单
+                         * H	已作废
+                         * I	退款
+                         * J	票据处理中
+                         */
+                        cbsPayBillEntity.set("billstatus", "D");
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "支付已成功");
+                        /**
+                         * 银行付款单状态
+                         * OP	准备提交
+                         * OS	银企处理中
+                         * BP	银行处理中
+                         * TS	交易成功
+                         * TF	交易失败
+                         * NC	交易未确认
+                         * OF	银企异常
+                         */
+                        cbsPayBillEntity.set("bankpaystatus","TS");
+                        break;
+                    case "2":
+                        //CBS支付状态 1 未支付 2 支付处理中 3 支付成功  4 支付失败 5 银行退票
+                        cbsPayBillEntity.set("nckd_cbsstatus", 4);
+                        //付款单状态
+                        cbsPayBillEntity.set("billstatus", "C");
+                        cbsPayBillEntity.set("bankpaystatus","TF");
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "支付失败,"+ payRemark);
+                        break;
+                    case "3":
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "支付未完成" );
+                        break;
+                    case "4":
+                        //CBS支付状态 1 未支付 2 支付处理中 3 支付成功  4 支付失败 5 银行退票
+                        cbsPayBillEntity.set("nckd_cbsstatus", 5);
+                        //付款单状态
+                        cbsPayBillEntity.set("billstatus", "C");
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "银行退票" + payRemark);
+                        break;
+                }
+                cbsPayBillEntity.set("nckd_statusdate",new Date());
+                if (!item.getString("OPTSTU").isEmpty()) {
+                    String payStatusInfo = payStatusInfoMap.get(item.getString("OPTSTU"));
+                    cbsPayBillEntity.set("bankreturnmsg", payStatusInfo);
+
+                    //add by wanghaiwu_kd 2024/04/22
+                    //如果cbs返回结果中的OPTSTU参数=BF时,也是支付失败
+                    if("BF".equals(item.getString("OPTSTU"))){
+                        //CBS支付状态 1 未支付 2 支付处理中 3 支付成功  4 支付失败 5 银行退票
+                        cbsPayBillEntity.set("nckd_cbsstatus", 4);
+                        //付款单状态
+                        cbsPayBillEntity.set("billstatus", "C");
+                        cbsPayBillEntity.set("bankpaystatus","TF");
+                        cbsPayBillEntity.set("nckd_cbsreturnmsg", "支付失败,"+ payRemark);
+                    }
+                }
+
+                SaveServiceHelper.update(new DynamicObject[]{cbsPayBillEntity});
+
+                //add by wanghaiwu_kd 2024-08-29
+                //如果 单据状态 = 已付款,付款类型 = 资金上划 或 资金下拨 或 跨主体调拨,则调用操作生成收款单。
+                DynamicObject paymenttype = cbsPayBillEntity.getDynamicObject("paymenttype");
+                String payTypeName = paymenttype.getString("name");
+                String billStatus = cbsPayBillEntity.getString("billstatus");
+
+                if("D".equals(billStatus)
+                        && ("资金上划".equals(payTypeName) || "资金下拨".equals(payTypeName) || "跨主体调拨".equals(payTypeName))){
+                    String operateName = "pushifmandsave";
+                    OperationResult resultSave = OperationServiceHelper.executeOperate(operateName, ENTITY_NAME, new DynamicObject[]{cbsPayBillEntity}, OperateOption.create());
+                }
+            }
+
+        }
+
+    }
+
+
+
+
+    /**
+     * CBS 付款单 生成单据Body
+     * @returns
+     */
+    public static String buildPayInfoBody(DynamicObject billEntity,Map<String, String> cbsObj){
+        StringBuffer body = new StringBuffer();
+        boolean isdebug = false;
+        if (isdebug) {
+
+            body.append("<CBSERPPGK>\r\n");
+            body.append("<INFO>\r\n");
+            body.append("<FUNNAM>ERPAYSAV</FUNNAM>\r\n");
+            body.append("</INFO>\r\n");
+            body.append("<APPAYSAVX>\r\n");
+            //银行接口类型
+            body.append("<BNKTYP>CMB</BNKTYP>\r\n");
+            //业务子类型    0-标准支付
+            body.append("<BUSTYP>0</BUSTYP>\r\n");
+            //币种   10 人民币  21 港币  32 美元  65 日元 43 英镑 35 欧元
+            body.append("<CCYNBR>").append(cbsObj.get("CCYNBR")).append("</CCYNBR>\r\n");
+            //付款账号
+            body.append("<CLTACC>755915712510203</CLTACC>\r\n");
+            //付方客户号  付款账号所属企业号
+            //body.append("<CLTNBR>0003</CLTNBR>\r\n");
+            //支付日期   提交银行的付款日期 大于等于当前交日期
+            body.append("<EPTDAT>").append(cbsObj.get("EPTDAT")).append("</EPTDAT>\r\n");
+            //支付时间   提交银行的付款日期 的准确时间
+            body.append("<EPTTIM>").append(cbsObj.get("EPTTIM")).append("</EPTTIM>\r\n");
+            //摘要
+            body.append("<EXTTX1>").append(billEntity.getString("description")).append("</EXTTX1>\r\n");
+            //中心处理方式   0 其他 2 打印银行票据 3 银企直联支付 5 ERP渠道支付
+            body.append("<OPRMOD>3</OPRMOD>\r\n");
+            /** 操作类型  必填
+                202-对外支付
+                204-银行调拨
+                206-内转
+                401-集中支付
+                235-网银互联
+                236-集中网银互联
+                215-联动支付
+                251-手工上划
+                252-手工下拨
+             */
+            body.append("<OPRTYP>202</OPRTYP>\r\n");
+            //是否加急
+            body.append("<PAYSON>").append(cbsObj.get("PAYSON")).append("</PAYSON>\r\n");
+            //结算方式  0 其它 1 现金 2 转账 3 电汇 4 汇票 5 内转
+            body.append("<PAYTYP>").append(cbsObj.get("PAYTYP")).append("</PAYTYP>\r\n");
+            //内部户账号   对外支付以及集中支付业务可输入,其他务为空
+            body.append("<INNACC></INNACC>\r\n");
+            //起息日期
+            //body.append("<INTDAT></INTDAT>\r\n");
+            //预算项编号
+            //body.append("<ITMNBR></ITMNBR>\r\n");
+            //资金计划流水号
+            //body.append("<PLNNBR></PLNNBR>\r\n");
+            // 批量提交时序号不能重复 单笔提交时值
+            body.append("<RECNUM>1</RECNUM>\r\n");
+            //客户参考业务号  ERP系统唯一编号,此编号同一渠道不能重复提交   (业务单据号)
+            //body.append("<REFNBR>PV-202304-001003</REFNBR>\r\n");
+            body.append("<REFNBR>").append(cbsObj.get("REFNBR")).append("</REFNBR>\r\n");
+            //银联号
+            body.append("<BRDNBR>").append(cbsObj.get("unioncode")).append("</BRDNBR>\r\n");
+            //收款人账号  内转时为收款内部户,其他业务为收款银行账号。
+            body.append("<REVACC>755915712510402</REVACC>\r\n");
+            //收款人开户行
+            body.append("<REVBNK>招商银行深圳分行营业部</REVBNK>\r\n");
+            //收款方城市
+            body.append("<REVCIT>深圳市</REVCIT>\r\n");
+            //EMAIL地址
+            //body.append("<REVEML></REVEML>\r\n");
+            //手机号
+            body.append("<REVMOB></REVMOB>\r\n");
+            //收款人名称
+            body.append("<REVNAM>招商银行</REVNAM>\r\n");
+            //收方省份
+            body.append("<REVPRV>广东省</REVPRV>\r\n");
+            //金额
+            body.append("<TRSAMT>0.01</TRSAMT>\r\n");
+            //交易用途
+            body.append("<TRSUSE>").append(cbsObj.get("TRSUSE")).append("</TRSUSE>\r\n");
+            //收款人种类  0 - 居民机构, 1 - 居民个人
+            //body.append("<REVCAT></REVCAT>\r\n");
+            //支付类别   00 普通汇兑  10 工资  20 特别目的
+            body.append("<PAYCAT></PAYCAT>\r\n");
+            body.append("</APPAYSAVX>\r\n");
+            body.append("</CBSERPPGK>\r\n");
+        } else {
+            body.append("<CBSERPPGK>\r\n");
+            body.append("<INFO>\r\n");
+            body.append("<FUNNAM>ERPAYSAV</FUNNAM>\r\n");
+            body.append("</INFO>\r\n");
+            body.append("<APPAYSAVX>\r\n");
+            //银行接口类型
+            body.append("<BNKTYP>").append(cbsObj.get("BNKTYP")).append("</BNKTYP>\r\n");
+            //业务子类型    0-标准支付
+            body.append("<BUSTYP>0</BUSTYP>\r\n");
+            //币种   10 人民币  21 港币  32 美元  65 日元 43 英镑 35 欧元
+            body.append("<CCYNBR>").append(cbsObj.get("CCYNBR")).append("</CCYNBR>\r\n");
+            //付款账号
+            body.append("<CLTACC>").append(cbsObj.get("CLTACC")).append("</CLTACC>\r\n");
+            //付方客户号  付款账号所属企业号
+            //body.append("<CLTNBR>0003</CLTNBR>\r\n");
+            //支付日期   提交银行的付款日期 大于等于当前交日期
+            body.append("<EPTDAT>").append(cbsObj.get("EPTDAT")).append("</EPTDAT>\r\n");
+            //支付时间   提交银行的付款日期 的准确时间
+            body.append("<EPTTIM>").append(cbsObj.get("EPTTIM")).append("</EPTTIM>\r\n");
+            //摘要
+            body.append("<EXTTX1>").append(billEntity.getString("description")).append("</EXTTX1>\r\n");
+            //中心处理方式   0 其他 2 打印银行票据 3 银企直联支付 5 ERP渠道支付
+            body.append("<OPRMOD>3</OPRMOD>\r\n");
+            /** 操作类型  必填
+                202-对外支付
+                204-银行调拨
+                206-内转
+                401-集中支付
+                235-网银互联
+                236-集中网银互联
+                215-联动支付
+                251-手工上划
+                252-手工下拨
+             */
+            body.append("<OPRTYP>202</OPRTYP>\r\n");
+            //是否加急
+            body.append("<PAYSON>").append(cbsObj.get("PAYSON")).append("</PAYSON>\r\n");
+            //结算方式  0 其它 1 现金 2 转账 3 电汇 4 汇票 5 内转
+            body.append("<PAYTYP>").append(cbsObj.get("PAYTYP")).append("</PAYTYP>\r\n");
+            //内部户账号   对外支付以及集中支付业务可输入,其他务为空
+            body.append("<INNACC></INNACC>\r\n");
+            //起息日期
+            //body.append("<INTDAT></INTDAT>\r\n");
+            //预算项编号
+            //body.append("<ITMNBR></ITMNBR>\r\n");
+            //资金计划流水号
+            //body.append("<PLNNBR></PLNNBR>\r\n");
+            // 批量提交时序号不能重复 单笔提交时值
+            body.append("<RECNUM>1</RECNUM>\r\n");
+            //客户参考业务号  ERP系统唯一编号,此编号同一渠道不能重复提交   (业务单据号)
+            body.append("<REFNBR>").append(cbsObj.get("REFNBR")).append("</REFNBR>\r\n");
+            //银联号
+            body.append("<BRDNBR>").append(cbsObj.get("unioncode")).append("</BRDNBR>\r\n");
+            //收款人账号  内转时为收款内部户,其他业务为收款银行账号。
+            body.append("<REVACC>").append(billEntity.getString("payeebanknum")).append("</REVACC>\r\n");
+            //收款人开户行
+            body.append("<REVBNK>").append(billEntity.getString("payeebankname")).append("</REVBNK>\r\n");
+            //收款方城市
+            body.append("<REVCIT>").append(cbsObj.get("citytxt")).append("</REVCIT>\r\n");
+            //EMAIL地址
+            //body.append("<REVEML></REVEML>\r\n");
+            //手机号
+            body.append("<REVMOB></REVMOB>\r\n");
+
+            //如果是第三方系统生成的付款处理,取收款人名称,否则取收款人实名
+            if(StringUtils.isNotEmpty(billEntity.getString("nckd_apisourcebillno"))){
+                //收款人名称
+                body.append("<REVNAM>").append(billEntity.getString("payeename")).append("</REVNAM>\r\n");
+            } else {
+                //收款人实名
+                body.append("<REVNAM>").append(billEntity.getString("recaccbankname")).append("</REVNAM>\r\n");
+            }
+
+            //收方省份
+            body.append("<REVPRV>").append(cbsObj.get("provincetxt")).append("</REVPRV>\r\n");
+
+            //是否同城 add by wanghaiwu_kd 2024/04/11
+            if(cbsObj.get("ctyflg") != null){
+                body.append("<CTYFLG>").append(cbsObj.get("ctyflg")).append("</CTYFLG>\r\n");
+            }
+
+            //金额
+            body.append("<TRSAMT>").append(cbsObj.get("TRSAMT")).append("</TRSAMT>\r\n");
+            //交易用途
+            body.append("<TRSUSE>").append(cbsObj.get("TRSUSE")).append("</TRSUSE>\r\n");
+            //收款人种类  0 - 居民机构, 1 - 居民个人
+            //body.append("<REVCAT></REVCAT>\r\n");
+            //支付类别   00 普通汇兑  10 工资  20 特别目的
+            body.append("<PAYCAT></PAYCAT>\r\n");
+            body.append("</APPAYSAVX>\r\n");
+            body.append("</CBSERPPGK>\r\n");
+        }
+
+        return body.toString();
+    }
+
+
+    /**
+     * CBS 支付状态 查询 Body 生成
+     * @params  billMap
+     * @returns
+     */
+    public static String getQueryBody(Map<String, String> billMap) {
+        StringBuffer body = new StringBuffer();
+
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ERPAYSTA</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+
+        for (Map.Entry<String, String> billEntry : billMap.entrySet()) {
+            body.append("<ERPAYSTAX>\r\n");
+            body.append("<REFNBR>").append(billEntry.getKey()).append("</REFNBR>\r\n");
+            body.append("<BUSNBR>").append(billEntry.getValue()).append("</BUSNBR>\r\n");
+            body.append("</ERPAYSTAX>\r\n");
+        }
+
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 处理单据编号  去除版本号信息
+     * @params  str        单据号
+     * @params  splitStr   分隔字符
+     * @returns
+     */
+    public static String splitBillNo(String str,String splitStr) {
+        String ss = "";
+        int point =  str.indexOf(splitStr);
+        if(point>0){
+            ss = str.substring(0,point);
+        }else{
+            ss = str;
+        }
+
+        return ss;
+    }
+
+
+    /**
+     * 获取 所有 cbs 系统的url地址
+     * @returns cbsSystemList
+     */
+    public static List<String> cbsSystemList(){
+        List<String> cbs = new ArrayList<>();
+        String entityname = "nckd_cbssetting";
+        String selectProperties = "nckd_cbssetentry.nckd_company.id, nckd_cbssetentry.nckd_cbsurl";
+        DynamicObjectCollection objCols = QueryServiceHelper.query(entityname, selectProperties, null);
+        if(objCols.size() == 0){
+            return null;
+        }
+        for(int i= 0;i<objCols.size();i++){
+            DynamicObject dobj = objCols.get(i);
+            cbs.add(dobj.getString("nckd_cbssetentry.nckd_cbsurl"));
+        }
+
+        return cbs;
+    }
+
+    /**
+     * 获取收款银行类型
+     * @return
+     */
+    public static String getBankType(Object bebankID){
+
+        //行名行号
+        DynamicObject payeeacctbankEntity = BusinessDataServiceHelper.loadSingle(bebankID,"bd_bebank");
+
+        QFilter filter1 = new QFilter("bebank", QCP.equals, payeeacctbankEntity.getPkValue());
+        QFilter[] filters = new QFilter[]{filter1};
+        //合作金融机构
+        DynamicObject finorginfoEntity = BusinessDataServiceHelper.loadSingle("bd_finorginfo",filters);
+        DynamicObject bankCateEntity = finorginfoEntity.getDynamicObject("bank_cate");
+
+        //银行类型
+        DynamicObject bankcgsettingEntity = BusinessDataServiceHelper.loadSingle(bankCateEntity.getPkValue(), "bd_bankcgsetting");
+        String bankType  = bankcgsettingEntity.getString("nckd_cbsbanktype");
+
+        return bankType;
+    }
+
+
+}

+ 245 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/PayBillEditPlugin.java

@@ -0,0 +1,245 @@
+package kd.cosmic.jkjt.fi.cas.formplugin;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.control.Label;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.fi.cas.consts.BillTypeConstants;
+import kd.fi.cas.formplugin.PaymentBillEdit;
+import java.math.BigDecimal;
+import java.util.EventObject;
+import java.util.Map;
+
+/**
+ * @author turbo
+ *
+ * 同名转账PC布局单据显示,付款方、收款方的协定利率、协定留存金额。单据字段已添加,逻辑描述:
+ * 数据来源:协定存款单据
+ * 条件:协议状态=正常 , 收付.银行账号=协定账号
+ * 未找到记录则字段显示为空
+ */
+public class PayBillEditPlugin extends PaymentBillEdit {
+    private String AgreementDepositEntityName = "cim_agreement_deposit";
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+        setIstypeSyn();
+
+        DynamicObject payeracctbank = (DynamicObject)this.getValue("payeracctbank");
+        if(payeracctbank != null){
+            getDepositAgreeInfo(true, (Long)payeracctbank.getPkValue());
+        }
+
+    }
+
+    @Override
+    public void afterLoadData(EventObject e) {
+        super.afterLoadData(e);
+        setIstypeSyn();
+        setPayerPayeeInfoLb();
+    }
+
+    @Override
+    public void click(EventObject evt) {
+//        super.click(evt);
+    }
+
+    @Override
+    public void closedCallBack(ClosedCallBackEvent e) {
+//        super.closedCallBack(e);
+//
+//        Map data;
+//        if ("changepayer".equals(e.getActionId())) {
+//            data = (Map)e.getReturnData();
+//            if (data != null) {
+//                this.changePayerValueForDepositAgree(data);
+//            }
+//        } else if ("addpayee".equals(e.getActionId())) {
+//            data = (Map)e.getReturnData();
+//            if (data != null) {
+//                this.addPayeeValueForDepositAgree(data);
+//            }
+//        }
+    }
+
+
+    private void changePayerValueForDepositAgree(Map<String, Object> returnMap) {
+
+        if (getBillTypeSyn()) {
+            DynamicObject  payerbank = (DynamicObject ) returnMap.get("payeracctbank");
+            getDepositAgreeInfo(true, (Long)payerbank.getPkValue());
+        }
+
+    }
+
+    private void addPayeeValueForDepositAgree(Map<String, Object> returnMap) {
+
+        if (getBillTypeSyn()) {
+            Long payeebankId = (Long)returnMap.get("payeeacctbank");
+            getDepositAgreeInfo(false,payeebankId);
+        }
+
+    }
+
+    private void setIstypeSyn(){
+        if (getBillTypeSyn()) {
+            this.getView().setVisible(true, new String[]{"nckd_flex_payer_rate"});
+            this.getView().setVisible(true, new String[]{"nckd_payer_agreeaamount"});
+
+            this.getView().setVisible(true, new String[]{"nckd_flex_payee_rate"});
+            this.getView().setVisible(true, new String[]{"nckd_payee_agreeaamount"});
+        }else{
+            this.getView().setVisible(false, new String[]{"nckd_flex_payer_rate"});
+            this.getView().setVisible(false, new String[]{"nckd_payer_agreeaamount"});
+
+            this.getView().setVisible(false, new String[]{"nckd_flex_payee_rate"});
+            this.getView().setVisible(false, new String[]{"nckd_payee_agreeaamount"});
+        }
+    }
+
+    private boolean getBillTypeSyn(){
+        DynamicObject billtype = this.getDynamicObject("billtype");
+        if (billtype != null && BillTypeConstants.PAYBILL_SYN.equals(billtype.getPkValue())) {
+            return true;
+        }
+        return false;
+    }
+
+    private void  setPayerPayeeInfoLb(){
+        if (getBillTypeSyn()) {
+            if(this.getBigDecimal("nckd_payerrate")!=null) {
+                BigDecimal rate1 = this.getBigDecimal("nckd_payerrate").setScale(2,BigDecimal.ROUND_HALF_UP);
+                ((Label) this.getControl("nckd_payerbanknamev")).setText(rate1.toString());
+            }
+            if(this.getBigDecimal("nckd_payeragreeamount")!=null) {
+                BigDecimal amount1 = this.getBigDecimal("nckd_payeragreeamount").setScale(2, BigDecimal.ROUND_HALF_UP);
+                ((Label) this.getControl("nckd_payerbanknamev1")).setText(amount1.toString());
+            }
+            if(this.getBigDecimal("nckd_payeerate")!=null) {
+                BigDecimal rate2 = this.getBigDecimal("nckd_payeerate").setScale(2, BigDecimal.ROUND_HALF_UP);
+                ((Label) this.getControl("nckd_payeebanknumv")).setText(rate2.toString());
+            }
+            if(this.getBigDecimal("nckd_payeeagreeamount")!=null) {
+                BigDecimal amount2 = this.getBigDecimal("nckd_payeeagreeamount").setScale(2, BigDecimal.ROUND_HALF_UP);
+                ((Label) this.getControl("nckd_payeebanknumv1")).setText(amount2.toString());
+            }
+        }
+    }
+
+
+    /**
+     * 单据选择
+     * @param isPayer
+     * @param bankId
+     */
+    private void  getDepositAgreeInfo(boolean isPayer, Long bankId){
+
+        ///先清除原有值
+        Object value = null;
+        if(isPayer) {
+            this.setValue("nckd_payerrate", value);
+            this.setValue("nckd_payeragreeamount", value);
+
+            ((Label) this.getControl("nckd_payerbanknamev")).setText("");
+            ((Label) this.getControl("nckd_payerbanknamev1")).setText("");
+        }else{
+            this.setValue("nckd_payeerate", value);
+            this.setValue("nckd_payeeagreeamount", value);
+
+            ((Label) this.getControl("nckd_payeebanknumv")).setText("");
+            ((Label) this.getControl("nckd_payeebanknumv1")).setText("");
+        }
+
+        if(bankId == null) {
+            return;
+        }
+
+        QFilter filterAgreement1 = new QFilter("bankacct", QCP.equals, bankId);
+        QFilter filterAgreement2 = new QFilter("status", QCP.equals, "A");
+        QFilter filterAgreement3 = new QFilter("billstatus", QCP.equals, "C");
+        QFilter[] filtersAgreeement = new QFilter[]{filterAgreement1.and(filterAgreement2).and(filterAgreement3)};
+        DynamicObject[] agreementBillEntities = BusinessDataServiceHelper.load(AgreementDepositEntityName, "id,billno", filtersAgreeement);
+
+        for (DynamicObject dataEntity : agreementBillEntities) {
+            DynamicObject bankBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), AgreementDepositEntityName);
+
+            if(isPayer) {
+
+                this.setValue("nckd_payerrate", bankBillEntity.getBigDecimal("rate"));
+                this.setValue("nckd_payeragreeamount", bankBillEntity.getBigDecimal("amount"));
+
+                if(this.getBigDecimal("nckd_payerrate")!=null) {
+                    BigDecimal rate1 = this.getBigDecimal("nckd_payerrate").setScale(2,BigDecimal.ROUND_HALF_UP);
+                    ((Label) this.getControl("nckd_payerbanknamev")).setText(rate1.toString());
+                }
+                if(this.getBigDecimal("nckd_payeragreeamount")!=null) {
+                    BigDecimal amount1 = this.getBigDecimal("nckd_payeragreeamount").setScale(2, BigDecimal.ROUND_HALF_UP);
+                    ((Label) this.getControl("nckd_payerbanknamev1")).setText(amount1.toString());
+                }
+
+            }else{
+                this.setValue("nckd_payeerate", bankBillEntity.getBigDecimal("rate"));
+                this.setValue("nckd_payeeagreeamount", bankBillEntity.getBigDecimal("amount"));
+
+
+                if(this.getBigDecimal("nckd_payeerate")!=null) {
+                    BigDecimal rate2 = this.getBigDecimal("nckd_payeerate").setScale(2, BigDecimal.ROUND_HALF_UP);
+                    ((Label) this.getControl("nckd_payeebanknumv")).setText(rate2.toString());
+                }
+                if(this.getBigDecimal("nckd_payeeagreeamount")!=null) {
+                    BigDecimal amount2 = this.getBigDecimal("nckd_payeeagreeamount").setScale(2, BigDecimal.ROUND_HALF_UP);
+                    ((Label) this.getControl("nckd_payeebanknumv1")).setText(amount2.toString());
+                }
+            }
+        }
+
+
+    }
+
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        String key = e.getProperty().getName();
+        ChangeData[] changeData = e.getChangeSet();
+        Object newValue = changeData[0].getNewValue();
+        Object oldValue = changeData[0].getOldValue();
+        if (newValue != oldValue) {
+            switch (key) {
+                //收款银行
+                case "payeebank":
+                    if(newValue == null){
+                        this.getModel().setValue("nckd_payeiscapitalpool", false);
+                    } else {
+                        DynamicObject bebank = (DynamicObject) newValue;
+                        QFilter qFilter = new QFilter("bebank", QCP.equals, bebank.getLong("id"));
+                        DynamicObject bank = BusinessDataServiceHelper.loadSingle("bd_finorginfo", qFilter.toArray());
+                        if(bank != null && bank.getDynamicObject("bank_cate") != null){
+                            DynamicObject bankCate = bank.getDynamicObject("bank_cate");
+                            bankCate = BusinessDataServiceHelper.loadSingle(bankCate.getPkValue(), "bd_bankcgsetting");
+                            this.getModel().setValue("nckd_payeiscapitalpool", bankCate.getBoolean("nckd_iscapitalpoolbank"));
+                        }
+                    }
+                    break;
+                //银行账号
+                case "payeracctbank":
+                    if (getBillTypeSyn()) {
+                        DynamicObject payerbank = this.getDynamicObject("payeracctbank");
+                        Long payeebankId = payerbank == null ? null : (Long)payerbank.getPkValue();
+                        getDepositAgreeInfo(true, payeebankId);
+                    }
+                    break;
+                //收款账户ID
+                case "payeeacctbank":
+                    if (getBillTypeSyn()) {
+                        Long payeebankId = this.getValue("payeeacctbank") == null ? null : (Long)this.getValue("payeeacctbank");
+                        getDepositAgreeInfo(false,payeebankId);
+                    }
+                    break;
+            }
+        }
+    }
+}

+ 50 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/PayBillListPlugin.java

@@ -0,0 +1,50 @@
+package kd.cosmic.jkjt.fi.cas.formplugin;
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.list.plugin.AbstractListPlugin;
+import com.alibaba.druid.util.StringUtils;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+
+import java.util.List;
+
+
+/**
+ * @author turborao
+ * @date 2023/04/27
+ * @description    付款单列表插件 处理支付 列表插件
+ */
+public class PayBillListPlugin extends AbstractListPlugin {
+
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+        if(StringUtils.equals("cbspay",afterDoOperationEventArgs.getOperateKey())){
+            OperationResult operationResult = afterDoOperationEventArgs.getOperationResult();
+            operationResult.setShowMessage(false);
+            String message = operationResult.getMessage();
+
+            List<IOperateInfo> errorInfos = operationResult.getAllErrorOrValidateInfo();
+            boolean isSuccess = operationResult.isSuccess();
+            if(errorInfos.size()>0){
+                operationResult.setSuccess(false);
+                StringBuffer msg = new StringBuffer();
+                for (IOperateInfo errInfo: errorInfos) {
+                    msg.append(errInfo.getMessage()).append("\r\n");
+                }
+                message = message + "\r\n" + msg.toString();
+            }
+
+            this.getView().showTipNotification(message);
+        }
+        if(StringUtils.equals("pay",afterDoOperationEventArgs.getOperateKey())){
+
+        }
+        if(StringUtils.equals("cancelpay",afterDoOperationEventArgs.getOperateKey())){
+
+        }
+    }
+}

+ 308 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillImageEditPlugin.java

@@ -0,0 +1,308 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.IClientViewProxy;
+import kd.bos.form.IFormView;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.siit.SiitServerHandler;
+import kd.fi.cas.util.EmptyUtil;
+
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/03
+ * 插件说明:付款处理表单插件,增加影像系统的入口
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PayBillImageEditPlugin extends AbstractBillPlugIn {
+    private static final Log logger = LogFactory.getLog(PayBillImageEditPlugin.class);
+
+    private static final String KEY_BTN_Updateimage = "nckd_updateimage";
+    private static final String KEY_BTN_Viewimage = "nckd_viewimage";
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        this.addItemClickListeners(new String[]{"tbsave"});
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+
+        String itemKey = evt.getItemKey();
+        if (KEY_BTN_Updateimage.equals(itemKey)) {
+            this.getUpdateImageUrl();
+        }
+        if (KEY_BTN_Viewimage.equals(itemKey)) {
+            this.getViewImageUrl();
+        }
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        IFormView view = this.getView();
+        IDataModel m = this.getModel();
+        String billstatus = m.getDataEntity().getString("billstatus");
+
+        if (billstatus.matches("D")) {
+            view.setVisible(false, new String[]{"nckd_updateimage"});
+        } else{
+            view.setVisible(true, new String[]{"nckd_updateimage"});
+        }
+    }
+
+    /**
+     * 获取barCode
+     * 根据条件设置barcode:如果描述不为空,并且nckd_srcbillno做为barcode
+     * @param id
+     * @return
+     */
+    public String getBarCode(String id){
+        String barCode = "";
+
+        DynamicObject paymentInfo = BusinessDataServiceHelper.loadSingle(id, "cas_paybill");
+        barCode = paymentInfo.getString("nckd_srcbillno");
+
+        return barCode;
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs evt) {
+        super.afterDoOperation(evt);
+
+        String name = evt.getOperateKey();
+        OperationResult operationResult = evt.getOperationResult();
+        if (name.equals("delete") && operationResult.isSuccess()) {
+            this.deleteBillImage();
+        }
+    }
+
+    /**
+     * 查看影像
+     */
+    private void getViewImageUrl(){
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+            this.getView().showErrorNotification("请先保存付款处理!");
+            return;
+        }
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+
+        if(!EmptyUtil.isEmpty(id)){
+            //add by wnaghaiwu_kd 2023/12/05
+            String barCode = getBarCode(id.toString());
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+            }
+
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            JSONObject jsonCreateImagePath = SiitServerHandler.getCreateImagePath(barCode,username);
+
+            JSONObject jsonImagePath = new JSONObject();
+            jsonImagePath.put("safety",jsonSafety);
+            jsonImagePath.put("params",jsonCreateImagePath);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            /**
+             * 调用获取票夹页面地址接口
+             */
+            logger.info("Siit Log" + jsonImagePath.toJSONString());
+            String uri = SiitServerHandler.url + "/siitservice/createImagePathApi/createImagePath";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonImagePath.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(appTokenJ1.getString("status").equals("true")){
+                openUrl(appTokenJ1.getString("data"));
+            } else{
+                if("Service_createImagePath_00002".equals(appTokenJ1.getString("errorCode"))){
+                    this.getView().showErrorNotification("未上传影像,无法查看!");
+                }else {
+                    this.getView().showErrorNotification("查看异常!影像返回信息:"  + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                }
+            }
+        }
+    }
+
+    /**
+     * 上传影像
+     */
+    private void getUpdateImageUrl(){
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+            this.getView().showErrorNotification("请先保存付款处理!");
+            return;
+        }
+        DynamicObject org =  (DynamicObject)this.getModel().getValue("org");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+        if(!EmptyUtil.isEmpty(id)){
+            String barCode = getBarCode(id.toString());
+            String uri = "";
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+
+                if(!isExists(Long.valueOf(String.valueOf(id)), "flow")) {
+                    JSONObject jsonFlow = SiitServerHandler.getFlowParams(barCode, org.getString("number"), username, "");
+                    //JSONObject jsonCreateImagePath = SiitServerHandler.getCreateImagePath(barCode);
+
+                    //触发扫描接口调入参数
+                    JSONObject jsonFlowPost = new JSONObject();
+                    jsonFlowPost.put("safety", jsonSafety);
+                    jsonFlowPost.put("params", jsonFlow);
+
+                    //对于以凭证id为barCode的凭证,需要先要触发扫描接口(待办),以上游业务单据为barCode的凭证不需要触发待办
+                    logger.info("Siit Log" + jsonFlowPost.toJSONString());
+                    uri = SiitServerHandler.url + "/siitservice/startFlow/addFlow";
+                    String appReturnStr = HttpUtilAction.doPost(uri, jsonFlowPost.toJSONString(), headers);
+                    JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+                    logger.info("Siit Log" + appTokenJO.toString());
+
+                    if (!appTokenJO.getString("status").equals("true")) {
+                        this.getView().showErrorNotification("上传异常!影像返回信息:" + (appTokenJO == null ? "" : appTokenJO.toString()));
+                        return;
+                    } else {
+                        //触发待办成功,记录触发日志
+                        saveFlowLog(Long.valueOf(String.valueOf(id)), "flow");
+                    }
+                }
+            }
+
+            JSONObject jsonPageUrl = SiitServerHandler.getPageUrl(barCode,username);
+            //获取票夹页面地址调入参数
+            JSONObject jsonPagePost = new JSONObject();
+            jsonPagePost.put("safety",jsonSafety);
+            jsonPagePost.put("params",jsonPageUrl);
+
+            /**
+             * 调用获取票夹页面地址接口
+             */
+            logger.info("Siit Log" +jsonPagePost.toJSONString());
+            uri = SiitServerHandler.url + "/siitservice/ticketpage/getPageUrl";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonPagePost.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(appTokenJ1.getString("status").equals("true")){
+                openUrl( appTokenJ1.getString("data"));
+            } else {
+                this.getView().showErrorNotification("调用获取票夹页面接口异常!影像返回信息:" + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                return;
+            }
+        }
+    }
+
+    private boolean isExists(Long voucherId, String operatorType){
+        boolean isExists = false;
+        QFilter qFilter = new QFilter("nckd_voucherid", QCP.equals, voucherId);
+        qFilter.and(new QFilter("nckd_billtype", QCP.equals, "cas_paybill"));
+        qFilter.and(new QFilter("nckd_operatortype", QCP.equals, operatorType));
+
+        DynamicObject flowLog = BusinessDataServiceHelper.loadSingle("nckd_sitflowlog", qFilter.toArray());
+        if(flowLog != null){
+            isExists = true;
+        }
+
+        return isExists;
+    }
+
+    private void saveFlowLog(Long voucherId, String operatorType){
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_sitflowlog");
+
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("creator", RequestContext.get().getCurrUserId());
+        dynamicObject.set("modifier", RequestContext.get().getCurrUserId());
+        dynamicObject.set("nckd_billtype", "cas_paybill");
+        dynamicObject.set("nckd_voucherid", voucherId);
+        dynamicObject.set("nckd_operatortype", operatorType);
+
+//        dynamicObject.set("nckd_sendtime", new Date());
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(dynamicObject.getDataEntityType().getName(), dynamicObject, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, dynamicObject);
+
+        if(StringUtils.isEmpty(billno)){
+            billno = UUID.randomUUID().toString().replace("-", "");
+            billno = (billno == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : billno;
+        }
+        dynamicObject.set("number", billno);
+
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+    }
+
+    private void deleteBillImage(){
+        Object id = this.getModel().getValue("id");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+        if(!EmptyUtil.isEmpty(id)){
+            String barCode = getBarCode(id.toString());
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+            }
+
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            JSONObject jsonDeleteBill = SiitServerHandler.deleteBill(barCode,username);
+
+            JSONObject jsonDeletePost = new JSONObject();
+            jsonDeletePost.put("safety",jsonSafety);
+            jsonDeletePost.put("params",jsonDeleteBill);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            /**
+             * 调用作废接口
+             */
+            logger.info("Siit Log" +jsonDeletePost.toJSONString());
+            String uri = SiitServerHandler.url + "/siitservice/todeleteApi/deleteBill";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonDeletePost.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(!appTokenJ1.getString("status").equals("true")){
+                this.getView().showErrorNotification("作废异常!影像返回信息:" + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                return;
+            }
+        }
+    }
+
+    public void openUrl(String url){
+        String openurl = url;
+        IClientViewProxy proxy = this.getView().getService(IClientViewProxy.class);
+        Map<String, String> mpURL = new HashMap<String, String>();
+        mpURL.put("url", openurl);
+        proxy.addAction("openUrl", mpURL);
+    }
+}

+ 66 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillViewVouInfoEditPlugin.java

@@ -0,0 +1,66 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+
+import java.math.BigDecimal;
+import java.util.EventObject;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/09/23
+ * 插件说明:付款处理表单插件,显示凭证字段信息
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PayBillViewVouInfoEditPlugin extends AbstractBillPlugIn {
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+//            this.getView().showErrorNotification("请先保存凭证!");
+            return;
+        }
+
+        if(this.getModel().getValue("nckd_voucherid") == null){
+            return;
+        }
+
+        Long voucherId = Long.valueOf(this.getModel().getValue("nckd_voucherid").toString());
+        if(voucherId == 0L){
+            this.getView().setVisible(false, "nckd_voucher");
+            return;
+        }
+
+        QFilter qFilter = new QFilter("id", QCP.equals, voucherId);
+
+        DynamicObject voucher = BusinessDataServiceHelper.loadSingle("gl_voucher", "id, entries, entries.account, entries.edescription, entries.creditlocal", qFilter.toArray());
+        if(voucher == null){
+            return;
+        }
+
+        DynamicObjectCollection vouEntrys = voucher.getDynamicObjectCollection("entries");
+        if(vouEntrys.size() > 0){
+            this.getModel().beginInit();
+
+            for(DynamicObject entry : vouEntrys){
+                DynamicObject account = entry.getDynamicObject("account");
+                BigDecimal creditlocal = entry.getBigDecimal("creditlocal");
+
+                //取科目为银行存款(1002)的贷方分录摘要。
+                if("1002".equals(account.getString("number")) && creditlocal.compareTo(BigDecimal.ZERO) != 0){
+                    this.getModel().setValue("nckd_edescription", entry.getString("edescription"));
+                    break;
+                }
+            }
+
+            this.getModel().endInit();
+
+            this.getView().updateView("nckd_edescription");
+        }
+    }
+}

+ 66 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillViewVouInfoMobEditPlugin.java

@@ -0,0 +1,66 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import kd.bos.bill.AbstractMobBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+
+import java.math.BigDecimal;
+import java.util.EventObject;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/09/23
+ * 插件说明:移动端表单插件,显示凭证字段信息
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PayBillViewVouInfoMobEditPlugin extends AbstractMobBillPlugIn {
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+//            this.getView().showErrorNotification("请先保存凭证!");
+            return;
+        }
+
+        if(this.getModel().getValue("nckd_voucherid") == null){
+            return;
+        }
+
+        Long voucherId = Long.valueOf(this.getModel().getValue("nckd_voucherid").toString());
+        if(voucherId == 0L){
+            this.getView().setVisible(false, "nckd_voucher");
+            return;
+        }
+
+        QFilter qFilter = new QFilter("id", QCP.equals, voucherId);
+
+        DynamicObject voucher = BusinessDataServiceHelper.loadSingle("gl_voucher", "id, entries, entries.account, entries.edescription, entries.creditlocal", qFilter.toArray());
+        if(voucher == null){
+            return;
+        }
+
+        DynamicObjectCollection vouEntrys = voucher.getDynamicObjectCollection("entries");
+        if(vouEntrys.size() > 0){
+            this.getModel().beginInit();
+
+            for(DynamicObject entry : vouEntrys){
+                DynamicObject account = entry.getDynamicObject("account");
+                BigDecimal creditlocal = entry.getBigDecimal("creditlocal");
+
+                //取科目为银行存款(1002)的贷方分录摘要。
+                if("1002".equals(account.getString("number")) && creditlocal.compareTo(BigDecimal.ZERO) != 0){
+                    this.getModel().setValue("nckd_edescription", entry.getString("edescription"));
+                    break;
+                }
+            }
+
+            this.getModel().endInit();
+
+            this.getView().updateView("nckd_edescription");
+        }
+    }
+}

+ 52 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillVoucherViewEditPlugin.java

@@ -0,0 +1,52 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.bill.BillShowParameter;
+import kd.bos.bill.OperationStatus;
+import kd.bos.form.IFormView;
+import kd.bos.form.ShowType;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/07
+ * 插件说明:付款处理表单插件,增加财辅凭证显示
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PayBillVoucherViewEditPlugin extends AbstractBillPlugIn {
+    private static final Log logger = LogFactory.getLog(PayBillVoucherViewEditPlugin.class);
+
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+//            this.getView().showErrorNotification("请先保存凭证!");
+            return;
+        }
+
+        if(this.getModel().getValue("nckd_voucherid") == null){
+            return;
+        }
+
+        Long voucherId = Long.valueOf(this.getModel().getValue("nckd_voucherid").toString());
+        if(voucherId == 0L){
+            this.getView().setVisible(false, "nckd_voucher");
+            return;
+        }
+
+        IFormView view = this.getView();
+        BillShowParameter form = new BillShowParameter();
+
+        form.setFormId("gl_voucher");
+        form.setPkId(voucherId);
+        form.setCustomParam("nckd_paymentviewvoucher", true);
+        form.getOpenStyle().setShowType(ShowType.InContainer);
+        form.getOpenStyle().setTargetKey("nckd_voucher");
+        form.setStatus(OperationStatus.VIEW);
+
+        view.showForm(form);
+    }
+}

+ 133 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PayBillVoucherViewMobEditPlugin.java

@@ -0,0 +1,133 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bill.AbstractMobBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.form.IClientViewProxy;
+import kd.bos.form.control.Button;
+import kd.bos.form.control.Control;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.siit.SiitServerHandler;
+import kd.fi.cas.util.EmptyUtil;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/08
+ * 插件说明:付款处理表单移动端插件,增加影像系统的入口
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PayBillVoucherViewMobEditPlugin extends AbstractMobBillPlugIn {
+    private static final Log logger = LogFactory.getLog(PayBillVoucherViewMobEditPlugin.class);
+
+    private static final String KEY_BTN_VIEWIMG = "nckd_viewimage";
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+//        this.addItemClickListeners("nckd_viewimage");
+        // 按钮点击
+        Button button = this.getView().getControl(KEY_BTN_VIEWIMG);
+        button.addClickListener(this);
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+    }
+
+
+
+    @Override
+    public void click(EventObject evt) {
+        super.click(evt);
+
+        Control source = (Control)evt.getSource();
+        if (KEY_BTN_VIEWIMG.equals(source.getKey())){
+            this.getViewImageUrl();
+        }
+    }
+
+    /**
+     * 查看影像
+     */
+    private void getViewImageUrl(){
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+            this.getView().showErrorNotification("请先保存付款处理!");
+            return;
+        }
+        DynamicObject userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+
+        if(!EmptyUtil.isEmpty(id)){
+            //add by wnaghaiwu_kd 2023/12/05
+            String barCode = getBarCode(id.toString());
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+            }
+
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            JSONObject jsonCreateImagePath = SiitServerHandler.getCreateAppImagePath(barCode,username);
+
+            JSONObject jsonImagePath = new JSONObject();
+            jsonImagePath.put("safety",jsonSafety);
+            jsonImagePath.put("params",jsonCreateImagePath);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            /**
+             * 调用获取票夹页面地址接口
+             */
+            logger.info("Siit Log" + jsonImagePath.toJSONString());
+            String uri = SiitServerHandler.url + "/siitservice/createAppImagePathApi/createImagePath";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonImagePath.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(appTokenJ1.getString("status").equals("true")){
+                openUrl(appTokenJ1.getString("data"));
+            } else{
+                if("Service_createImagePath_00002".equals(appTokenJ1.getString("errorCode"))){
+                    this.getView().showErrorNotification("未上传影像,无法查看!");
+                } else {
+                    this.getView().showErrorNotification("查看异常!影像返回信息:"  + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取barCode
+     * 根据条件设置barcode:如果描述不为空,并且nckd_srcbillno做为barcode
+     * @param id
+     * @return
+     */
+    public String getBarCode(String id){
+        String barCode = "";
+
+        DynamicObject paymentInfo = BusinessDataServiceHelper.loadSingle(id, "cas_paybill");
+        barCode = paymentInfo.getString("nckd_srcbillno");
+
+        return barCode;
+    }
+
+    public void openUrl(String url){
+//        this.getView().openUrl(url);
+        String openurl = url;
+        IClientViewProxy proxy = this.getView().getService(IClientViewProxy.class);
+        Map<String, String> mpURL = new HashMap<String, String>();
+        mpURL.put("url", openurl);
+        mpURL.put("openStyle", "0");
+        proxy.addAction("openUrl", mpURL);
+    }
+}

+ 37 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PaysynoApplyEditPlugin.java

@@ -0,0 +1,37 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.i18n.mservice.I18nServiceHelper;
+import kd.bos.i18n.mservice.utils.AmountConvertResult;
+
+import java.math.BigDecimal;
+
+/**
+ * 插件说明:表单插件
+ * 表单标识:财务云-资金结算-事前申请(nckd_cas_paysynoapply)
+ * @author wanghaiwu_kd
+ * @date 2023/12/15
+ */
+public class PaysynoApplyEditPlugin  extends AbstractBillPlugIn {
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        String name = e.getProperty().getName();
+
+        if ("nckd_payamount".equals(name)) {
+            //转账总额转换为大写金额
+            BigDecimal payeeamount = (BigDecimal)this.getModel().getValue("nckd_payamount");
+            String langAbbrCode = "ZH";
+            String currencyNumber = "CNY";
+            if(this.getModel().getValue("nckd_payeecurrency") != null){
+                currencyNumber = ((DynamicObject)this.getModel().getValue("nckd_payeecurrency")).getString("number");
+            }
+            String isTransCurrNum = "true";
+            AmountConvertResult result = I18nServiceHelper.amountConvertUppercase(langAbbrCode, currencyNumber, payeeamount.toString(), isTransCurrNum);
+            if(result != null && result.isSuccess()){
+                this.getModel().setValue("nckd_capitalamount", result.getResult());
+            }
+        }
+    }
+}

+ 39 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/pay/PaysynoApplyF7ListPlugin.java

@@ -0,0 +1,39 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.pay;
+
+import kd.bos.form.events.FilterContainerInitArgs;
+import kd.bos.form.events.SetFilterEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 插件说明:表单插件,处理f7过滤问题
+ * 表单标识:财务云-资金结算-事前申请(nckd_cas_paysynoapplyf7)
+ * @author wanghaiwu_kd
+ * @date 2023/12/15
+ */
+public class PaysynoApplyF7ListPlugin extends AbstractListPlugin {
+    @Override
+    public void setFilter(SetFilterEvent e) {
+        super.setFilter(e);
+        List<QFilter> filters = e.getQFilters();
+        if (!filters.isEmpty()) {
+            for (int index = filters.size() - 1; index >= 0; --index) {
+                if (((QFilter) filters.get(index)).getProperty().equals("enable")
+                        || ((QFilter) filters.get(index)).getProperty().equals("status")) {
+                    filters.remove(index);
+                }
+            }
+        }
+        Map<String, Object> customParams = this.getView().getFormShowParameter().getCustomParams();
+        String status = customParams.get("nckd_status") == null ? "" : customParams.get("nckd_status").toString();
+        e.addCustomQFilter(new QFilter("status", QCP.equals, status));
+    }
+
+    @Override
+    public void filterContainerInit(FilterContainerInitArgs args) {
+        super.filterContainerInit(args);
+    }
+}

+ 103 - 0
main/java/kd/cosmic/jkjt/fi/cas/formplugin/transferapply/TransferApplyEditPlugin.java

@@ -0,0 +1,103 @@
+package kd.cosmic.jkjt.fi.cas.formplugin.transferapply;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.i18n.mservice.I18nServiceHelper;
+import kd.bos.i18n.mservice.utils.AmountConvertResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.servicehelper.org.OrgViewType;
+import kd.bos.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.EventObject;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/11/22
+ * @description 表单插件:财务云-调拨申请(cas_transferapply)
+ */
+public class TransferApplyEditPlugin extends AbstractBillPlugIn {
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+
+        IDataModel model = this.getModel();
+
+        DynamicObject openOrg = (DynamicObject) model.getValue("applyorg");
+        if(openOrg != null){
+            setDefaultValueByOpenOrgChanage(openOrg);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        String name = e.getProperty().getName();
+
+        if ("payeeamount".equals(name)) {
+            //转账总额转换为大写金额
+            BigDecimal payeeamount = (BigDecimal)this.getModel().getValue("payeeamount");
+            String langAbbrCode = "ZH";
+            String currencyNumber = "CNY";
+            if(this.getModel().getValue("payeecurrency") != null){
+                currencyNumber = ((DynamicObject)this.getModel().getValue("payeecurrency")).getString("number");
+            }
+            String isTransCurrNum = "false";
+            AmountConvertResult result = I18nServiceHelper.amountConvertUppercase(langAbbrCode, currencyNumber, payeeamount.toString(), isTransCurrNum);
+            if(result != null && result.isSuccess()){
+                this.getModel().setValue("nckd_capitalamount", result.getResult());
+            }
+        } else if("applyorg".equals(name)){
+            // 获取修改数据
+            ChangeData[] changeData = e.getChangeSet();
+            DynamicObject newValue = (DynamicObject)changeData[0].getNewValue();
+            Boolean haveFirstOrg = this.getModel().getDataEntityType().getProperties().containsKey("nckd_firstlevelorg");
+            if(haveFirstOrg) {
+                setDefaultValueByOpenOrgChanage(newValue);
+            }
+        }
+    }
+
+    private void setDefaultValueByOpenOrgChanage(DynamicObject openOrg){
+        IDataModel model = this.getModel();
+
+        model.setValue("nckd_firstlevelorg", null);
+        model.setValue("nckd_firstorglongno", null);
+
+        if(openOrg != null){
+            List<Long> parentIds = OrgUnitServiceHelper.getAllSuperiorOrgs(OrgViewType.Bankroll, openOrg.getLong("id"));
+            if(!parentIds.contains(openOrg.getLong("id"))){
+                parentIds.add(openOrg.getLong("id"));
+            }
+            QFilter filterView = new QFilter("view.id", QCP.equals, 8L);
+            QFilter filteraOrg = new QFilter("org.id", QCP.in, parentIds);
+            QFilter[] filters = new QFilter[]{filterView, filteraOrg};
+            String selectProperties = "org, number, longnumber";
+            String orderBy = "longnumber desc";
+            DynamicObject[] orgList = BusinessDataServiceHelper.load("bos_org_structure", selectProperties, filters, orderBy);
+            for(int i = 0; i < orgList.length; i++){
+                DynamicObject treeOrg = orgList[i];
+                Long orgId = treeOrg.getLong("org.id");
+                String longnumber = treeOrg.getString("longnumber");
+                DynamicObject org = BusinessDataServiceHelper.loadSingle(orgId, "bos_org");
+
+                if(org.getDynamicObject("orgpattern") != null){
+                    String pattern = org.getDynamicObject("orgpattern").getString("name");
+                    if(StringUtils.isNotEmpty(pattern) && pattern.contains("一级子公司")){
+                        model.setValue("nckd_firstlevelorg", org);
+                        model.setValue("nckd_firstorglongno", longnumber);
+                        break;
+                    }
+                }
+            }
+
+            this.getView().updateView("nckd_firstlevelorg");
+        }
+    }
+}

+ 102 - 0
main/java/kd/cosmic/jkjt/fi/cas/opplugin/PayBillForCBSOpPlugin.java

@@ -0,0 +1,102 @@
+package kd.cosmic.jkjt.fi.cas.opplugin;
+
+import com.alibaba.druid.util.StringUtils;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.cosmic.jkjt.fi.cas.common.PayBillToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.sdk.plugin.Plugin;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author turborao
+ * @date 2023/04/26
+ * @description    付款单支付插件    操作插件
+ */
+public class PayBillForCBSOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    private static final String ENTITY_NAME = "cas_paybill";
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+
+        String errMsg = "";
+
+        //获取选中行
+        DynamicObject[] billEntities = e.getDataEntities();
+
+        //获取操作按钮操作编码
+        String operationKey = e.getOperationKey();
+        if (StringUtils.equals( "cbspay",operationKey)){
+           errMsg =  PayBillToolUtil.payBillForCBSSync(billEntities);
+
+        }else if (StringUtils.equals( "dotest",operationKey)){
+
+            //获取多套CBS系统地址
+            List<String> cbsSysList =  PayBillToolUtil.cbsSystemList();
+            Map<String, Map<String, String>> cbsMap = new HashMap<>();
+
+            for(String cbsUrl : cbsSysList) {
+
+                Map<String, String> billMap = new HashMap<>();
+
+                for (DynamicObject dataEntity : billEntities) {
+                    DynamicObject payBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), ENTITY_NAME);
+
+                    String billno = payBillEntity.getString("billno");
+                    String batchno = payBillEntity.getString("batchseqid");
+
+                    ///处理支付单版本号,避免重复推送CBS支付数据
+//                    if (payBillEntity.getInt("nckd_payversion") > 0) {
+//                        StringBuffer billNumCBS = new StringBuffer();
+//                        billNumCBS.append(billno).append("~").append(payBillEntity.getInt("nckd_payversion"));
+//                        billno = billNumCBS.toString();
+//                    }
+
+
+                    ///当cbs状态为处理中的,才去获取返回
+                    String cbsStatus =  payBillEntity.getString("nckd_cbsstatus");
+                    if(!"2".equals(cbsStatus)){
+                        continue;
+                    }
+
+                    ///多CBS处理
+                    String cbsUrlBill = ParamsUtil.getCBSURL(payBillEntity.getDynamicObject("openorg").getLong("id"));
+
+                    if(cbsUrl.equals(cbsUrlBill)) {
+                        billMap.put(billno, batchno);
+                    }
+                }
+                cbsMap.put(cbsUrl,billMap);
+
+            }
+
+            for (Map.Entry<String, Map<String, String>> billMaps : cbsMap.entrySet()) {
+                String url  = billMaps.getKey();
+                Map<String, String> billMap = billMaps.getValue();
+                PayBillToolUtil.payStatusForCBSSync(url,billMap);
+            }
+
+        }
+
+        if(!errMsg.isEmpty()) {
+            ////将错误信息返回到前端
+            e.setCancelMessage(errMsg);
+            e.setCancel(true);
+            System.out.println("PayBillForCBSOpPlugin 错误信息:" + errMsg.toString());
+        }
+
+        super.beforeExecuteOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+
+    }
+
+}

+ 42 - 0
main/java/kd/cosmic/jkjt/fi/cas/opplugin/PaymentBeforeCommitCusOpPlugin.java

@@ -0,0 +1,42 @@
+package kd.cosmic.jkjt.fi.cas.opplugin;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.validate.AbstractValidator;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/07
+ * 插件说明:操作插件
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PaymentBeforeCommitCusOpPlugin extends AbstractOperationServicePlugIn {
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+
+        e.addValidator(new AbstractValidator() {
+            @Override
+            public void validate() {
+                String opKey = this.getOperateKey();
+                //提交银企操作增加判断逻辑,如果结算方式不是银企直连(JSFS00),不允许提交银企
+                if("commitbe".equals(opKey)) {
+                    ExtendedDataEntity[] dataEntitys = this.getDataEntities();
+                    for (ExtendedDataEntity dataEntity : dataEntitys) {
+                        if(dataEntity == null){
+                            continue;
+                        }
+                        DynamicObject paymentBill = dataEntity.getDataEntity();
+                        DynamicObject settleType = paymentBill.getDynamicObject("settletype");
+
+                        if(settleType != null && !"JSFS00".equals(settleType.getString("number"))){
+                            this.addErrorMessage(dataEntity, "结算方式不是银企直连,不允许提交银企!");
+                        }
+                    }
+                }
+            }
+        });
+    }
+}

+ 83 - 0
main/java/kd/cosmic/jkjt/fi/cas/opplugin/PaymentWriteFeeApplyOpPlugin.java

@@ -0,0 +1,83 @@
+package kd.cosmic.jkjt.fi.cas.opplugin;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/07/04
+ * 插件说明:回写资金费用报账
+ * 表单标识:付款处理(nckd_cas_paybill_ext)
+ */
+public class PaymentWriteFeeApplyOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(PaymentWriteFeeApplyOpPlugin.class);
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+
+        String opKey = e.getOperationKey();
+
+        //确认付款、取消付款时更新资金费用报账单状态
+        if(opKey.equals("pay") || opKey.equals("cancelpay")){
+
+            updateFeeApplyPayStatus(e, opKey);
+        }
+    }
+
+    private void updateFeeApplyPayStatus(AfterOperationArgs e, String opKey){
+        DynamicObject[] dataEntities = e.getDataEntities();
+        List<DynamicObject> listObj = new ArrayList<>();
+        boolean payStatus = true;
+        if(opKey.equals("cancelpay")){
+            payStatus = false;
+        }
+
+        for (DynamicObject obj : dataEntities) {
+            obj = BusinessDataServiceHelper.loadSingle(obj.getPkValue(), obj.getDynamicObjectType().getName());
+            Long sourcebillid = obj.getLong("sourcebillid");
+            if(sourcebillid.compareTo(0L) == 0){
+                continue;
+            }
+
+            QFilter qFilter = new QFilter("id", QCP.equals, sourcebillid);
+            DynamicObject sourceDO = BusinessDataServiceHelper.loadSingle("nckd_fundfeeapply", qFilter.toArray());
+
+            if(sourceDO != null){
+                sourceDO.set("nckd_isallpay", payStatus);
+
+                listObj.add(sourceDO);
+            }
+        }
+
+        if(listObj.size() > 0){
+            SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+        }
+    }
+}

+ 58 - 0
main/java/kd/cosmic/jkjt/fi/cas/task/AutoAccountbankFreezeTask.java

@@ -0,0 +1,58 @@
+package kd.cosmic.jkjt.fi.cas.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.dataentity.OperateOption;
+import java.util.Date;
+import java.util.Map;
+
+
+/**
+ * @author turborao
+ * @date 2023/05/04
+ * @description    调度任务类,用于未开通CBS银企接口且未报备的银行账户,按预计开通CBS日期进行自动冻结。
+ */
+public class AutoAccountbankFreezeTask extends AbstractTask {
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+        /**
+         * 账户查询单据标识nckd_am_accountbank_ext
+         * 过滤字段:
+         * 开通CBS银企接口=否,nckd_issetcbs=0
+         * 未开通CBS是否报备=否,nckd_isreport=0
+         * 执行冻结日期:预计开通CBS日期,nckd_excbsdate
+         */
+        String cbsOpen = "0";
+        ///String cbsReport = "0";
+        String ENTITY_NAME = "am_accountbank";
+
+        QFilter filter1 = new QFilter("nckd_issetcbs", QCP.equals, cbsOpen);
+        ////QFilter filter2 = new QFilter("nckd_isreport", QCP.equals, cbsReport);
+        QFilter[] filters = new QFilter[]{filter1};
+        DynamicObject[] billEntities = BusinessDataServiceHelper.load(ENTITY_NAME, "id,billno,nckd_excbsdate", filters);
+        for (DynamicObject dataEntity : billEntities) {
+            Date excbsdate = dataEntity.getDate("nckd_excbsdate");
+            if(excbsdate!=null && DateUtils.compareByDay(DateUtils.getDateAfterAddDays(excbsdate,1),DateUtils.getCurrentDate())<=0){
+
+                DynamicObject bankBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), ENTITY_NAME);
+
+                OperateOption option = OperateOption.create();
+
+                ///增加调用参数
+                option.setVariableValue("createOrg",bankBillEntity.getDynamicObject("company").getPkValue().toString());
+
+                ///执行账户管理的冻结操作
+                OperationResult result = OperationServiceHelper.executeOperate("freeze", ENTITY_NAME, new DynamicObject[] { bankBillEntity }, option);
+            }
+        }
+
+    }
+}

+ 61 - 0
main/java/kd/cosmic/jkjt/fi/cas/task/AutoDownloadBankBillTask.java

@@ -0,0 +1,61 @@
+package kd.cosmic.jkjt.fi.cas.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.exception.KDException;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.fi.cas.helper.MultiBaseDataHelper;
+
+
+import java.util.*;
+
+
+/**
+ * @author turbo
+ * 自动执行,”下载银行对账单“功能,拉取银行交易明细,做银行账号对账用
+ *
+ */
+public class AutoDownloadBankBillTask extends AbstractTask {
+
+    private String billEntityName = "cas_downbankstate";
+    protected OperationResult operationResult;
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+
+        /**
+         * 获取银行计算天数参数
+         */
+        Calendar calStart = Calendar.getInstance();
+        int days = -1 * (ParamsUtil.getQueryDays() + 1);
+        calStart.setTime(new Date());
+        calStart.add(Calendar.DATE, days);
+
+        DynamicObject billDynamicObject = BusinessDataServiceHelper.newDynamicObject(billEntityName);
+
+        QFilter f2 = new QFilter("fisbankroll", "=", "1");
+        QFilter f3 = new QFilter("enable", "=", "1");
+        QFilter[] fs = new QFilter[]{f2.and(f3)};
+        DynamicObject[] orgs = BusinessDataServiceHelper.load("bos_org", "masterid,number",fs);
+        DynamicObjectCollection orgCol = MultiBaseDataHelper.generateMultiPropValue(billDynamicObject, "org", orgs);
+
+        billDynamicObject.set("org", orgCol);
+        billDynamicObject.set("begindate",calStart.getTime());
+        billDynamicObject.set("enddate",new Date());
+
+        OperateOption option = OperateOption.create();
+
+        ///执行下载银行对账单功能操作
+        OperationResult result = OperationServiceHelper.executeOperate("download", billEntityName, new DynamicObject[] { billDynamicObject }, option);
+
+
+    }
+}

+ 416 - 0
main/java/kd/cosmic/jkjt/fi/cas/task/DateUtils.java

@@ -0,0 +1,416 @@
+package kd.cosmic.jkjt.fi.cas.task;
+
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateUtils {
+    private static final Log logger = LogFactory.getLog(DateUtils.class);
+    public static final String DATEFORMAT = "yyyy-MM-dd";
+
+    public DateUtils() {
+    }
+
+    public static int compareByDay(Date oneDate, Date otherDate) {
+        if (oneDate != null && otherDate != null) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(oneDate);
+            int year = calendar.get(1);
+            int dayOfYear = calendar.get(6);
+            calendar.clear();
+            calendar.setTime(otherDate);
+            int otherYear = calendar.get(1);
+            int otherDayOfYear = calendar.get(6);
+            if (year < otherYear) {
+                return -1;
+            } else if (year > otherYear) {
+                return 1;
+            } else if (dayOfYear < otherDayOfYear) {
+                return -1;
+            } else {
+                return dayOfYear > otherDayOfYear ? 1 : 0;
+            }
+        } else {
+            throw new IllegalArgumentException("The date must not be null");
+        }
+    }
+
+    public static Date getDateAfterAddDays(Date srcDate, int days) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(srcDate);
+        calendar.add(5, days);
+        return calendar.getTime();
+    }
+
+    public static int getDaysBetweenTwoDate(Date firstDate, Date secondDate) {
+        if (firstDate != null && secondDate != null) {
+            int nDay = (int)((secondDate.getTime() - firstDate.getTime()) / 86400000L);
+            return nDay;
+        } else {
+            throw new IllegalArgumentException("The date must not be null");
+        }
+    }
+
+    public static Date getCurrentDate() {
+        return new Date();
+    }
+
+    public static Date getThisWeekMonday(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var6) {
+            logger.error("parse date error!", var6);
+        }
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+        int dayWeek = cal.get(7);
+        if (1 == dayWeek) {
+            cal.add(5, -1);
+        }
+
+        cal.setFirstDayOfWeek(2);
+        int day = cal.get(7);
+        cal.add(5, cal.getFirstDayOfWeek() - day);
+        return cal.getTime();
+    }
+
+    public static Date getThisWeekSunDay(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+        Calendar cal = Calendar.getInstance();
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var6) {
+            logger.error("parse date error!", var6);
+        }
+
+        cal.setTime(date);
+        int dayWeek = cal.get(7);
+        if (1 == dayWeek) {
+            cal.add(5, -1);
+        }
+
+        cal.setFirstDayOfWeek(2);
+        int day = cal.get(7);
+        cal.add(5, cal.getFirstDayOfWeek() - day + 6);
+        cal.set(11, 23);
+        cal.set(12, 59);
+        cal.set(13, 59);
+        return cal.getTime();
+    }
+
+    public static Date getLastWeekMonday(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(getThisWeekMonday(date));
+        cal.add(5, -7);
+        return cal.getTime();
+    }
+
+    public static Date getLastWeekSunday(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(getThisWeekSunDay(date));
+        cal.add(5, -7);
+        return cal.getTime();
+    }
+
+    public static Date getNextWeekMonday(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(getThisWeekMonday(date));
+        cal.add(5, 7);
+        return cal.getTime();
+    }
+
+    public static Date getNextWeekSunday(Date date) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(getThisWeekSunDay(date));
+        cal.add(5, 7);
+        return cal.getTime();
+    }
+
+    public static Date getFirstDayOfThisMonth(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        Calendar cale = Calendar.getInstance();
+        cale.setTime(date);
+        cale.add(2, 0);
+        cale.set(5, 1);
+        return cale.getTime();
+    }
+
+    public static Date getLastDayOfThisMonth(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        Calendar cale = Calendar.getInstance();
+        cale.setTime(date);
+        cale.add(2, 1);
+        cale.set(5, 0);
+        cale.set(11, 23);
+        cale.set(12, 59);
+        cale.set(13, 59);
+        return cale.getTime();
+    }
+
+    public static Date getFirstDayOfPreMonth(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(5, 1);
+        calendar.add(2, -1);
+        return calendar.getTime();
+    }
+
+    public static Date getLastDayOfPreMonth(Date date) {
+        Date firstDayOfPreMonth = getFirstDayOfPreMonth(date);
+        Date lastDayOfPreMonth = getLastDayOfThisMonth(firstDayOfPreMonth);
+        return lastDayOfPreMonth;
+    }
+
+    public static Date getFirstDayOfNextMonth(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        String format = simpleDateFormat.format(date);
+
+        try {
+            date = simpleDateFormat.parse(format);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(5, 1);
+        calendar.add(2, 1);
+        return calendar.getTime();
+    }
+
+    public static Date getLastDayOfNextMonth(Date date) {
+        Date firstDayOfNextMonth = getFirstDayOfNextMonth(date);
+        Date lastDayOfThisMonth = getLastDayOfThisMonth(firstDayOfNextMonth);
+        return lastDayOfThisMonth;
+    }
+
+    public static Date getEndDayTime(Date date) {
+        Calendar calendarEnd = Calendar.getInstance();
+        calendarEnd.setTime(date);
+        calendarEnd.set(11, 23);
+        calendarEnd.set(12, 59);
+        calendarEnd.set(13, 59);
+        calendarEnd.set(14, 0);
+        return calendarEnd.getTime();
+    }
+
+    public static String monthDayClearZero(String dateStr) {
+        Integer month = Integer.valueOf(dateStr.substring(5, 7));
+        Integer day = Integer.valueOf(dateStr.substring(8, 10));
+        StringBuilder res = new StringBuilder();
+        res.append(month);
+        res.append('-');
+        res.append(day);
+        return res.toString();
+    }
+
+    public static String formatShortDate(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        return simpleDateFormat.format(date);
+    }
+
+    public static String formatDate(Date date) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return simpleDateFormat.format(date);
+    }
+
+    public static Date parseShortDate(String str) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        Date parse = null;
+
+        try {
+            parse = simpleDateFormat.parse(str);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        return parse;
+    }
+
+    public static Date parseDate(String str) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        Date parse = null;
+
+        try {
+            parse = simpleDateFormat.parse(str);
+        } catch (ParseException var4) {
+            logger.error("parse date error!", var4);
+        }
+
+        return parse;
+    }
+
+    public static Date getYearFirst(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        int year = calendar.get(1);
+        calendar.clear();
+        calendar.set(1, year);
+        Date currYearFirst = calendar.getTime();
+        return currYearFirst;
+    }
+
+    public static Date getYearLast(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        int year = calendar.get(1);
+        calendar.clear();
+        calendar.set(1, year);
+        calendar.roll(6, -1);
+        calendar.set(11, 23);
+        calendar.set(12, 59);
+        calendar.set(13, 59);
+        Date currYearLast = calendar.getTime();
+        return currYearLast;
+    }
+
+    public static Date getHalfYearStartTime(Date date) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(date);
+        int currentMonth = c.get(2) + 1;
+        Date now = null;
+
+        try {
+            if (currentMonth >= 1 && currentMonth <= 6) {
+                c.set(2, 0);
+            } else if (currentMonth >= 7 && currentMonth <= 12) {
+                c.set(2, 6);
+            }
+
+            c.set(5, 1);
+            now = c.getTime();
+        } catch (Exception var5) {
+            logger.error(var5);
+        }
+
+        return now;
+    }
+
+    public static Date getHalfYearEndTime(Date date) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(date);
+        int currentMonth = c.get(2) + 1;
+        Date now = null;
+
+        try {
+            if (currentMonth >= 1 && currentMonth <= 6) {
+                c.set(2, 5);
+                c.set(5, 30);
+            } else if (currentMonth >= 7 && currentMonth <= 12) {
+                c.set(2, 11);
+                c.set(5, 31);
+            }
+
+            c.set(11, 23);
+            c.set(12, 59);
+            c.set(13, 59);
+            now = c.getTime();
+        } catch (Exception var5) {
+            logger.error(var5);
+        }
+
+        return now;
+    }
+
+    public static Date getQuarterStartTime(Date date) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(date);
+        int currentMonth = c.get(2) + 1;
+        Date now = null;
+
+        try {
+            if (currentMonth >= 1 && currentMonth <= 3) {
+                c.set(2, 0);
+            } else if (currentMonth >= 4 && currentMonth <= 6) {
+                c.set(2, 3);
+            } else if (currentMonth >= 7 && currentMonth <= 9) {
+                c.set(2, 6);
+            } else if (currentMonth >= 10 && currentMonth <= 12) {
+                c.set(2, 9);
+            }
+
+            c.set(5, 1);
+            now = c.getTime();
+        } catch (Exception var5) {
+            logger.error(var5);
+        }
+
+        return now;
+    }
+
+    public static Date getQuarterEndTime(Date date) {
+        Calendar c = Calendar.getInstance();
+        c.setTime(date);
+        int currentMonth = c.get(2) + 1;
+        Date now = null;
+
+        try {
+            if (currentMonth >= 1 && currentMonth <= 3) {
+                c.set(2, 2);
+                c.set(5, 31);
+            } else if (currentMonth >= 4 && currentMonth <= 6) {
+                c.set(2, 5);
+                c.set(5, 30);
+            } else if (currentMonth >= 7 && currentMonth <= 9) {
+                c.set(2, 8);
+                c.set(5, 30);
+            } else if (currentMonth >= 10 && currentMonth <= 12) {
+                c.set(2, 11);
+                c.set(5, 31);
+            }
+
+            c.set(11, 23);
+            c.set(12, 59);
+            c.set(13, 59);
+            now = c.getTime();
+        } catch (Exception var5) {
+            logger.error(var5);
+        }
+
+        return now;
+    }
+
+    public static Date getStartDayTime(Date date) {
+        Calendar todayStart = Calendar.getInstance();
+        todayStart.setTime(date);
+        todayStart.set(11, 0);
+        todayStart.set(12, 0);
+        todayStart.set(13, 0);
+        todayStart.set(14, 0);
+        return todayStart.getTime();
+    }
+}

+ 80 - 0
main/java/kd/cosmic/jkjt/fi/cas/task/PayBillReturnStatusForCBSTask.java

@@ -0,0 +1,80 @@
+package kd.cosmic.jkjt.fi.cas.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.cosmic.jkjt.fi.cas.common.PayBillToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * @author turborao
+ * @date 2023/04/28
+ * @description    付款单状态更新插件  调试任务插件
+ */
+public class PayBillReturnStatusForCBSTask extends AbstractTask {
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+        String cbsStatus = "2";
+        String isCbs = "1";
+        String ENTITY_NAME = "cas_paybill";
+        Map<String, Map<String, String>> cbsMap = new HashMap<>();
+
+        QFilter filter1 = new QFilter("nckd_cbsstatus", QCP.equals, cbsStatus);
+        QFilter filter2 = new QFilter("nckd_iscbs", QCP.equals, isCbs);
+        QFilter[] filters = new QFilter[]{filter1.and(filter2)};
+        DynamicObject[] billEntities = BusinessDataServiceHelper.load(ENTITY_NAME, "id,billno", filters);
+        //获取多套CBS系统地址
+        List<String> cbsSysList =  PayBillToolUtil.cbsSystemList();
+
+        for(String cbsUrl : cbsSysList){
+
+            Map<String, String> billMap = new HashMap<>();
+
+
+            for (DynamicObject dataEntity : billEntities) {
+                DynamicObject payBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), ENTITY_NAME);
+
+                String billno = payBillEntity.getString("billno");
+                String batchno = payBillEntity.getString("batchseqid");
+
+                ///处理支付单版本号,避免重复推送CBS支付数据
+//                if(payBillEntity.getInt("nckd_payversion")>0){
+//                    StringBuffer billNumCBS = new StringBuffer();
+//                    billNumCBS.append(billno).append("~").append(payBillEntity.getInt("nckd_payversion"));
+//                    billno = billNumCBS.toString();
+//                }
+
+                ///多CBS处理
+                String cbsUrlBill = ParamsUtil.getCBSURL(payBillEntity.getDynamicObject("openorg").getLong("id"));
+
+                if(cbsUrl.equals(cbsUrlBill)) {
+                    billMap.put(billno, batchno);
+                }
+            }
+
+            cbsMap.put(cbsUrl,billMap);
+        }
+
+        for (Map.Entry<String, Map<String, String>> billMaps : cbsMap.entrySet()) {
+            String url  = billMaps.getKey();
+            Map<String, String> billMap = billMaps.getValue();
+            PayBillToolUtil.payStatusForCBSSync(url,billMap);
+        }
+
+
+
+    }
+
+
+}
+

+ 253 - 0
main/java/kd/cosmic/jkjt/fi/cas/task/PayReturnStatusForERSystemTask.java

@@ -0,0 +1,253 @@
+package kd.cosmic.jkjt.fi.cas.task;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.fi.arapcommon.util.DateUtils;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author turbo
+ *
+ * 回写报销系统付款状态  通过任务执行,保证可补偿更新
+ *
+ */
+public class PayReturnStatusForERSystemTask extends AbstractTask {
+    private static final Log logger = LogFactory.getLog(PayReturnStatusForERSystemTask.class);
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        String srcSystem = "ER";
+        String ENTITY_NAME = "cas_paybill";
+
+        DynamicObject commonParams = ParamsUtil.getCommonParamsAllField();
+
+        String url = "";
+        int updateDay = -3;
+        if(commonParams != null ){
+            url = commonParams.getString("nckd_fiurl");
+            updateDay = commonParams.getInt("nckd_fidays");
+        }
+
+        if(StringUtils.isEmpty(url)){
+            logger.info("turborao PayReturnStatusForERSystemTask: url is not setting");
+            return;
+        }
+
+        Date modifyDay = DateUtils.getNextDay(new Date(), updateDay);
+
+        QFilter filter = new QFilter("nckd_srcsystem", QCP.equals, srcSystem);
+        filter.and(new QFilter("nckd_apisourcebillno", QCP.not_equals, " "));
+        filter.and(new QFilter("modifytime", QCP.large_equals, modifyDay));
+//        filter.and(new QFilter("billno", QCP.equals, "PV-202410-006562"));
+
+        DynamicObject[] billEntities = BusinessDataServiceHelper.load(ENTITY_NAME, "id,billno", filter.toArray());
+
+        for (DynamicObject dataEntity : billEntities) {
+            DynamicObject payBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), ENTITY_NAME);
+
+            //结算方式  JSFS00(银企直连)、JSFS01(现金)、JSFS04(CBS)、JSFS13(网银)
+            DynamicObject settletypeEntity = payBillEntity.getDynamicObject("settletype");
+            String settletypeString = settletypeEntity.getString("number");
+
+            String billStatus = payBillEntity.getString("billstatus");
+            String  apisourcebillno =  payBillEntity.getString("nckd_apisourcebillno");
+
+//            //在同步日志查询一遍,如果同步过就不同步
+//            QFilter logFilter = new QFilter("nckd_ficono", QCP.equals, apisourcebillno);
+//            boolean isExists = QueryServiceHelper.exists("nckd_cbslog", logFilter.toArray());
+//            if(isExists){
+//                continue;
+//            }
+
+            //CBS状态、返回信息
+            String cbsstatus =  payBillEntity.getString("nckd_cbsstatus");
+            String cbsreturnmsg =  payBillEntity.getString("nckd_cbsreturnmsg");
+            //银行付款单状态、银行返回信息
+            String bankpaystatus = payBillEntity.getString("bankpaystatus");
+            String bankreturnmsg = payBillEntity.getString("bankreturnmsg");
+
+            //财辅系统付款状态paymentStatus 1 支付成功、 0  支付失败  3  支付撤销
+            String erSystemStatus = "";
+
+            if("JSFS00".equals(settletypeString)) {
+                switch (bankpaystatus) {
+                    case "TS":
+                        cbsstatus = "3";
+                        erSystemStatus = "1";
+                        break;
+                    case "TF":
+                    case "NC":
+                    case "OF":
+                        cbsstatus = "4";
+                        erSystemStatus = "0";
+                        break;
+                }
+                cbsreturnmsg = bankreturnmsg;
+            } else {
+                switch (cbsstatus) {
+                    case "3":
+                        erSystemStatus = "1";
+                        break;
+                    case "4":
+                    case "5":
+                        erSystemStatus = "0";
+                        break;
+                }
+            }
+
+            //当cbs状态为  1 未支付 2 支付处理中  不做回写
+            if("H".equals(billStatus) || "I".equals(billStatus)) {
+                erSystemStatus = "3";
+                cbsreturnmsg = "支付撤销";
+            } else {
+                if(!"D".equals(billStatus) && erSystemStatus.isEmpty()) {
+                    continue;
+                }
+            }
+
+            /**
+             * 处理非CBS支付
+             */
+            if("D".equals(billStatus) && !erSystemStatus.equals("1")){
+                erSystemStatus = "1";
+                switch(settletypeString){
+                    case "JSFS01" :
+                        cbsreturnmsg = "现金支付";
+                        break;
+                    case "JSFS13" :
+                        cbsreturnmsg = "网银支付";
+                        break;
+                    default:
+                        cbsreturnmsg = "其他支付";
+                        break;
+                }
+            }
+
+            JSONObject json = new JSONObject();
+
+            json.put("businessNumber", apisourcebillno);
+            json.put("paymentStatus", erSystemStatus);
+            if(!"1".equals(erSystemStatus)) {
+                json.put("failCode", cbsstatus);
+                json.put("failReason", cbsreturnmsg);
+            }
+            json.put("payDate", null);
+
+            //added by wanghaiwu_kd 2024/05/30
+            //增加【流水号】参数
+            // 结算方式 = CBS支付:返回的是cbs付款申请单的流水号
+            // 结算方式 = 银企直联:返回的是离线明细上的电子回单关联标记
+            String serialNumber = "";
+            //交易明细流水号
+            String detailNumber = "";
+
+            //结算方式 = CBS支付
+            if("JSFS04".equals(settletypeString)) {
+                serialNumber = payBillEntity.getString("batchseqid");
+
+                if("1".equals(erSystemStatus)) {
+                    //增加支付时间参数
+                    if(payBillEntity.get("nckd_paytime") != null && payBillEntity.getLong("nckd_paytime") > 0){
+                        json.put("payDate", payBillEntity.getLong("nckd_paytime"));
+                    } else if(payBillEntity.getDate("paydate") != null){
+                        json.put("payDate", payBillEntity.getDate("paydate").getTime());
+                    }
+
+                    //根据业务参考号查找交易明细
+                    QFilter qFilter = new QFilter("bizrefno", QCP.equals, payBillEntity.getString("billno"));
+                    qFilter.and(new QFilter("description", QCP.not_equals, "手续费"));
+
+                    DynamicObject beiDetail = BusinessDataServiceHelper.loadSingle("bei_betransdetail_imp", qFilter.toArray());
+
+                    if(beiDetail != null){
+                        detailNumber = beiDetail.getString("detailid");
+                    }
+                }
+            }
+            //结算方式 = 银企直联
+            else if("JSFS00".equals(settletypeString)) {
+                //对账标识码
+                String bankCheckFlag = payBillEntity.getString("bankcheckflag");
+                if(StringUtils.isNotEmpty(bankCheckFlag)){
+                    QFilter qFilter = new QFilter("bankcheckflag", QCP.equals, bankCheckFlag);
+
+                    //根据对账标识码查找交易明细
+                    DynamicObject beiDetail = BusinessDataServiceHelper.loadSingle("bei_betransdetail_imp", qFilter.toArray());
+                    if(beiDetail != null){
+                        serialNumber = beiDetail.getString("receiptno");
+                        detailNumber = beiDetail.getString("detailid");
+                    }
+                }
+
+                if("1".equals(erSystemStatus)) {
+                    QFilter qFilter = new QFilter("srcbillno", QCP.equals, payBillEntity.getString("billno"));
+                    qFilter.and(new QFilter("srcbilltype", QCP.equals, "cas_paybill"));
+
+                    //查询银行付款单,如果是交易成功,获取银行付款单上的付款时间
+                    DynamicObject bankPayBill = BusinessDataServiceHelper.loadSingle("bei_bankpaybill", qFilter.toArray());
+                    if(bankPayBill != null && "TS".equals(bankPayBill.getString("bankpaystate"))){
+                        json.put("payDate", bankPayBill.getDate("paytime").getTime());
+                    } else if(payBillEntity.getDate("paydate") != null){
+                        json.put("payDate", payBillEntity.getDate("paydate").getTime());
+                    }
+                }
+            } else {
+                if("1".equals(erSystemStatus)) {
+                    if(payBillEntity.getDate("paydate") != null){
+                        json.put("payDate", payBillEntity.getDate("paydate").getTime());
+                    } else {
+                        Date date = new Date();
+                        json.put("payDate", date.getTime());
+                    }
+                }
+            }
+
+            json.put("serialNumber", serialNumber);
+            json.put("detailNumber", detailNumber);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            String uri = url + "/api/financial/FinancialPayment/paymentUpdate";
+
+            logger.info("turborao PayReturnStatusForERSystemTask:" + json.toJSONString());
+
+            String appReturnStr = HttpUtilAction.doPost(uri,json.toJSONString(),headers);
+
+            logger.info("turborao PayReturnStatusForERSystemTask:" + appReturnStr);
+
+            if(StringUtils.isEmpty(appReturnStr)){
+                continue;
+            }
+
+            JSONObject appReturnJson = JSONObject.parseObject(appReturnStr);
+
+            if("200".equals(appReturnJson.getString("code"))){
+                //如果是支付成功,就记录下日志
+//                if("1".equals(erSystemStatus)) {
+//                    //1、先保存CBS调用日志
+//                    String uuid = ParamsUtil.saveCBSLogData(uri, "synstatus2fico", json.toJSONString());
+//
+//                    //2、更新CBS调用日志返回结果
+//                    ParamsUtil.updateCBSLogData(uuid, appReturnStr);
+//                }
+            } else {
+                logger.info("turborao PayReturnStatusForERSystemTask: ER system is invalid");
+            }
+        }
+    }
+}

+ 69 - 0
main/java/kd/cosmic/jkjt/fi/gl/formplugin/VoucherCusEditPlugin.java

@@ -0,0 +1,69 @@
+package kd.cosmic.jkjt.fi.gl.formplugin;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.property.BasedataProp;
+import kd.bos.form.field.FlexEdit;
+import kd.bos.form.field.events.BeforeF7SelectEvent;
+import kd.bos.form.field.events.BeforeF7SelectListener;
+import kd.bos.list.ListFilterParameter;
+import kd.bos.list.ListShowParameter;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import java.util.EventObject;
+import java.util.List;
+
+/**
+ * @description 凭证表单插件,处理现金流量项目核算维度的资金计划项目过滤问题
+ * @author wanghaiwu_kd
+ * @date 2024/11/12
+ */
+public class VoucherCusEditPlugin extends AbstractBillPlugIn implements BeforeF7SelectListener {
+    private static String KEY_FORMID = "";
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+
+        KEY_FORMID = this.getView().getFormShowParameter().getFormId();
+        //凭证
+        if("gl_voucher".equals(KEY_FORMID)) {
+            FlexEdit flexEdit = (FlexEdit) this.getView().getControl("mcfassgrp");
+            flexEdit.registerBeforeF7SelectListener(this);
+        }
+        //现金流量设置
+        else if("gl_voucher_cashflowedit".equals(KEY_FORMID)) {
+            FlexEdit flexEdit = (FlexEdit) this.getView().getControl("maincfassgrp");
+            flexEdit.registerBeforeF7SelectListener(this);
+        }
+    }
+
+    @Override
+    public void beforeF7Select(BeforeF7SelectEvent evt) {
+        String property = evt.getProperty().getName();
+        String entityId = ((BasedataProp) evt.getProperty()).getBaseEntityId();
+        int curRowIndex = evt.getRow();
+
+        ListShowParameter showParameter = (ListShowParameter) evt.getFormShowParameter();
+        ListFilterParameter filterParam = showParameter.getListFilterParameter();
+        List<QFilter> qFilters = filterParam.getQFilters();
+        DynamicObject maincf = null;
+
+        if("cas_fundflowitem".equals(entityId)) {
+            if ("gl_voucher".equals(KEY_FORMID)) {
+                if (property.contains("mcfassgrp")) {
+                    maincf = (DynamicObject) this.getModel().getValue("maincf");
+                }
+            } else if ("gl_voucher_cashflowedit".equals(KEY_FORMID)) {
+                if (property.contains("maincfassgrp")) {
+                    maincf = (DynamicObject) this.getModel().getValue("maincfitem", curRowIndex);
+                }
+            }
+
+            if (maincf != null) {
+                String direction = "i".equals(maincf.getString("direction")) ? "B" : "C";
+
+                qFilters.add(new QFilter("direction", QCP.equals, direction));
+            }
+        }
+    }
+}

+ 355 - 0
main/java/kd/cosmic/jkjt/fi/gl/formplugin/voucherForImageEditPlugin.java

@@ -0,0 +1,355 @@
+package kd.cosmic.jkjt.fi.gl.formplugin;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.IClientViewProxy;
+import kd.bos.form.IFormView;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.siit.SiitServerHandler;
+import kd.fi.cas.util.EmptyUtil;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * @author turbo
+ * 凭证对接国信影像对接
+ */
+public class voucherForImageEditPlugin extends AbstractBillPlugIn {
+
+    private static final Log logger = LogFactory.getLog(voucherForImageEditPlugin.class);
+
+
+    private static final String KEY_BTN_Updateimage = "nckd_updateimage";
+    private static final String KEY_BTN_Viewimage = "nckd_viewimage";
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        this.addItemClickListeners(new String[]{"tbsave"});
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+
+        String itemKey = evt.getItemKey();
+        if (KEY_BTN_Updateimage.equals(itemKey)) {
+            this.getUpdateImageUrl();
+        }
+        if (KEY_BTN_Viewimage.equals(itemKey)) {
+            this.getViewImageUrl();
+        }
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Map<String, Object> customParams = this.getView().getFormShowParameter().getCustomParams();
+        boolean isPaymentViewVoucher = customParams.get("nckd_paymentviewvoucher") == null ? false : true;
+
+        if(isPaymentViewVoucher){
+            this.getView().setVisible(false, "titlepanelflex");
+        }
+
+        IFormView view = this.getView();
+        IDataModel m = this.getModel();
+        String billstatus = m.getDataEntity().getString("billstatus");
+
+        if (billstatus.matches("D")) {
+            view.setVisible(false, new String[]{"nckd_updateimage"});
+        } else{
+            view.setVisible(true, new String[]{"nckd_updateimage"});
+        }
+    }
+
+    /**
+     * 获取barCode
+     * 根据条件设置barcode:如果描述不为空,并且系统来源=会计平台 或 来源类型 = 外部导入时使用description做为barcode
+     * @param id
+     * @return
+     */
+    public String getBarCode(String id){
+        String barCode = "";
+        DynamicObject voucherInfo = BusinessDataServiceHelper.loadSingle(id, "gl_voucher");
+
+        String description = voucherInfo.getString("description");
+
+        if(StringUtils.isNotEmpty(description)){
+            DynamicObject sourcesys = voucherInfo.getDynamicObject("sourcesys");
+            String sourcetype = voucherInfo.getString("sourcetype");
+
+            if("ai".equals(sourcesys.getString("number")) || "8".equals(sourcetype)){
+                barCode = description;
+            }
+        }
+
+        //如果是合并单据,则默认取第一个单据号
+        if(StringUtils.isNotEmpty(barCode) && barCode.contains(",")){
+            barCode = barCode.split(",")[0];
+        }
+
+        return barCode;
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs evt) {
+        super.afterDoOperation(evt);
+
+        String name = evt.getOperateKey();
+        OperationResult operationResult = evt.getOperationResult();
+        if (name.equals("delete") && operationResult.isSuccess()) {
+            this.deleteBillImage();
+        }
+    }
+
+    private void getViewImageUrl(){
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+            this.getView().showErrorNotification("请先保存凭证!");
+            return;
+        }
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+
+        if(!EmptyUtil.isEmpty(id)){
+            //add by wnaghaiwu_kd 2023/12/05
+            String barCode = getBarCode(id.toString());
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+            }
+
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            JSONObject jsonCreateImagePath = SiitServerHandler.getCreateImagePath(barCode,username);
+
+            JSONObject jsonImagePath = new JSONObject();
+            jsonImagePath.put("safety",jsonSafety);
+            jsonImagePath.put("params",jsonCreateImagePath);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            /**
+             * 调用获取票夹页面地址接口
+             */
+            logger.info("Siit Log" + jsonImagePath.toJSONString());
+            String uri = SiitServerHandler.url + "/siitservice/createImagePathApi/createImagePath";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonImagePath.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(appTokenJ1.getString("status").equals("true")){
+                openUrl(appTokenJ1.getString("data"));
+            }else{
+                if("Service_createImagePath_00002".equals(appTokenJ1.getString("errorCode"))){
+                    this.getView().showErrorNotification("未上传影像,无法查看!");
+                }else {
+                    this.getView().showErrorNotification("查看异常!影像返回信息:"  + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                }
+            }
+
+
+        }
+    }
+
+    private void getUpdateImageUrl(){
+        Object id = this.getModel().getValue("id");
+        if(Long.valueOf(String.valueOf(id)) <= 0L){
+            this.getView().showErrorNotification("请先保存凭证!");
+            return;
+        }
+        DynamicObject org =  (DynamicObject)this.getModel().getValue("org");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+        if(!EmptyUtil.isEmpty(id)){
+            String barCode = getBarCode(id.toString());
+            String uri = "";
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            if(StringUtils.isEmpty(barCode)){
+                barCode = id.toString();
+
+                if(!isExists(Long.valueOf(String.valueOf(id)), "flow")) {
+                    JSONObject jsonFlow = SiitServerHandler.getFlowParams(barCode, org.getString("number"), username, "");
+                    //JSONObject jsonCreateImagePath = SiitServerHandler.getCreateImagePath(barCode);
+
+                    //触发扫描接口调入参数
+                    JSONObject jsonFlowPost = new JSONObject();
+                    jsonFlowPost.put("safety", jsonSafety);
+                    jsonFlowPost.put("params", jsonFlow);
+
+                    //对于以凭证id为barCode的凭证,需要先要触发扫描接口(待办),以上游业务单据为barCode的凭证不需要触发待办
+                    logger.info("Siit Log" + jsonFlowPost.toJSONString());
+                    uri = SiitServerHandler.url + "/siitservice/startFlow/addFlow";
+                    String appReturnStr = HttpUtilAction.doPost(uri, jsonFlowPost.toJSONString(), headers);
+                    JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+                    logger.info("Siit Log" + appTokenJO.toString());
+
+                    if (!appTokenJO.getString("status").equals("true")) {
+                        this.getView().showErrorNotification("上传异常!影像返回信息:" + (appTokenJO == null ? "" : appTokenJO.toString()));
+                        return;
+                    } else {
+                        //触发待办成功,记录触发日志
+                        saveFlowLog(Long.valueOf(String.valueOf(id)), "flow");
+                    }
+                }
+            } else {
+                if(barCode.contains(",")){
+                    this.getView().showErrorNotification("此凭证是由合并单据生成,请前往具体单据内进行附件补传!");
+                    return;
+                }
+            }
+
+            JSONObject jsonPageUrl = SiitServerHandler.getPageUrl(barCode,username);
+            //获取票夹页面地址调入参数
+            JSONObject jsonPagePost = new JSONObject();
+            jsonPagePost.put("safety",jsonSafety);
+            jsonPagePost.put("params",jsonPageUrl);
+
+            /**
+             * 调用获取票夹页面地址接口
+             */
+            logger.info("Siit Log" +jsonPagePost.toJSONString());
+            uri = SiitServerHandler.url + "/siitservice/ticketpage/getPageUrl";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonPagePost.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(appTokenJ1.getString("status").equals("true")){
+                openUrl( appTokenJ1.getString("data"));
+            } else {
+                this.getView().showErrorNotification("调用获取票夹页面接口异常!影像返回信息:" + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                return;
+            }
+//            else {
+//                JSONObject jsonUpload = SiitServerHandler.getUploadPage(barCode,username);
+//                //获取附件上载页面地址调入参数
+//                JSONObject jsonPagePost = new JSONObject();
+//                jsonPagePost.put("safety",jsonSafety);
+//                jsonPagePost.put("params",jsonUpload);
+//
+//                //获取附件上载页面地址
+//                logger.info("Siit Log" +jsonPagePost.toJSONString());
+//                uri = SiitServerHandler.url + "/siitservice/uploadFile/uploadPage";
+//                String appReturnStr = HttpUtilAction.doPost(uri, jsonPagePost.toJSONString(), headers);
+//                JSONObject appToken = JSONObject.parseObject(appReturnStr);
+//                logger.info("Siit Log" +appToken.toString());
+//
+//                if(appToken.getString("status").equals("true")){
+//                    openUrl( appToken.getString("data"));
+//                } else {
+//                    this.getView().showErrorNotification("调用获取附件上传接口异常!影像返回信息:" + (appToken == null ? "" : appToken.toString()));
+//                    return;
+//                }
+//            }
+        }
+    }
+
+    private boolean isExists(Long voucherId, String operatorType){
+        boolean isExists = false;
+        QFilter qFilter = new QFilter("nckd_voucherid", QCP.equals, voucherId);
+        qFilter.and(new QFilter("nckd_billtype", QCP.equals, "gl_voucher"));
+        qFilter.and(new QFilter("nckd_operatortype", QCP.equals, operatorType));
+
+        DynamicObject flowLog = BusinessDataServiceHelper.loadSingle("nckd_sitflowlog", qFilter.toArray());
+        if(flowLog != null){
+            isExists = true;
+        }
+
+        return isExists;
+    }
+
+    private void saveFlowLog(Long voucherId, String operatorType){
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_sitflowlog");
+
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("creator", RequestContext.get().getCurrUserId());
+        dynamicObject.set("modifier", RequestContext.get().getCurrUserId());
+        dynamicObject.set("nckd_billtype", "gl_voucher");
+        dynamicObject.set("nckd_voucherid", voucherId);
+        dynamicObject.set("nckd_operatortype", operatorType);
+
+//        dynamicObject.set("nckd_sendtime", new Date());
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(dynamicObject.getDataEntityType().getName(), dynamicObject, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, dynamicObject);
+
+        if(StringUtils.isEmpty(billno)){
+            billno = UUID.randomUUID().toString().replace("-", "");
+            billno = (billno == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : billno;
+        }
+        dynamicObject.set("number", billno);
+
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+    }
+
+    private void deleteBillImage(){
+        Object id = this.getModel().getValue("id");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+        String username = userInfo.getString("username");
+        if(!EmptyUtil.isEmpty(id)){
+            String barCode = id.toString();
+//            String barCode = getBarCode(id.toString());
+//            if(StringUtils.isEmpty(barCode)){
+//                barCode = id.toString();
+//            }
+
+            JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode,username);
+            JSONObject jsonDeleteBill = SiitServerHandler.deleteBill(barCode,username);
+
+            JSONObject jsonDeletePost = new JSONObject();
+            jsonDeletePost.put("safety",jsonSafety);
+            jsonDeletePost.put("params",jsonDeleteBill);
+
+            Map<String, String> headers = new ConcurrentHashMap<>(1);
+            headers.put("Content-Type", "application/json");
+
+            /**
+             * 调用作废接口
+             */
+            logger.info("Siit Log" +jsonDeletePost.toJSONString());
+            String uri = SiitServerHandler.url + "/siitservice/todeleteApi/deleteBill";
+            String appReturnStr1 = HttpUtilAction.doPost(uri, jsonDeletePost.toJSONString(), headers);
+            JSONObject appTokenJ1 = JSONObject.parseObject(appReturnStr1);
+            logger.info("Siit Log" +appTokenJ1.toString());
+
+            if(!appTokenJ1.getString("status").equals("true")){
+                logger.info("Siit Log:作废异常!影像返回信息:" + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+//                this.getView().showErrorNotification("作废异常!影像返回信息:" + (appTokenJ1 == null ? "" : appTokenJ1.toString()));
+                return;
+            }
+
+        }
+
+    }
+
+    public void openUrl(String url){
+        String openurl = url;
+        IClientViewProxy proxy = this.getView().getService(IClientViewProxy.class);
+        Map<String, String> mpURL = new HashMap<String, String>();
+        mpURL.put("url", openurl);
+        proxy.addAction("openUrl", mpURL);
+    }
+}

+ 304 - 0
main/java/kd/cosmic/jkjt/fi/gl/opplugin/VoucherForImageOpPlugin.java

@@ -0,0 +1,304 @@
+package kd.cosmic.jkjt.fi.gl.opplugin;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.siit.SiitServerHandler;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @description 凭证操作插件,触发影像待办及归档动作
+ * @author wanghaiwu_kd
+ * @date 2024/12/16
+ */
+public class VoucherForImageOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(VoucherForImageOpPlugin.class);
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+    }
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+
+        List<String> fieldKeys = e.getFieldKeys();
+
+        fieldKeys.add("sourcetype");
+        fieldKeys.add("sourcesys");
+        fieldKeys.add("description");
+        fieldKeys.add("nckd_bankserialno");
+    }
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+
+        String opKey = e.getOperationKey();
+
+        if("submit".equals(opKey)) {
+            String errMsg = "";
+
+            //触发待办
+            DynamicObject[] dataEntities = e.getDataEntities();
+
+            for (DynamicObject voucher : dataEntities) {
+                String barCode = getBarCode(voucher);
+                //如果不是交易认领的凭证,则跳过
+                if (voucher.get("nckd_detailclaim") == null || !voucher.getBoolean("nckd_detailclaim")) {
+                    continue;
+                }
+
+                String msg = "";
+                if (voucher.getLong("id") > 0) {
+                    //触发影像待办、归档操作
+                    msg = send2Siit(voucher, barCode);
+                } else {
+                    msg = "凭证(" + voucher.getString("billno") + ")请先保存后再提交!";
+                }
+
+                if (StringUtils.isNotEmpty(msg)) {
+                    if (errMsg.length() > 0) {
+                        errMsg += "\r\n";
+                    }
+                    errMsg += msg;
+                }
+            }
+
+            if (!errMsg.isEmpty()) {
+                ////将错误信息返回到前端
+                e.setCancelMessage(errMsg);
+                e.setCancel(true);
+
+                logger.info("VoucherForImageOpPlugin 错误信息:" + errMsg.toString());
+            }
+        }
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+
+        String opKey = e.getOperationKey();
+
+        if("save".equals(opKey)) {
+            //触发待办
+            DynamicObject[] dataEntities = e.getDataEntities();
+
+            for (DynamicObject voucher : dataEntities) {
+                String barCode = getBarCode(voucher);
+                //如果不是交易认领的凭证,则跳过
+                if (voucher.get("nckd_detailclaim") == null || !voucher.getBoolean("nckd_detailclaim")) {
+                    continue;
+                }
+
+                if (voucher.getLong("id") > 0) {
+                    //触发影像待办、归档操作
+                    send2Siit(voucher, barCode);
+                }
+            }
+
+            SaveServiceHelper.update(dataEntities);
+        }
+    }
+
+
+    /**
+     * 触发影像待办、归档操作
+     * @param voucher
+     * @param barCode
+     * @return
+     */
+    private String send2Siit(DynamicObject voucher, String barCode){
+        String errMsg = "";
+
+        Long id = voucher.getLong("id");
+        String billno = voucher.getString("billno");
+
+        DynamicObject org = voucher.getDynamicObject("org");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+
+        String username = userInfo.getString("username");
+        barCode = id.toString();
+
+        Map<String, String> headers = new ConcurrentHashMap<>(1);
+        headers.put("Content-Type", "application/json");
+        JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode, username);
+
+        //触发待办
+        if(!isExists(Long.valueOf(String.valueOf(id)), "flow")) {
+            JSONObject jsonFlow = SiitServerHandler.getFlowParams(barCode, org.getString("number"), username, String.valueOf(id));
+
+            //触发扫描接口调入参数
+            JSONObject jsonFlowPost = new JSONObject();
+            jsonFlowPost.put("safety", jsonSafety);
+            jsonFlowPost.put("params", jsonFlow);
+
+            //对于以凭证id为barCode的凭证,需要先要触发扫描接口(待办),以上游业务单据为barCode的凭证不需要触发待办
+            logger.info("Siit Log" + jsonFlowPost.toJSONString());
+
+            String uri = SiitServerHandler.url + "/siitservice/startFlow/addFlow";
+            String appReturnStr = HttpUtilAction.doPost(uri, jsonFlowPost.toJSONString(), headers);
+
+            if(StringUtils.isEmpty(appReturnStr)){
+                return "凭证(" + billno + ")触发影像待办失败";
+            }
+
+            JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+
+            logger.info("Siit Log" + appTokenJO.toString());
+
+            if (!appTokenJO.getString("status").equals("true")) {
+                return "凭证(" + billno + ")触发待办失败" + (appTokenJO == null ? "" : appTokenJO.toString());
+            }
+
+            //触发待办成功,记录触发日志
+            saveFlowLog(Long.valueOf(String.valueOf(id)), "flow");
+
+            voucher.set("nckd_isflowed", true);
+            voucher.set("nckd_flowtime", new Date());
+        }
+
+        //取消触发归档操作
+//        //触发归档
+//        if(!isExists(Long.valueOf(String.valueOf(id)), "archive")) {
+//            //触发归档操作
+//            JSONObject jsonArchive = SiitServerHandler.getAotuArchive(barCode, org.getString("number"), username);
+//
+//            //触发归档接口调入参数
+//            JSONObject jsonArchivePost = new JSONObject();
+//            jsonArchivePost.put("safety", jsonSafety);
+//            jsonArchivePost.put("params", jsonArchive);
+//
+//            //对于以凭证id为barCode的凭证,需要先要触发扫描接口(待办),以上游业务单据为barCode的凭证不需要触发待办
+//            logger.info("Siit Log" + jsonArchive.toJSONString());
+//
+//            String uri = SiitServerHandler.url + "/siitservice/archiveApi/aotuArchive";
+//            String appReturnStr = HttpUtilAction.doPost(uri, jsonArchivePost.toJSONString(), headers);
+//
+//            if(StringUtils.isEmpty(appReturnStr)){
+//                return "凭证(" + billno + ")触发影像归档失败";
+//            }
+//
+//            JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+//
+//            logger.info("Siit Log" + appTokenJO.toString());
+//
+//            if (!appTokenJO.getString("status").equals("true")) {
+//                return "凭证(" + billno + ")触发影像归档接口失败:" + (appTokenJO == null ? "" : appTokenJO.toString());
+//            }
+//
+//            //触发归档成功,记录触发日志
+//            saveFlowLog(Long.valueOf(String.valueOf(id)), "archive");
+//        }
+
+        return errMsg;
+    }
+
+    /**
+     * 获取凭证的barcode
+     * @param voucherInfo
+     * @return
+     */
+    public String getBarCode(DynamicObject voucherInfo){
+        String barCode = "";
+
+        String description = voucherInfo.getString("description");
+
+        if(StringUtils.isNotEmpty(description)){
+            DynamicObject sourcesys = voucherInfo.getDynamicObject("sourcesys");
+            String sourcetype = voucherInfo.getString("sourcetype");
+
+            if("ai".equals(sourcesys.getString("number")) || "8".equals(sourcetype)){
+                barCode = description;
+            }
+        }
+
+        //如果是合并单据,则默认取第一个单据号
+        if(StringUtils.isNotEmpty(barCode) && barCode.contains(",")){
+            barCode = barCode.split(",")[0];
+        }
+
+        return barCode;
+    }
+
+    /**
+     * 判断凭证是否已出发过待办
+     * @param voucherId
+     * @return
+     */
+    private boolean isExists(Long voucherId, String operatorType){
+        boolean isExists = false;
+        QFilter qFilter = new QFilter("nckd_voucherid", QCP.equals, voucherId);
+        qFilter.and(new QFilter("nckd_billtype", QCP.equals, "gl_voucher"));
+        qFilter.and(new QFilter("nckd_operatortype", QCP.equals, operatorType));
+
+        DynamicObject flowLog = BusinessDataServiceHelper.loadSingle("nckd_sitflowlog", qFilter.toArray());
+        if(flowLog != null){
+            isExists = true;
+        }
+
+        return isExists;
+    }
+
+    /**
+     * 保存触发影像日志
+     * @param voucherId
+     */
+    private void saveFlowLog(Long voucherId, String operatorType){
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_sitflowlog");
+
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("creator", RequestContext.get().getCurrUserId());
+        dynamicObject.set("modifier", RequestContext.get().getCurrUserId());
+        dynamicObject.set("nckd_billtype", "gl_voucher");
+        dynamicObject.set("nckd_voucherid", voucherId);
+        dynamicObject.set("nckd_operatortype", operatorType);
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(dynamicObject.getDataEntityType().getName(), dynamicObject, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, dynamicObject);
+
+        if(StringUtils.isEmpty(billno)){
+            billno = UUID.randomUUID().toString().replace("-", "");
+            billno = (billno == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : billno;
+        }
+        dynamicObject.set("number", billno);
+
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+    }
+}

+ 163 - 0
main/java/kd/cosmic/jkjt/fi/gl/opplugin/VoucherSubmitCheckNumberOpPlugin.java

@@ -0,0 +1,163 @@
+package kd.cosmic.jkjt.fi.gl.opplugin;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.business.plugin.AbstractUniqueBillno;
+import kd.bos.business.plugin.UniqueBillno;
+import kd.bos.business.plugin.mode.CodeRuleModeFactory;
+import kd.bos.business.plugin.mode.CodeRuleNumberMode;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.coderule.opplugin.util.OrgUtil;
+import kd.bos.dataentity.RefObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.BillEntityType;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.fi.gl.util.QFBuilder;
+import kd.fi.gl.util.voucher.VoucherNumberUtils;
+
+import java.util.*;
+
+/**
+ * @description 凭证提交操作插件,用来解决标准产品API写入的凭证不会验重的问题
+ * @author wanghaiwu_kd
+ * @date 2024/12/09
+ */
+public class VoucherSubmitCheckNumberOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(VoucherSubmitCheckNumberOpPlugin.class);
+
+    protected AbstractUniqueBillno uniqueBillno = new UniqueBillno();
+    protected List<CodeRuleNumberMode> numberModes;
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+
+        if (this.operateOption.tryGetVariableValue("webapitag_of_datasource", new RefObject())) {
+            String datasource = this.operateOption.getVariableValue("webapitag_of_datasource");
+            if (String.valueOf(true).equals(datasource)) {
+                DynamicObject[] dataEntities = e.getDataEntities();
+
+                if(dataEntities.length == 1){
+                    this.numberModes = CodeRuleModeFactory.newInstance(this.operateOption, "AbstractCodeRule", this.getBillNoField((BillEntityType)this.billEntityType));
+
+                    singleGenerateNumber(dataEntities[0]);
+                }
+            }
+        }
+    }
+
+    private void singleGenerateNumber(DynamicObject dynamicObject) {
+        BillEntityType billEntityType = (BillEntityType)dynamicObject.getDataEntityType();
+        CodeRuleInfo codeRuleInfo = this.getCodeRuleInfo(dynamicObject);
+        if (codeRuleInfo != null) {
+            this.singleGenerateNewNumber(dynamicObject, billEntityType, codeRuleInfo);
+        }
+    }
+
+    protected CodeRuleInfo getCodeRuleInfo(DynamicObject dynamicObject) {
+//        String entityNum = dynamicObject.getDataEntityType().getName();
+//        String orgId = OrgUtil.getMainOrgId(dynamicObject);
+//        return CodeRuleServiceHelper.getCodeRule(entityNum, dynamicObject, orgId);
+        return VoucherNumberUtils.getCodeRuleInfoWithPriority(dynamicObject.getLong("org.id"));
+    }
+
+    private void singleGenerateNewNumber(DynamicObject dynamicObject, BillEntityType billEntityType, CodeRuleInfo codeRuleInfo) {
+        logger.info("[CodeRuleOp]生成编号");
+        String billNoFldKey = this.getBillNoField(billEntityType);
+        this.commonGenerateNewNumber(dynamicObject, codeRuleInfo, billNoFldKey);
+    }
+
+    private void commonGenerateNewNumber(DynamicObject dynamicObject, CodeRuleInfo codeRuleInfo, String billNoFldKey) {
+        String newNumber = dynamicObject.getString(billNoFldKey);
+        newNumber = this.generateNumberForUniqueBillno(dynamicObject, billNoFldKey, codeRuleInfo, newNumber);
+
+        dynamicObject.set(billNoFldKey, newNumber);
+    }
+
+    private String generateNumberForUniqueBillno(DynamicObject dynamicObject, String billNoFldKey, CodeRuleInfo codeRuleInfo, String number) {
+        String entityNum = dynamicObject.getDataEntityType().getName();
+        int loopCount = 0;
+
+        while(true) {
+            boolean existBillNum = this.uniqueBillno.checkReatedInDB(entityNum, this.getQFilterExistUniqueBillNo(billNoFldKey, number, dynamicObject));
+            if (!existBillNum) {
+                logger.info("[CodeRuleOp]尝试" + loopCount + "次后发现不重复的编码");
+                break;
+            }
+
+            number = this.executeGenerateProcess(dynamicObject, codeRuleInfo);
+            ++loopCount;
+            if (loopCount > 50) {
+                logger.info("[CodeRuleOp]尝试50次后依然没有发现不重复的编码,停止继续消耗流水号");
+                break;
+            }
+        }
+
+        return number;
+    }
+
+    protected QFilter[] getQFilterExistUniqueBillNo(String billNoFldKey, String number, DynamicObject voucherDyn) {
+        logger.info("getQFilterExistUniqueBillNo is called.");
+
+//        ((VoucherCodeRuleOp.VoucherUniqueBillNo)this.uniqueBillno).currentCheckVoucherId = voucherDyn.getLong("id");
+
+        Set<String> enhancedSortItems = VoucherNumberUtils.getEnhancedSortItems(this.getCodeRuleInfo(voucherDyn));
+        JSONObject groupByInfos = VoucherNumberUtils.getGroupByInfos(voucherDyn, enhancedSortItems);
+        QFBuilder qfBuilder = VoucherNumberUtils.transJsonToQFBuilder(groupByInfos);
+        qfBuilder.add("billno", "=", number);
+        qfBuilder.add("billstatus", "in", new Object[]{"B", "C", "D"});
+        return qfBuilder.toArray();
+    }
+
+    private String executeGenerateProcess(DynamicObject dynamicObject, CodeRuleInfo codeRuleInfo) {
+        String number = "";
+        if (null == this.numberModes) {
+            return number;
+        }
+        try {
+            for (CodeRuleNumberMode numberMode : this.numberModes) {
+                number = numberMode.getNumber(dynamicObject, codeRuleInfo);
+            }
+        }
+        catch (KDException e) {
+            if (null != e.getErrorCode() && StringUtils.equals((CharSequence)"CODERULE_NO_EXECUTE", (CharSequence)e.getErrorCode().getCode())) {
+                return number;
+            }
+            if (e.getErrorCode() != null && "ERRCODE_CODERULE_VALIDATE_NUMBER".equals(e.getErrorCode().getCode())) {
+                throw new KDBizException(e.getErrorCode(), new Object[0]);
+            }
+            logger.error((Throwable)e);
+            throw new KDException(e.getErrorCode(), new Object[0]);
+        }
+        return number;
+    }
+
+    protected String getBillNoField(BillEntityType billEntityType) {
+        return billEntityType.getBillNo();
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+    }
+}

+ 262 - 0
main/java/kd/cosmic/jkjt/fi/gl/report/AccBalanceFormRptExtPlugin.java

@@ -0,0 +1,262 @@
+package kd.cosmic.jkjt.fi.gl.report;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.datamodel.ListSelectedRow;
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.entity.report.IReportListModel;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.bos.form.*;
+import kd.bos.form.control.events.ClickListener;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.list.ListShowParameter;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.report.ReportList;
+import kd.bos.report.plugin.AbstractReportFormPlugin;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.fi.gl.accsys.AccSysUtil;
+import kd.fi.gl.report.NavToAssRpt;
+import java.util.*;
+import java.util.stream.Collectors;
+import kd.fi.cas.util.EmptyUtil;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/07/12
+ * @description 科目余额表报表插件
+ * 功能:1、增加核算维度余额表按钮,无核算维度科目可以根据下级科目的核算维度关联查询
+ */
+public class AccBalanceFormRptExtPlugin extends AbstractReportFormPlugin implements ClickListener {
+    public static final String KEY_BTN_NAVASSISTBAL = "nckd_navassistbal";
+
+    public AccBalanceFormRptExtPlugin() {
+    }
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        this.addItemClickListeners(new String[]{"toolbarap"});
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+
+        switch (evt.getItemKey()) {
+            case KEY_BTN_NAVASSISTBAL:
+                ReportList control = (ReportList)this.getControl("reportlistap");
+                int[] selectRows = control.getEntryState().getSelectedRows();
+                if (selectRows.length == 0) {
+                    this.getView().showTipNotification(ResManager.loadKDString("请选中一行联查核算维度余额表。", "AccBalanceFormRpt_2", "fi-gl-formplugin", new Object[0]));
+                    return;
+                } else {
+                    IReportListModel reportModel = control.getReportModel();
+                    DynamicObject rowData = reportModel.getRowData(selectRows[0]);
+                    DynamicObject account = rowData.getDynamicObject("accountnumber");
+                    if (account == null) {
+                        this.getView().showTipNotification(ResManager.loadKDString("请选中明细行。", "AccBalanceFormRpt_3", "fi-gl-formplugin", new Object[0]));
+                        return;
+                    } else {
+                        DynamicObject period = (DynamicObject)this.getModel().getValue("endperiod");
+                        Date endDate = period.getDate("enddate");
+                        QFilter fstartDate = new QFilter("startdate", "<=", endDate);
+                        QFilter fendDate = new QFilter("enddate", ">=", endDate);
+                        QFilter fmasterId = new QFilter("masterid", "=", account.getLong("masterid"));
+                        Long orgID = this.getParentOrg();
+                        QFilter fids = BaseDataServiceHelper.getBaseDataFilter("bd_accountview", orgID);
+                        DynamicObject[] acctDyo = BusinessDataServiceHelper.load("bd_accountview", "id, checkitementry.asstactitem", new QFilter[]{fids, fstartDate, fendDate, fmasterId});
+                        DynamicObjectCollection acctEntry = null;
+                        if (acctDyo.length == 0) {
+                            DynamicObject acct = BusinessDataServiceHelper.loadSingle(account.get("id"), EntityMetadataCache.getDataEntityType("bd_accountview"));
+                            acctEntry = acct.getDynamicObjectCollection("checkitementry");
+                        } else {
+                            acctEntry = acctDyo[0].getDynamicObjectCollection("checkitementry");
+                        }
+
+                        if (acctEntry.isEmpty()) {
+                            //所选科目无核算维度,查询下级存在核算维度的科目
+                            navToAssRptExt(account);
+                        } else {
+                            //所选科目有核算维度,走标准的逻辑
+                            ReportQueryParam queryParam = this.getQueryParam();
+                            DynamicObjectCollection orgs = queryParam.getFilter().getDynamicObjectCollection("orgs");
+                            if (orgs.isEmpty()) {
+                                return;
+                            } else {
+                                Long assgrpId = rowData.getLong("assgrp_id");
+                                (new NavToAssRpt.Builder(queryParam, this.getView())).navKey("gl_rpt_assistbalance").orgId(((DynamicObject)orgs.get(0)).getLong("id")).accountId(account.getLong("id")).assgrpId(assgrpId).build().apply();
+                            }
+                        }
+                    }
+                }
+            default:
+        }
+    }
+
+    public void navToAssRptExt(DynamicObject account){
+        String longNumber = account.getString("longnumber");
+        Object value = this.getModel().getValue("accounttable_id");
+        DynamicObject period = (DynamicObject)this.getModel().getValue("endperiod");
+        Date endDate = period.getDate("enddate");
+        Long orgID = this.getParentOrg();
+        if (this.hasProperty("orgs")) {
+            DynamicObjectCollection orgs = (DynamicObjectCollection)this.getModel().getValue("orgs");
+            List<Long> orgList = (List)orgs.stream().mapToLong((x) -> {
+                return x.getLong("fbasedataid_id");
+            }).boxed().collect(Collectors.toList());
+            orgID = getParentOrgByChildre(orgList);
+        }
+
+        QFilter faccounttable = new QFilter("accounttable", "=", value);
+        QFilter fstartDate = new QFilter("startdate", "<=", endDate);
+        QFilter fendDate = new QFilter("enddate", ">=", endDate);
+        QFilter flongnumber = new QFilter("longnumber", "like",longNumber + "_%");
+        QFilter fids = BaseDataServiceHelper.getBaseDataFilter("bd_accountview", orgID);
+
+        //查询选择记录的所有下级科目
+        DynamicObject[] acctDyo = BusinessDataServiceHelper.load("bd_accountview", "id, checkitementry.asstactitem",
+                new QFilter[]{fids, fstartDate, fendDate, faccounttable, flongnumber});
+
+        String acctIds = "";
+        //定义科目集合(当前科目下级有核算维度的科目)
+        List<Long> itemIds = new ArrayList<>();
+        //定义核算维度
+        List<Long> acctIdList = new ArrayList<>();
+
+        for (DynamicObject acct : acctDyo) {
+            DynamicObjectCollection entry = acct.getDynamicObjectCollection("checkitementry");
+            if (entry.size() > 0) {
+                acctIdList.add(acct.getLong("id"));
+            }
+
+            for (DynamicObject entryRow : entry) {
+                DynamicObject assit = entryRow.getDynamicObject("asstactitem");
+                itemIds.add(assit.getLong("id"));
+            }
+        }
+        acctIdList = (List<Long>)acctIdList.stream().distinct().collect(Collectors.toList());
+        itemIds = (List<Long>)itemIds.stream().distinct().collect(Collectors.toList());
+
+        for (Long acctId : acctIdList) {
+            acctIds += (acctIds.length() > 0 ? "," : "") + acctId;
+        }
+
+        if(itemIds.size() > 0) {
+            this.getPageCache().put("_acctIds", acctIds);
+            QFilter qFilter = new QFilter("id", QCP.in, itemIds);
+            this.showF7Form("bd_asstacttype", true, qFilter, null, "asstselect", null);
+        } else {
+            this.getView().showTipNotification(ResManager.loadKDString("该科目无下级科目或下级科目没有核算维度,不能联查。", "AccBalanceFormRpt_4", "fi-gl-formplugin", new Object[0]));
+        }
+    }
+
+    public Long getParentOrg() {
+        String parentOrg = this.getPageCache().get("_parentOrg");
+        return StringUtils.isBlank(parentOrg) ? 0L : Long.valueOf(parentOrg);
+    }
+
+    private boolean hasProperty(String name) {
+        return this.getModel().getProperty(name) != null;
+    }
+
+    public static long getParentOrgByChildre(List<Long> filterOrgs) {
+        return AccSysUtil.getParentOrgByChildre(filterOrgs);
+    }
+
+
+
+    public void closedCallBack(ClosedCallBackEvent evt) {
+        super.closedCallBack(evt);
+
+        if ("asstselect".equals(evt.getActionId())) {
+            if (EmptyUtil.isNoEmpty(evt.getReturnData())) {
+                ReportQueryParam queryParam = this.getQueryParam();
+                DynamicObjectCollection orgs = queryParam.getFilter().getDynamicObjectCollection("orgs");
+                if (orgs.isEmpty()) {
+                    return;
+                } else {
+                    ListSelectedRowCollection returnDatas = (ListSelectedRowCollection)evt.getReturnData();
+                    if (EmptyUtil.isEmpty(returnDatas)) {
+                        return;
+                    }
+                    String asstIds = "";
+
+                    for (ListSelectedRow row : returnDatas) {
+                        asstIds += (asstIds.length() > 0 ? "," : "") + row.getPrimaryKeyValue();
+                    }
+
+                    String acctIds = this.getPageCache().get("_acctIds");
+
+                    (new NavToAssRptExt.Builder(queryParam, this.getView())).navKey("gl_rpt_assistbalance").orgId(((DynamicObject)orgs.get(0)).getLong("id")).acctIds(acctIds).assgrpIds(asstIds).build().apply();
+                }
+            } else {
+                this.getView().showTipNotification(ResManager.loadKDString("未选择核算维度,不能联查。", "AccBalanceFormRpt_4", "fi-gl-formplugin", new Object[0]));
+            }
+        }
+    }
+
+    protected void showF7Form(String formId, boolean isMultiSelect, QFilter filter, Map param, String callBackKey, Object[] selectRows) {
+        ListShowParameter parameter = ShowFormHelper.createShowListForm(formId, isMultiSelect, 0, true);
+        if (EmptyUtil.isNoEmpty(param)) {
+            parameter.getCustomParams().putAll(param);
+        }
+
+        if (EmptyUtil.isNoEmpty(callBackKey)) {
+            CloseCallBack callBack = new CloseCallBack(this, callBackKey);
+            parameter.setCloseCallBack(callBack);
+        }
+
+        if (EmptyUtil.isNoEmpty(filter)) {
+            parameter.getListFilterParameter().setFilter(filter);
+        }
+
+//        parameter.setSelectedRows(selectRows);
+        this.getView().showForm(parameter);
+    }
+
+    public static ListShowParameter createShowListForm(String formId, boolean isMultiSelect, int f7Style, boolean isLookUP) {
+        ListShowParameter para = new ListShowParameter();
+        para.setLookUp(isLookUP);
+        para.setBillFormId(formId);
+        if (isLookUP) {
+            para.getOpenStyle().setShowType(ShowType.Modal);
+            StyleCss css = new StyleCss();
+            css.setWidth("960px");
+            css.setHeight("580px");
+            para.getOpenStyle().setInlineStyleCss(css);
+            para.setFormId(getListFormId(formId, f7Style));
+            para.setF7Style(f7Style);
+        } else {
+            para.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+            FormConfig formConfig = FormMetadataCache.getListFormConfig(formId);
+            para.setFormId(formConfig.getListFormId());
+        }
+
+        para.setMultiSelect(isMultiSelect);
+        para.setShowTitle(false);
+        para.setHasRight(true);
+        return para;
+    }
+
+    private static String getListFormId(String formId, int f7Style) {
+        FormConfig formConfig = FormMetadataCache.getListFormConfig(formId);
+        switch (f7Style) {
+            case 1:
+                if ("bos_user".equals(formId)) {
+                    return formConfig.getF7ListFormId();
+                }
+
+                return "bos_orgtreelistf7";
+            case 2:
+                return "bos_listf7";
+            default:
+                return formConfig == null ? formId : formConfig.getF7ListFormId();
+        }
+    }
+}

+ 189 - 0
main/java/kd/cosmic/jkjt/fi/gl/report/NavToAssRptExt.java

@@ -0,0 +1,189 @@
+package kd.cosmic.jkjt.fi.gl.report;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.report.FilterInfo;
+import kd.bos.entity.report.FilterItemInfo;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.bos.form.IFormView;
+import kd.bos.form.ShowType;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.report.ReportShowParameter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.fi.gl.util.GLUtil;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/07/14
+ */
+public class NavToAssRptExt {
+    protected ReportQueryParam param;
+    protected IFormView view;
+    protected String acctIds;
+    protected Long orgId;
+    protected Long curId;
+    protected Long measureunitId;
+    protected String assgrpIds;
+    protected String navKey;
+
+    private static final String KEY_FIELD_ISSUB = "issubstractpl";
+
+    public void apply() {
+        ReportShowParameter report = new ReportShowParameter();
+
+        if(StringUtils.isEmpty(this.navKey)){
+            report.setFormId("gl_rpt_subsidiaryledger");
+        } else {
+            report.setFormId(this.navKey);
+        }
+        report.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+        FilterInfo info = this.param.getFilter();
+        Long org = this.orgId;
+        if (this.orgId == null || this.orgId == 0L) {
+            org = info.getLong("org");
+        }
+
+        Long bookType = info.getLong("booktype");
+        FilterInfo filter = new FilterInfo();
+        long periodType = info.getLong("periodtype");
+        long startPeriod = info.getLong("startperiod");
+        long endPeriod = info.getLong("endperiod");
+        Date dateBegin = info.getDate("datebegin");
+        Date dateEnd = info.getDate("dateend");
+        DynamicObject acct;
+        if (dateBegin != null && dateEnd != null) {
+            DynamicObject sp = GLUtil.getPeriodByDate(dateBegin, periodType);
+            acct = GLUtil.getPeriodByDate(dateEnd, periodType);
+            if (sp != null && acct != null) {
+                startPeriod = sp.getLong("id");
+                endPeriod = acct.getLong("id");
+            }
+        }
+
+        Map<String, Object> flexProp = new HashMap<String, Object>();
+        if ("gl_rpt_assistbalance".equals(this.navKey)) {
+            filter.addFilterItem("orgs", info.getValue("orgs"));
+            filter.addFilterItem("orgview", info.getValue("orgview"));
+
+            if (StringUtils.isNotEmpty(this.assgrpIds)) {
+                List<Long> flexIdList = new ArrayList<Long>();
+                for (String id : this.assgrpIds.split(",")){
+                    flexIdList.add(Long.valueOf(id));
+                }
+                QFilter qFilter = new QFilter("id", QCP.in, flexIdList);
+                String selectProperties = "id, flexfield";
+                DynamicObject[] actTypes = BusinessDataServiceHelper.load("bd_asstacttype", selectProperties, new QFilter[]{qFilter});
+                for (DynamicObject actType : actTypes) {
+                    flexProp.put(actType.getString("flexfield"), 0);
+                }
+            }
+        }
+
+        List<FilterItemInfo> ass = new ArrayList<FilterItemInfo>(flexProp.size());
+
+        for (Map.Entry<String, Object> entry2 : flexProp.entrySet()) {
+            Set<Object> vals = new HashSet<Object>();
+            ass.add(new FilterItemInfo((String)entry2.getKey(), vals, "in"));
+        }
+
+        String curStr;
+        if (this.curId != null && this.curId != 0L) {
+            curStr = String.valueOf(this.curId);
+        } else {
+            curStr = info.getString("currency");
+        }
+
+        List<Long> acctIdList = new ArrayList<Long>();
+        for (String id : this.acctIds.split(",")){
+            acctIdList.add(Long.valueOf(id));
+        }
+
+        filter.addFilterItem("org", org);
+        filter.addFilterItem("booktype", bookType);
+        filter.addFilterItem("periodtype", periodType);
+        filter.addFilterItem("startperiod", startPeriod);
+        filter.addFilterItem("endperiod", endPeriod);
+        filter.addFilterItem("accounttable", info.getLong("accounttable"));
+        filter.addFilterItem("account", acctIdList, QCP.in);
+        filter.addFilterItem("accountlevel", info.getString("accountlevel"));
+        filter.addFilterItem("currency", curStr);
+        filter.addFilterItem("measureunits", this.measureunitId);
+        filter.addFilterItem("showqty", info.getBoolean("showqty"));
+        filter.addFilterItem("showleafaccount", info.getBoolean("showleafaccount"));
+        filter.addFilterItem("nodisplayforzero", info.getBoolean("nodisplayforzero"));
+        filter.addFilterItem("balancezero", info.getBoolean("balancezero"));
+
+        if (info.containProp(KEY_FIELD_ISSUB)) {
+            filter.addFilterItem(KEY_FIELD_ISSUB, info.getBoolean(KEY_FIELD_ISSUB));
+        }
+
+        filter.setFlexFilterItems(ass);
+        ReportQueryParam queryParam = new ReportQueryParam();
+        queryParam.setFilter(filter);
+        report.setQueryParam(queryParam);
+        this.view.showForm(report);
+    }
+
+    protected NavToAssRptExt(NavToAssRptExt.Builder builder) {
+        this.param = builder.param;
+        this.view = builder.view;
+        this.acctIds = builder.acctIds;
+        this.orgId = builder.orgId;
+        this.curId = builder.curId;
+        this.measureunitId = builder.measureunitId;
+        this.assgrpIds = builder.assgrpIds;
+        this.navKey = builder.navKey;
+    }
+
+    public static class Builder {
+        private ReportQueryParam param;
+        private String navKey;
+        private IFormView view;
+        private String acctIds;
+        private Long orgId;
+        private Long curId;
+        private Long measureunitId;
+        private String assgrpIds;
+
+        public Builder(ReportQueryParam param, IFormView view) {
+            this.param = param;
+            this.view = view;
+        }
+
+        public NavToAssRptExt.Builder acctIds(String acctIds) {
+            this.acctIds = acctIds;
+            return this;
+        }
+
+        public NavToAssRptExt.Builder navKey(String navKey) {
+            this.navKey = navKey;
+            return this;
+        }
+
+        public NavToAssRptExt.Builder currencyId(Long curId) {
+            this.curId = curId;
+            return this;
+        }
+
+        public NavToAssRptExt.Builder orgId(Long orgId) {
+            this.orgId = orgId;
+            return this;
+        }
+
+        public NavToAssRptExt.Builder measureunitId(Long measureunitId) {
+            this.measureunitId = measureunitId;
+            return this;
+        }
+
+        public NavToAssRptExt.Builder assgrpIds(String assgrpIds) {
+            this.assgrpIds = assgrpIds;
+            return this;
+        }
+
+        public NavToAssRptExt build() {
+            return new NavToAssRptExt(this);
+        }
+    }
+}

+ 295 - 0
main/java/kd/cosmic/jkjt/fi/gl/task/VoucherForImageArchiveTask.java

@@ -0,0 +1,295 @@
+package kd.cosmic.jkjt.fi.gl.task;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.fi.cas.task.DateUtils;
+import kd.cosmic.jkjt.siit.HttpUtilAction;
+import kd.cosmic.jkjt.siit.SiitServerHandler;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @description:调度任务类,认领生成的凭证,后台对前一天已经触发待办的凭证自动归档
+ * @author wanghaiwu_kd
+ * @date 2024/12/24
+ */
+public class VoucherForImageArchiveTask extends AbstractTask {
+    private static final Log logger = LogFactory.getLog(VoucherForImageArchiveTask.class);
+    public static final String KEY_ENTITY_VOUCHER = "gl_voucher";
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        //1、执行创建影像待办动作
+        executeImageFlow(requestContext, map);
+        //2、执行创建影像归档动作
+        executeImageArchive(requestContext, map);
+    }
+
+    /**
+     * 创建影像待办
+     */
+    private void executeImageFlow(RequestContext requestContext, Map<String, Object> map){
+        QFilter qFilter = new QFilter("nckd_detailclaim", QCP.equals, true);
+        qFilter.and(new QFilter("nckd_isflowed", QCP.equals, false));
+
+        logger.info("VoucherForImageArchiveTask:创建影像待办查询条件:" + qFilter.toString());
+
+        DynamicObjectType type = EntityMetadataCache.getDataEntityType(KEY_ENTITY_VOUCHER);
+        //先找到批量的pkid
+        List<Object> list = QueryServiceHelper.queryPrimaryKeys(KEY_ENTITY_VOUCHER,  qFilter.toArray(), null, Integer.MAX_VALUE);
+        //根据pkid找到完整的对象
+        DynamicObject[] vouchers = BusinessDataServiceHelper.load(list.toArray(), type);
+
+        logger.info("VoucherForImageArchiveTask:查询到" + vouchers.length + "条凭证需要触发影像待办。");
+
+        List<DynamicObject> listObj = new ArrayList<>();
+        for(DynamicObject voucher : vouchers){
+            send2Siit(voucher, "flow");
+
+            listObj.add(voucher);
+
+            if(listObj.size() == 500) {
+                SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+
+                listObj.clear();
+            }
+        }
+
+        if(listObj.size() > 0) {
+            SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+        }
+    }
+
+    /**
+     * 创建影像归档
+     * @param requestContext
+     * @param map
+     */
+    private void executeImageArchive(RequestContext requestContext, Map<String, Object> map){
+        Date nowDate = new Date();
+
+        try {
+            nowDate = getCurDateForm(nowDate, true);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+
+        if(map.get("overday") != null){
+            int overday = Integer.valueOf(map.get("overday").toString());
+            overday = -1 * overday;
+            nowDate = DateUtils.getDateAfterAddDays(nowDate, overday);
+        }
+
+        QFilter qFilter = new QFilter("nckd_detailclaim", QCP.equals, true);
+        qFilter.and(new QFilter("nckd_isflowed", QCP.equals, true));
+        qFilter.and(new QFilter("nckd_flowtime", QCP.less_than, nowDate));
+        qFilter.and(new QFilter("nckd_isarchived", QCP.equals, false));
+
+        logger.info("VoucherForImageArchiveTask:创建影像归档查询条件:" + qFilter.toString());
+
+        DynamicObjectType type = EntityMetadataCache.getDataEntityType(KEY_ENTITY_VOUCHER);
+        //先找到批量的pkid
+        List<Object> list = QueryServiceHelper.queryPrimaryKeys(KEY_ENTITY_VOUCHER,  qFilter.toArray(), null, Integer.MAX_VALUE);
+        //根据pkid找到完整的对象
+        DynamicObject[] vouchers = BusinessDataServiceHelper.load(list.toArray(), type);
+
+        logger.info("VoucherForImageArchiveTask:查询到" + vouchers.length + "条凭证需要触发影像归档。");
+
+        List<DynamicObject> listObj = new ArrayList<>();
+        for(DynamicObject voucher : vouchers){
+            send2Siit(voucher, "archive");
+
+            listObj.add(voucher);
+
+            if(listObj.size() == 500) {
+                SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+
+                listObj.clear();
+            }
+        }
+
+        if(listObj.size() > 0) {
+            SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+        }
+    }
+
+    /**
+     * 触发影像待办、归档操作
+     * @param voucher
+     * @param operateType
+     */
+    private void send2Siit(DynamicObject voucher, String operateType){
+        Long id = voucher.getLong("id");
+        String billno = voucher.getString("billno");
+
+        DynamicObject org = voucher.getDynamicObject("org");
+        DynamicObject  userInfo =  UserServiceHelper.getUserInfoByID(UserServiceHelper.getCurrentUserId(),"number,username");
+
+        String username = userInfo.getString("username");
+        String barCode = id.toString();
+
+        Map<String, String> headers = new ConcurrentHashMap<>(1);
+        headers.put("Content-Type", "application/json");
+        JSONObject jsonSafety = SiitServerHandler.getSafetyParams(barCode, username);
+
+        if("flow".equals(operateType)){
+            //触发待办
+            if(!isExists(Long.valueOf(String.valueOf(id)), "flow")) {
+                JSONObject jsonFlow = SiitServerHandler.getFlowParams(barCode, org.getString("number"), username, String.valueOf(id));
+
+                //触发扫描接口调入参数
+                JSONObject jsonFlowPost = new JSONObject();
+                jsonFlowPost.put("safety", jsonSafety);
+                jsonFlowPost.put("params", jsonFlow);
+
+                logger.info("VoucherForImageArchiveTask:待办接口入参, " + jsonFlowPost.toJSONString());
+
+                String uri = SiitServerHandler.url + "/siitservice/startFlow/addFlow";
+                String appReturnStr = HttpUtilAction.doPost(uri, jsonFlowPost.toJSONString(), headers);
+
+                if(StringUtils.isEmpty(appReturnStr)){
+                    logger.info("VoucherForImageArchiveTask:凭证(" + barCode + ", " + billno + ")触发影像待办失败");
+                    return;
+                }
+
+                JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+
+                logger.info("Siit Log" + appTokenJO.toString());
+
+                if (!appTokenJO.getString("status").equals("true")) {
+                    logger.info("VoucherForImageArchiveTask:凭证(" + barCode + ", " + billno + ")触发待办失败, " + (appTokenJO == null ? "" : appTokenJO.toString()));
+                    return;
+                }
+
+                //触发待办成功,记录触发日志
+                saveFlowLog(Long.valueOf(String.valueOf(id)), "flow");
+
+                voucher.set("nckd_isflowed", true);
+                voucher.set("nckd_flowtime", new Date());
+            }
+        } else if("archive".equals(operateType)){
+            //触发归档
+            if(isExists(Long.valueOf(String.valueOf(id)), "flow")
+                    && !isExists(Long.valueOf(String.valueOf(id)), "archive")) {
+                //触发归档操作
+                JSONObject jsonArchive = SiitServerHandler.getAotuArchive(barCode, org.getString("number"), username);
+
+                //触发归档接口调入参数
+                JSONObject jsonArchivePost = new JSONObject();
+                jsonArchivePost.put("safety", jsonSafety);
+                jsonArchivePost.put("params", jsonArchive);
+
+                //对于以凭证id为barCode的凭证,需要先要触发扫描接口(待办),以上游业务单据为barCode的凭证不需要触发待办
+                logger.info("VoucherForImageArchiveTask:归档接口入参, " + jsonArchive.toJSONString());
+
+                String uri = SiitServerHandler.url + "/siitservice/archiveApi/aotuArchive";
+                String appReturnStr = HttpUtilAction.doPost(uri, jsonArchivePost.toJSONString(), headers);
+
+                if(StringUtils.isEmpty(appReturnStr)){
+                    logger.info("VoucherForImageArchiveTask:凭证(" + barCode + ", " + billno + ")触发影像归档失败");
+                    return;
+                }
+
+                logger.info("VoucherForImageArchiveTask:归档接口出参, " + appReturnStr);
+
+                JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+
+                logger.info("VoucherForImageArchiveTask:" + appTokenJO.toString());
+
+                if (!appTokenJO.getString("status").equals("true")) {
+                    logger.info("VoucherForImageArchiveTask:凭证(" + barCode + ", " + billno + ")触发归档失败, " + (appTokenJO == null ? "" : appTokenJO.toString()));
+                    return;
+                }
+
+                //触发归档成功,记录触发日志
+                saveFlowLog(Long.valueOf(String.valueOf(id)), "archive");
+
+                voucher.set("nckd_isarchived", true);
+                voucher.set("nckd_archivetime", new Date());
+            }
+        }
+    }
+
+    /**
+     * 判断凭证是否已出发过待办
+     * @param voucherId
+     * @return
+     */
+    private boolean isExists(Long voucherId, String operatorType){
+        boolean isExists = false;
+        QFilter qFilter = new QFilter("nckd_voucherid", QCP.equals, voucherId);
+        qFilter.and(new QFilter("nckd_billtype", QCP.equals, "gl_voucher"));
+        qFilter.and(new QFilter("nckd_operatortype", QCP.equals, operatorType));
+
+        DynamicObject flowLog = BusinessDataServiceHelper.loadSingle("nckd_sitflowlog", qFilter.toArray());
+        if(flowLog != null){
+            isExists = true;
+        }
+
+        return isExists;
+    }
+
+    /**
+     * 保存触发影像日志
+     * @param voucherId
+     */
+    private void saveFlowLog(Long voucherId, String operatorType){
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_sitflowlog");
+
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("creator", RequestContext.get().getCurrUserId());
+        dynamicObject.set("modifier", RequestContext.get().getCurrUserId());
+        dynamicObject.set("nckd_billtype", "gl_voucher");
+        dynamicObject.set("nckd_voucherid", voucherId);
+        dynamicObject.set("nckd_operatortype", operatorType);
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(dynamicObject.getDataEntityType().getName(), dynamicObject, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, dynamicObject);
+
+        if(StringUtils.isEmpty(billno)){
+            billno = UUID.randomUUID().toString().replace("-", "");
+            billno = (billno == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : billno;
+        }
+        dynamicObject.set("number", billno);
+
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+    }
+
+    /**
+     * 日期格式化
+     * @param date
+     * @param isStart
+     * @return
+     * @throws ParseException
+     */
+    public Date getCurDateForm(Date date, boolean isStart) throws ParseException {
+        SimpleDateFormat shortformat = new SimpleDateFormat("yyyy-MM-dd");
+        SimpleDateFormat longformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateStr = shortformat.format(date);
+        String suffix = " 00:00:00";
+        if (!isStart) {
+            suffix = " 23:59:59";
+        }
+
+        return longformat.parse(dateStr + suffix);
+    }
+}

+ 905 - 0
main/java/kd/cosmic/jkjt/imc/rim/formplugin/message/service/ExcelInvoiceSaveServiceEx.java

@@ -0,0 +1,905 @@
+package kd.cosmic.jkjt.imc.rim.formplugin.message.service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.db.tx.TX;
+import kd.bos.db.tx.TXHandle;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.orm.util.CollectionUtils;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.imc.rim.common.constant.DeductionConstant;
+import kd.imc.rim.common.constant.ExpenseConstant;
+import kd.imc.rim.common.constant.InputInvoiceTypeEnum;
+import kd.imc.rim.common.message.exception.MsgException;
+import kd.imc.rim.common.service.InvoiceLog;
+import kd.imc.rim.common.utils.UUID;
+import kd.imc.rim.common.utils.*;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+public class ExcelInvoiceSaveServiceEx {
+    private static Log logger = LogFactory.getLog(ExcelInvoiceSaveServiceEx.class);
+    protected Long invoiceType;
+    private static final String[] invoiceFields = new String[]{"invoice_code", "invoice_no", "invoice_date", "check_code", "invoice_amount", "invoice_status", "total_tax_amount", "saler_name", "saler_tax_no", "buyer_name", "buyer_tax_no", "total_amount", "check_status"};
+    private static final String[] deductionFields = new String[]{"deduction_flag", "deduction_purpose", "authenticate_flag", "select_time", "authenticate_time", "tax_period", "transport_deduction", "effective_tax_amount"};
+
+    public ExcelInvoiceSaveServiceEx(Long invoiceType) {
+        this.invoiceType = invoiceType;
+    }
+
+    private DynamicObject getOrgId(String id) {
+        QFilter filter = new QFilter("id", QCP.equals, Long.parseLong(id));
+        DynamicObject[] load = BusinessDataServiceHelper.load("bos_org", "id,number", new QFilter[]{filter});
+        //DynamicObject obj = QueryServiceHelper.queryOne("bos_org", "id", new QFilter[]{filter});
+        if (load == null || load.length==0){
+            return null;
+        }
+        return load[0];
+    }
+
+    public JSONObject save(JSONObject invoiceJson) {
+        TXHandle h = TX.required();
+        Throwable var3 = null;
+
+        JSONObject var13;
+        try {
+            try {
+                long start = System.currentTimeMillis();
+                DynamicObject invoiceObject = this.getInvoiceDynamicObject(invoiceJson);
+                this.setInvoiceExcelFieldValue(invoiceJson, invoiceObject, Boolean.FALSE);
+                this.setDeductionFieldValue(this.invoiceType, invoiceObject, invoiceJson);
+                DynamicObject mainObject = this.getExcelMainDynamicObject(invoiceObject, invoiceJson);
+                Boolean newFlag = this.checkIdIsNull(mainObject.getPkValue());
+                this.setExcelMainFieldValue(invoiceJson, mainObject);
+                DeductionConstant.searchBlacklist(mainObject.getString("saler_tax_no"));
+                this.setDeductionFieldValue(this.invoiceType, mainObject, invoiceJson);
+                this.setDeductionFlag(invoiceJson, invoiceObject);
+                this.setDeductionFlag(invoiceJson, mainObject);
+                this.setMainInvoiceInfo(mainObject);
+                InvoiceCheckUtils.enIDNum(invoiceObject);
+                String tax_org_id = invoiceJson.getString("tax_org");
+                //Long orgId = 0L;
+                DynamicObject hsOrg = getOrgId(tax_org_id);
+                if (hsOrg!=null){
+                    Long tax_org_id_l = Long.parseLong(tax_org_id);
+                    mainObject.set("org", hsOrg);
+                    mainObject.set("org_id", tax_org_id_l);
+                    mainObject.set("tax_org", hsOrg);
+                    mainObject.set("tax_org_id", tax_org_id_l);
+                }else{
+                    throw new RuntimeException("组织id:"+tax_org_id+"未匹配到审核启用的组织");
+                }
+                Long orgId = Long.parseLong(tax_org_id);
+                DynamicObject[] array = new DynamicObject[]{mainObject};
+
+                SaveServiceHelper.save(new DynamicObject[]{invoiceObject});
+                SaveServiceHelper.save(new DynamicObject[]{mainObject});
+                invoiceJson.put("newFlag", newFlag);
+                invoiceJson.put("serialNo", invoiceObject.getString("serial_no"));
+                invoiceJson.put("serial_no", invoiceObject.getString("serial_no"));
+                invoiceJson.put("invoiceId", invoiceObject.get("id"));
+                invoiceJson.put("mainId", mainObject.get("id"));
+                invoiceJson.put("tenantNo", invoiceObject.getString("tenant_no"));
+                invoiceJson.put("invoice_info", mainObject.getString("invoice_info"));
+                String serialNo = invoiceObject.getString("serial_no");
+                JSONObject logInfo = new JSONObject();
+                logInfo.put("mainId", mainObject.get("id"));
+                logInfo.put("invId", invoiceObject.get("id"));
+                logInfo.put("trace", RequestContext.get().getTraceId());
+                //Long orgId = DynamicObjectUtil.getDynamicObjectLongValue(mainObject.get("org"));
+
+
+                String resource = invoiceJson.getString("resource");
+                InvoiceLog.insert("save", serialNo, logInfo.toJSONString(), orgId, resource, invoiceJson.getString("collect_type"));
+                logger.info(invoiceJson.getString("fileName") + "第" + invoiceJson.getString("fileIndex") + "页,InvoiceSaveService.save统计信息:" + invoiceObject.getString("serial_no") + ",耗时:" + (System.currentTimeMillis() - start));
+                var13 = invoiceJson;
+            } catch (Throwable var23) {
+                h.markRollback();
+                throw var23;
+            }
+        } catch (Throwable var24) {
+            var3 = var24;
+            throw var24;
+        } finally {
+            if (h != null) {
+                if (var3 != null) {
+                    try {
+                        h.close();
+                    } catch (Throwable var22) {
+                        var3.addSuppressed(var22);
+                    }
+                } else {
+                    h.close();
+                }
+            }
+
+        }
+
+        return var13;
+    }
+
+    public DynamicObject getInvoiceDynamicObject(JSONObject invoiceJson) {
+        DynamicObject savedObject = this.checkInvoiceExist(invoiceJson);
+        String tenantNo = TenantUtils.getTenantNo();
+        String entityId = InputInvoiceTypeEnum.getEntity(this.invoiceType);
+        DynamicObject invoiceDynamicObject = null;
+        String userDelete = invoiceJson.getString("delete");
+        if (StringUtils.isEmpty(userDelete)) {
+            userDelete = "1";
+        }
+
+        if (ObjectUtils.isEmpty(savedObject)) {
+            invoiceDynamicObject = this.newDefaultDynamicObject(entityId, tenantNo, userDelete);
+        } else {
+            DynamicObject idObject = QueryServiceHelper.queryOne(entityId, "id", new QFilter[]{new QFilter("serial_no", "=", savedObject.get("serial_no"))});
+            if (idObject != null && idObject.get("id") != null) {
+                invoiceDynamicObject = BusinessDataServiceHelper.loadSingle(idObject.get("id"), entityId);
+                invoiceDynamicObject.set("delete", userDelete);
+            } else {
+                invoiceDynamicObject = this.newDefaultDynamicObject(entityId, tenantNo, userDelete);
+            }
+        }
+
+        return invoiceDynamicObject;
+    }
+
+    private DynamicObject newDefaultDynamicObject(String entityId, String tenantNo, String userDelete) {
+        DynamicObject invoiceDynamicObject = BusinessDataServiceHelper.newDynamicObject(entityId);
+        invoiceDynamicObject.set("tenant_no", tenantNo);
+        invoiceDynamicObject.set("serial_no", UUID.randomUUIDZero());
+        if (StringUtils.isEmpty(userDelete)) {
+            invoiceDynamicObject.set("delete", "1");
+        } else {
+            invoiceDynamicObject.set("delete", userDelete);
+        }
+
+        invoiceDynamicObject.set("expense_status", "1");
+        invoiceDynamicObject.set("original_state", "0");
+        invoiceDynamicObject.set("createtime", new Date());
+        return invoiceDynamicObject;
+    }
+
+    public JSONObject fillDefaultInvoice(JSONObject invoice) {
+        Date startDate = DateUtils.stringToDate("2019-03-31 23:59:59", "yyyy-MM-dd HH:mm:ss");
+        Date invoiceDate = invoice.getDate("invoice_date");
+        BigDecimal invoiceAmount = invoice.getBigDecimal("invoice_amount");
+        if (ObjectUtils.isEmpty(invoiceAmount)) {
+            invoiceAmount = BigDecimal.ZERO;
+        }
+
+        BigDecimal fuelAmount = invoice.getBigDecimal("fuel_surcharge");
+        if (ObjectUtils.isEmpty(fuelAmount)) {
+            fuelAmount = BigDecimal.ZERO;
+        }
+
+        BigDecimal constructionFee = invoice.getBigDecimal("airport_construction_fee");
+        if (ObjectUtils.isEmpty(constructionFee)) {
+            constructionFee = BigDecimal.ZERO;
+        }
+
+        BigDecimal otherFee = invoice.getBigDecimal("other_amount");
+        if (ObjectUtils.isEmpty(otherFee)) {
+            otherFee = BigDecimal.ZERO;
+        }
+
+        BigDecimal totalAmount = invoice.getBigDecimal("total_amount");
+        if (ObjectUtils.isEmpty(totalAmount)) {
+            totalAmount = BigDecimal.ZERO;
+        }
+
+        String customerName = invoice.getString("customer_name");
+        BigDecimal taxRate = DeductionUtils.taxRateByInvoiceDate(invoiceDate, InputInvoiceTypeEnum.AIR_INVOICE.getCode(), new Object[0]);
+        invoice.put("tax_rate", taxRate);
+        BigDecimal totalTaxAmount = DeductionUtils.airTaxAmountByInvoiceDate(invoiceDate, invoiceAmount, fuelAmount, customerName, otherFee, totalAmount, constructionFee, invoice.getString("place_of_departure"), invoice.getString("destination"));
+        invoice.put("total_tax_amount", totalTaxAmount);
+        return invoice;
+    }
+
+    public DynamicObject checkInvoiceExist(JSONObject invoiceJson) {
+        String entityId = InputInvoiceTypeEnum.getEntity(this.invoiceType);
+        String tenantNo = TenantUtils.getTenantNo();
+        QFilter tenantFilter = new QFilter("tenant_no", "=", tenantNo);
+        QFilter invoiceTypeFilter = new QFilter("invoice_type", "=", this.invoiceType);
+        QFilter invoiceCodeFilter = new QFilter("invoice_code", "=", invoiceJson.getString("invoice_code"));
+        QFilter invoiceNoFilter = new QFilter("invoice_no", "=", invoiceJson.getString("invoice_no"));
+        DynamicObject invoiceObject = null;
+        DynamicObject serialObject = QueryServiceHelper.queryOne("rim_invoice", "id, serial_no, expense_status", new QFilter[]{tenantFilter, invoiceCodeFilter, invoiceNoFilter, invoiceTypeFilter});
+        if (serialObject != null) {
+            return serialObject;
+        } else {
+            QFilter noFilter = null;
+            QFilter codeFilter = null;
+            if (InputInvoiceTypeEnum.AIR_INVOICE.getCode().equals(this.invoiceType)) {
+                codeFilter = new QFilter("eticket_no", "=", invoiceJson.getString("eticket_no"));
+                noFilter = new QFilter("print_num", "=", invoiceJson.getString("print_num"));
+            }
+
+            if (InputInvoiceTypeEnum.TRAIN_INVOICE.getCode().equals(this.invoiceType)) {
+                codeFilter = new QFilter("train_num", "=", invoiceJson.getString("train_num"));
+                noFilter = new QFilter("sequence_no", "=", invoiceJson.getString("sequence_no"));
+            }
+
+            if (InputInvoiceTypeEnum.TAX_PROOF.getCode().equals(this.invoiceType)) {
+                noFilter = new QFilter("tax_paid_proof_no", "=", invoiceJson.getString("tax_paid_proof_no"));
+            }
+
+            if (InputInvoiceTypeEnum.TRAIN_REFUND.getCode().equals(this.invoiceType)) {
+                noFilter = new QFilter("number", "=", invoiceJson.getString("number"));
+            }
+
+            if (InputInvoiceTypeEnum.HGJKS.getCode().equals(this.invoiceType)) {
+                noFilter = new QFilter("custom_declaration_no", "=", invoiceJson.getString("custom_declaration_no"));
+            }
+
+            if (InputInvoiceTypeEnum.ELECTRIC_ORDINARY.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.ELECTRIC_SPECIAL.getCode().equals(this.invoiceType)) {
+                noFilter = new QFilter("invoice_no", "=", invoiceJson.getString("invoice_no"));
+            }
+
+            if (codeFilter != null && noFilter != null) {
+                serialObject = QueryServiceHelper.queryOne(entityId, "serial_no", new QFilter[]{tenantFilter, codeFilter, noFilter});
+            } else if (noFilter != null) {
+                serialObject = QueryServiceHelper.queryOne(entityId, "serial_no", new QFilter[]{tenantFilter, noFilter});
+            }
+
+            DynamicObject mainObject = null;
+            if (serialObject != null && StringUtils.isNotBlank(serialObject.getString("serial_no"))) {
+                mainObject = QueryServiceHelper.queryOne("rim_invoice", "id, expense_status, serial_no", new QFilter[]{new QFilter("serial_no", "=", serialObject.getString("serial_no"))});
+            }
+
+            return mainObject;
+        }
+    }
+
+    public void setInvoiceExcelFieldValue(JSONObject invoiceJson, DynamicObject dynamicObject, Boolean isMain) {
+        Set<String> jsonKeys = invoiceJson.keySet();
+        if (!CollectionUtils.isEmpty(jsonKeys)) {
+            dynamicObject.set("invoice_type", this.invoiceType);
+            if (ObjectUtils.isEmpty(dynamicObject.get("creator"))) {
+                dynamicObject.set("creator", RequestContext.get().getUserId());
+            }
+
+            dynamicObject.set("billstatus", "C");
+            dynamicObject.set("modifytime", new Date());
+            dynamicObject.set("billno", invoiceJson.getString("invoice_code") + "-" + invoiceJson.getString("invoice_no"));
+            dynamicObject.set("aws_serial_no", invoiceJson.getString("serial_no"));
+            if (dynamicObject.get("createtime") == null) {
+                dynamicObject.set("createtime", new Date());
+            }
+
+            this.setTableField(invoiceJson, dynamicObject, isMain);
+            if (StringUtils.equals("1", invoiceJson.getString("resoureseAPI"))) {
+                JSONArray items = invoiceJson.getJSONArray("items");
+                if (items != null && !items.isEmpty()) {
+                    boolean needClear = true;
+
+                    for(int i = 0; i < items.size(); ++i) {
+                        JSONObject item = items.getJSONObject(i);
+                        item.put("needClear", needClear);
+                        this.setTableField(item, dynamicObject, isMain);
+                        needClear = false;
+                    }
+                }
+            }
+
+            Long orgId = this.getOrgId(invoiceJson);
+            Object taxOrg = invoiceJson.get("tax_org");
+            if (this.overrideOrg(dynamicObject)) {
+                dynamicObject.set("org", orgId);
+            }
+
+            if (taxOrg != null) {
+                dynamicObject.set("tax_org", taxOrg);
+            } else if (InputInvoiceTypeEnum.needCheck(this.invoiceType)) {
+                dynamicObject.set("tax_org", TenantUtils.getTaxOrgId((Long)null, invoiceJson.getString("buyer_tax_no")));
+            } else {
+                dynamicObject.set("tax_org", TenantUtils.getTaxOrgId(orgId));
+            }
+
+        }
+    }
+
+    private Boolean isAllowEdit(DynamicObject dynamicObject) {
+        String tenantNo = TenantUtils.getTenantNo();
+        String serialNo = dynamicObject.getString("serial_no");
+        QFilter invoiceFilter = (new QFilter("serial_no", "=", serialNo)).and("tenant_no", "=", tenantNo);
+        DynamicObject checkObj = QueryServiceHelper.queryOne("rim_invoice", "invoice_type, check_status, expense_status", invoiceFilter.toArray());
+        if (checkObj != null) {
+            Long type = checkObj.getLong("invoice_type");
+            String expenseStatus = checkObj.getString("expense_status");
+            if (StringUtils.isNotEmpty(expenseStatus) && !"1".equals(expenseStatus)) {
+                return Boolean.FALSE;
+            }
+
+            String checkStatus = checkObj.getString("check_status");
+            if (InputInvoiceTypeEnum.needCheck(type) && "1".equals(checkStatus)) {
+                return Boolean.FALSE;
+            }
+        }
+
+        return Boolean.TRUE;
+    }
+
+    private void setTableField(JSONObject invoiceJson, DynamicObject dynamicObject, Boolean isMain) {
+        Set<String> jsonKeys = invoiceJson.keySet();
+        if (!CollectionUtils.isEmpty(jsonKeys)) {
+            Boolean allowEdit = this.isAllowEdit(dynamicObject);
+            String entityid = null;
+            if (isMain) {
+                entityid = "rim_invoice";
+            } else {
+                entityid = InputInvoiceTypeEnum.getEntity(this.invoiceType);
+            }
+
+            JSONObject fieldFormatMap = MetadataUtil.getFeildFormatMap(entityid);
+            DynamicObject dynamicItem = null;
+            JSONObject itemsFormatObject = null;
+            if (fieldFormatMap.containsKey("items") && fieldFormatMap.getJSONObject("items") != null && allowEdit) {
+                itemsFormatObject = fieldFormatMap.getJSONObject("items");
+                DynamicObjectCollection items = dynamicObject.getDynamicObjectCollection("items");
+                if (invoiceJson.getBoolean("needClear") != null && invoiceJson.getBoolean("needClear")) {
+                    items.clear();
+                }
+
+                dynamicItem = items.addNew();
+            }
+
+            Iterator<String> jsonKeyIterator = jsonKeys.iterator();
+            if (!ObjectUtils.isEmpty(jsonKeyIterator)) {
+                while(true) {
+                    String jsonKey;
+                    List invoiceFieldList;
+                    do {
+                        List deductionFieldList;
+                        do {
+                            do {
+                                if (!jsonKeyIterator.hasNext()) {
+                                    return;
+                                }
+
+                                jsonKey = (String)jsonKeyIterator.next();
+                            } while(StringUtils.isEmpty(jsonKey));
+
+                            deductionFieldList = Arrays.asList(deductionFields);
+                        } while(deductionFieldList.contains(jsonKey));
+
+                        invoiceFieldList = Arrays.asList(invoiceFields);
+                    } while(!allowEdit && invoiceFieldList.contains(jsonKey));
+
+                    try {
+                        String format;
+                        if (allowEdit && itemsFormatObject != null && fieldFormatMap.containsKey("items")) {
+                            format = itemsFormatObject.getString(jsonKey);
+                            if (StringUtils.isNotEmpty(format)) {
+                                byte var17 = -1;
+                                switch(format.hashCode()) {
+                                    case -891985903:
+                                        if (format.equals("string")) {
+                                            var17 = 3;
+                                        }
+                                        break;
+                                    case 3076014:
+                                        if (format.equals("date")) {
+                                            var17 = 0;
+                                        }
+                                        break;
+                                    case 94843278:
+                                        if (format.equals("combo")) {
+                                            var17 = 2;
+                                        }
+                                        break;
+                                    case 1542263633:
+                                        if (format.equals("decimal")) {
+                                            var17 = 1;
+                                        }
+                                }
+
+                                switch(var17) {
+                                    case 0:
+                                        if (dynamicItem != null && !ObjectUtils.isEmpty(invoiceJson.getDate(jsonKey))) {
+                                            dynamicItem.set(jsonKey, invoiceJson.getDate(jsonKey));
+                                        }
+                                        break;
+                                    case 1:
+                                        if (dynamicItem != null) {
+                                            dynamicItem.set(jsonKey, invoiceJson.getBigDecimal(jsonKey));
+                                        }
+                                        break;
+                                    case 2:
+                                    case 3:
+                                        if (dynamicItem != null) {
+                                            dynamicItem.set(jsonKey, invoiceJson.getString(jsonKey));
+                                        }
+                                }
+                            }
+                        }
+
+                        if (fieldFormatMap.containsKey(jsonKey)) {
+                            format = fieldFormatMap.getString(jsonKey);
+                            byte var16 = -1;
+                            switch(format.hashCode()) {
+                                case -891985903:
+                                    if (format.equals("string")) {
+                                        var16 = 3;
+                                    }
+                                    break;
+                                case 3076014:
+                                    if (format.equals("date")) {
+                                        var16 = 0;
+                                    }
+                                    break;
+                                case 94843278:
+                                    if (format.equals("combo")) {
+                                        var16 = 2;
+                                    }
+                                    break;
+                                case 1542263633:
+                                    if (format.equals("decimal")) {
+                                        var16 = 1;
+                                    }
+                            }
+
+                            switch(var16) {
+                                case 0:
+                                    if (ObjectUtils.isEmpty(invoiceJson.getString(jsonKey))) {
+                                        break;
+                                    }
+
+                                    Date date;
+                                    if (!"account_date".equals(jsonKey) && !"tax_period".equals(jsonKey)) {
+                                        date = DateUtils.stringToDate(invoiceJson.getString(jsonKey), "yyyyMMdd");
+                                    } else {
+                                        date = DateUtils.stringToDate(invoiceJson.getString(jsonKey), "yyyyMM");
+                                    }
+
+                                    dynamicObject.set(jsonKey, date);
+                                    break;
+                                case 1:
+                                    dynamicObject.set(jsonKey, invoiceJson.getBigDecimal(jsonKey));
+                                    break;
+                                case 2:
+                                case 3:
+                                    if (StringUtils.isNotBlank(invoiceJson.getString(jsonKey))) {
+                                        dynamicObject.set(jsonKey, invoiceJson.getString(jsonKey));
+                                    }
+                            }
+                        }
+                    } catch (Throwable var18) {
+                        logger.error("数据格式转化失败", var18);
+                        throw new MsgException(var18, ResManager.loadKDString("格式转化出错", "ExcelInvoiceSaveService_0", "imc-rim-common", new Object[0]));
+                    }
+                }
+            }
+        }
+    }
+
+    public Long getOrgId(JSONObject invoiceJson) {
+        String orgId = invoiceJson.getString("org");
+        if (StringUtils.isEmpty(orgId)) {
+            return RequestContext.get().getOrgId();
+        } else {
+            return StringUtils.isNumeric(orgId) ? Long.parseLong(orgId) : 0L;
+        }
+    }
+
+    public DynamicObject getExcelMainDynamicObject(DynamicObject invoiceObject, JSONObject invoiceJson) {
+        String userId = RequestContext.get().getUserId();
+        String orgId = invoiceJson.getString("org_id");
+        if (orgId == null) {
+            orgId = RequestContext.get().getOrgId() + "";
+        }
+
+        String tenantNo = invoiceObject.getString("tenant_no");
+        String serialNo = invoiceObject.getString("serial_no");
+        QFilter tenantFilter = new QFilter("tenant_no", "=", tenantNo);
+        QFilter serialFilter = new QFilter("serial_no", "=", serialNo);
+        List<Object> idList = QueryServiceHelper.queryPrimaryKeys("rim_invoice", new QFilter[]{tenantFilter, serialFilter}, (String)null, 100);
+        DynamicObject mainDynamicObject = null;
+        String userDelete = invoiceJson.getString("delete");
+        if (StringUtils.isEmpty(userDelete)) {
+            userDelete = "1";
+        }
+
+        if (CollectionUtils.isEmpty(idList)) {
+            mainDynamicObject = BusinessDataServiceHelper.newDynamicObject("rim_invoice");
+            mainDynamicObject.set("tenant_no", tenantNo);
+            mainDynamicObject.set("org", orgId);
+            mainDynamicObject.set("serial_no", serialNo);
+            mainDynamicObject.set("billstatus", "C");
+            mainDynamicObject.set("invoice_type", this.invoiceType);
+            mainDynamicObject.set("expense_status", "1");
+            mainDynamicObject.set("check_status", "4");
+            if (StringUtils.isNotEmpty(invoiceJson.getString("resource"))) {
+                mainDynamicObject.set("resource", invoiceJson.get("resource"));
+            }
+
+            mainDynamicObject.set("audit_result", "0");
+            mainDynamicObject.set("original_state", "0");
+            mainDynamicObject.set("createtime", new Date());
+            mainDynamicObject.set("delete", userDelete);
+            invoiceJson.put("isNewInvoice", Boolean.TRUE);
+        } else {
+            mainDynamicObject = BusinessDataServiceHelper.loadSingle(idList.get(0), "rim_invoice");
+            invoiceJson.put("isNewInvoice", Boolean.FALSE);
+            mainDynamicObject.set("delete", userDelete);
+        }
+
+        mainDynamicObject.set("is_revise", "0");
+        if (InputInvoiceTypeEnum.TRAIN_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.AIR_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.TRANSPORT_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.BOAT_INVOICE.getCode().equals(this.invoiceType)) {
+            String deduction_flag = DeductionConstant.canBeDeductionForTransport(invoiceJson, Long.valueOf(orgId));
+            mainDynamicObject.set("deduction_flag", deduction_flag);
+            if (!"1".equals(mainDynamicObject.getString("transport_deduction"))) {
+                mainDynamicObject.set("transport_deduction", "0");
+            }
+        }
+
+        this.setUserAndOrg(userId, orgId, mainDynamicObject);
+        mainDynamicObject.set("modifytime", new Date());
+        return mainDynamicObject;
+    }
+
+    public void setMainInvoiceInfo(DynamicObject invoiceObject) {
+        String deductionFlag = invoiceObject.getString("deduction_flag");
+        System.out.println(deductionFlag);
+        String invoiceStatus = invoiceObject.getString("invoice_status");
+        String expenseStatus = invoiceObject.getString("expense_status");
+        String checkStatus = invoiceObject.getString("check_status");
+        String originalState = invoiceObject.getString("original_state");
+        if (!"1".equals(originalState)) {
+            originalState = "0";
+            invoiceObject.set("original_state", "0");
+        }
+
+        String authenticateFlag = invoiceObject.getString("authenticate_flag");
+        StringBuilder infoString = new StringBuilder();
+        if (this.invoiceType == null) {
+            this.invoiceType = invoiceObject.getDynamicObject("invoice_type").getLong("id");
+        }
+
+        String awsType = InputInvoiceTypeEnum.getAwsType(this.invoiceType);
+        if (StringUtils.isNotEmpty(awsType)) {
+            infoString.append("ty_").append(awsType).append(',');
+        }
+
+        if (StringUtils.isNotEmpty(invoiceStatus)) {
+            infoString.append("st_").append(invoiceStatus).append(',');
+        }
+
+        if (StringUtils.isNotEmpty(expenseStatus)) {
+            infoString.append("ex_").append(expenseStatus).append(',');
+        } else {
+            infoString.append("ex_").append('1').append(',');
+        }
+
+        if (InputInvoiceTypeEnum.needCheck(this.invoiceType)) {
+            if (StringUtils.isNotEmpty(checkStatus)) {
+                infoString.append("ch_").append(checkStatus).append(',');
+            }
+        } else {
+            invoiceObject.set("check_status", "1");
+        }
+
+        if (StringUtils.isNotEmpty(originalState)) {
+            infoString.append("or_").append(originalState).append(',');
+        }
+
+        if (StringUtils.isNotEmpty(authenticateFlag)) {
+            infoString.append("au_").append(authenticateFlag).append(',');
+        }
+
+        String is_revise = invoiceObject.getString("is_revise");
+        if ("1".equals(is_revise)) {
+            infoString.append("mo_1").append(',');
+        } else {
+            invoiceObject.set("is_revise", "0");
+        }
+
+        invoiceObject.set("invoice_info", infoString.toString());
+        String deductionFlag2 = invoiceObject.getString("deduction_flag");
+        System.out.println(deductionFlag2);
+    }
+
+    private void setDeductionFlag(JSONObject invoiceJson, DynamicObject mainDynamicObject) {
+        invoiceJson.put("allowDeduction", invoiceJson.get("allow_deduction"));
+        invoiceJson.put("deductionFlag", invoiceJson.get("deduction_flag"));
+        invoiceJson.put("invoiceStatus", invoiceJson.get("invoice_status"));
+        invoiceJson.put("totalTaxAmount", invoiceJson.get("total_tax_amount"));
+        invoiceJson.put("taxAmount", invoiceJson.get("tax_amount"));
+        if (!ObjectUtils.isEmpty(invoiceJson.getDate("invoice_date"))) {
+            invoiceJson.put("invoiceDate", DateUtils.format(invoiceJson.getDate("invoice_date"), "yyyy-MM-dd"));
+        }
+
+        Long orgId = DynamicObjectUtil.getDynamicObjectLongValue(mainDynamicObject.get("org"));
+        String deduction_flag;
+        if (!InputInvoiceTypeEnum.ORDINARY_ELECTRON.getCode().equals(this.invoiceType) && !InputInvoiceTypeEnum.ELECTRIC_ORDINARY.getCode().equals(this.invoiceType)) {
+            if (InputInvoiceTypeEnum.TRAIN_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.AIR_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.TRANSPORT_INVOICE.getCode().equals(this.invoiceType) || InputInvoiceTypeEnum.BOAT_INVOICE.getCode().equals(this.invoiceType)) {
+                deduction_flag = DeductionConstant.canBeDeductionForTransport(invoiceJson, orgId);
+                mainDynamicObject.set("deduction_flag", deduction_flag);
+                if (StringUtils.isEmpty(mainDynamicObject.getString("transport_deduction"))) {
+                    mainDynamicObject.set("transport_deduction", "0");
+                }
+            }
+        } else {
+            deduction_flag = DeductionConstant.canBeDeductionForOrdinary(invoiceJson, orgId);
+            mainDynamicObject.set("deduction_flag", deduction_flag);
+            if (StringUtils.isEmpty(mainDynamicObject.getString("transport_deduction"))) {
+                mainDynamicObject.set("transport_deduction", "0");
+            }
+        }
+
+        if (InputInvoiceTypeEnum.canDeduction(this.invoiceType)) {
+            deduction_flag = DeductionConstant.canBeDeductionForTaxInvoice(invoiceJson);
+            String deductionFlag = mainDynamicObject.getString("deduction_flag");
+            if (!StringUtils.isEmpty(deduction_flag)) {
+                mainDynamicObject.set("deduction_flag", deduction_flag);
+            }
+
+            String authenticateFlag = mainDynamicObject.getString("authenticate_flag");
+            if (StringUtils.isEmpty(authenticateFlag)) {
+                mainDynamicObject.set("authenticate_flag", "0");
+            }
+        }
+
+    }
+
+    public void setDeductionFieldValue(Long invoiceType, DynamicObject dynamicObject, JSONObject invoiceJson) {
+        String authenticateFlag;
+        if (InputInvoiceTypeEnum.canDeduction(invoiceType)) {
+            authenticateFlag = invoiceJson.getString("authenticate_flag");
+            if (StringUtils.isEmpty(authenticateFlag)) {
+                if (StringUtils.isNotEmpty(dynamicObject.getString("authenticate_flag"))) {
+                    return;
+                }
+
+                authenticateFlag = "0";
+            }
+
+            Date authenticateTime = invoiceJson.getDate("authenticate_time");
+            if (authenticateTime == null) {
+                if ("2".equals(authenticateFlag)) {
+                    authenticateTime = invoiceJson.getDate("select_authenticate_time");
+                } else if ("3".equals(authenticateFlag)) {
+                    authenticateTime = invoiceJson.getDate("scan_authenticate_time");
+                }
+            }
+
+            dynamicObject.set("authenticate_flag", authenticateFlag);
+            if (!"0".equals(authenticateFlag)) {
+                dynamicObject.set("authenticate_time", authenticateTime);
+                dynamicObject.set("select_time", invoiceJson.getDate("select_time"));
+                if (!StringUtils.isEmpty(invoiceJson.getString("tax_period"))) {
+                    dynamicObject.set("tax_period", DateUtils.stringToDate(invoiceJson.getString("tax_period"), "yyyyMM"));
+                }
+
+                dynamicObject.set("deduction_purpose", invoiceJson.getString("deduction_purpose"));
+            } else if ("0".equals(authenticateFlag)) {
+                dynamicObject.set("authenticate_time", (Object)null);
+                dynamicObject.set("select_time", (Object)null);
+                dynamicObject.set("tax_period", (Object)null);
+                dynamicObject.set("deduction_purpose", "");
+            }
+
+            if (invoiceJson.getBigDecimal("effective_tax_amount") != null) {
+                dynamicObject.set("effective_tax_amount", invoiceJson.getBigDecimal("effective_tax_amount"));
+            }
+        }
+
+        if (InputInvoiceTypeEnum.canTransportDeduction(invoiceType)) {
+            authenticateFlag = invoiceJson.getString("transport_deduction");
+            if (StringUtils.isEmpty(authenticateFlag)) {
+                if (StringUtils.isNotEmpty(dynamicObject.getString("transport_deduction"))) {
+                    return;
+                }
+
+                authenticateFlag = "0";
+            }
+
+            dynamicObject.set("transport_deduction", authenticateFlag);
+            if (!"0".equals(authenticateFlag)) {
+                dynamicObject.set("authenticate_time", invoiceJson.getDate("authenticate_time"));
+                if (!StringUtils.isEmpty(invoiceJson.getString("tax_period"))) {
+                    dynamicObject.set("tax_period", DateUtils.stringToDate(invoiceJson.getString("tax_period"), "yyyyMM"));
+                }
+            } else {
+                dynamicObject.set("authenticate_time", (Object)null);
+                dynamicObject.set("tax_period", (Object)null);
+            }
+
+            if (invoiceJson.getBigDecimal("effective_tax_amount") != null) {
+                dynamicObject.set("effective_tax_amount", invoiceJson.getBigDecimal("effective_tax_amount"));
+            }
+        }
+
+    }
+
+    public void setExcelMainFieldValue(JSONObject invoiceJson, DynamicObject dynamicObject) {
+        this.setInvoiceExcelFieldValue(invoiceJson, dynamicObject, Boolean.TRUE);
+        String receiver = invoiceJson.getString("receiver");
+        if (StringUtils.isNotEmpty(receiver)) {
+            QFilter nameFilter = new QFilter("name", "=", receiver);
+            DynamicObject receiverObj = QueryServiceHelper.queryOne("bos_user", "id", nameFilter.toArray());
+            if (receiverObj != null) {
+                Long userId = receiverObj.getLong("id");
+                if (!ObjectUtils.isEmpty(userId)) {
+                    dynamicObject.set("receiver", userId);
+                }
+            }
+        }
+
+        if (this.isAllowEdit(dynamicObject)) {
+            if (!StringUtils.isEmpty(invoiceJson.getString("eticket_no"))) {
+                dynamicObject.set("invoice_no", invoiceJson.getString("eticket_no"));
+            }
+
+            JSONArray items = invoiceJson.getJSONArray("items");
+            if (!CollectionUtils.isEmpty(items)) {
+                JSONObject itemJson = items.getJSONObject(0);
+                dynamicObject.set("main_goods_name", itemJson.getString("goods_name"));
+            }
+
+            if (StringUtils.isNotBlank(invoiceJson.getString("tax_paid_proof_no"))) {
+                dynamicObject.set("invoice_no", invoiceJson.getString("tax_paid_proof_no"));
+            }
+
+            if (StringUtils.isNotBlank(invoiceJson.getString("train_num"))) {
+                dynamicObject.set("invoice_code", invoiceJson.getString("train_num"));
+            }
+
+            if (StringUtils.isNotBlank(invoiceJson.getString("sequence_no"))) {
+                dynamicObject.set("invoice_no", invoiceJson.getString("sequence_no"));
+            }
+
+            if (StringUtils.isNotBlank(invoiceJson.getString("number"))) {
+                dynamicObject.set("invoice_no", invoiceJson.getString("number"));
+            }
+
+            if (StringUtils.isNotEmpty(invoiceJson.getString("custom_declaration_no"))) {
+                dynamicObject.set("invoice_no", invoiceJson.getString("custom_declaration_no"));
+            }
+
+            if (StringUtils.isNotEmpty(invoiceJson.getString("invoice_amount"))) {
+                dynamicObject.set("invoice_amount", invoiceJson.getBigDecimal("invoice_amount"));
+            } else {
+                dynamicObject.set("invoice_amount", invoiceJson.getBigDecimal("amount"));
+            }
+
+            if (StringUtils.isNotEmpty(invoiceJson.getString("total_tax_amount"))) {
+                dynamicObject.set("total_tax_amount", invoiceJson.getBigDecimal("total_tax_amount"));
+            }
+
+            if (StringUtils.isEmpty(dynamicObject.getString("is_revise"))) {
+                dynamicObject.set("is_revise", "0");
+            }
+
+            if ("65".equals(invoiceJson.get("expense_status")) && ObjectUtils.isEmpty(invoiceJson.get("account_time"))) {
+                dynamicObject.set("account_time", new Date());
+            }
+
+            if ("0".equals(invoiceJson.getString("company_seal"))) {
+                dynamicObject.set("company_seal", "0");
+            } else {
+                dynamicObject.set("company_seal", "1");
+            }
+
+            if ("1".equals(invoiceJson.getString("continuous_no"))) {
+                dynamicObject.set("continuous_no", "1");
+            } else {
+                dynamicObject.set("continuous_no", "0");
+            }
+
+            String originalState = dynamicObject.getString("original_state");
+            if ("1".equals(originalState) && dynamicObject.get("original_time") == null) {
+                dynamicObject.set("original_time", new Date());
+            }
+
+            String invoiceSource = invoiceJson.getString("invoice_source");
+            String invoiceRiskLevel = invoiceJson.getString("invoice_risk_level");
+            if (StringUtils.isNotEmpty(invoiceSource)) {
+                dynamicObject.set("invoice_source", invoiceSource);
+            }
+
+            if (StringUtils.isNotEmpty(invoiceRiskLevel)) {
+                dynamicObject.set("invoice_risk_level", invoiceRiskLevel);
+            }
+        }
+
+    }
+
+    private void setUserAndOrg(String userId, String orgId, DynamicObject mainDynamicObject) {
+        DynamicObjectCollection userEntry = mainDynamicObject.getDynamicObjectCollection("collect_user_entry");
+        Boolean curUserFlag = Boolean.FALSE;
+        DynamicObject item;
+        if (!CollectionUtils.isEmpty(userEntry)) {
+            Iterator var6 = userEntry.iterator();
+
+            while(var6.hasNext()) {
+                DynamicObject userObj = (DynamicObject)var6.next();
+                item = userObj.getDynamicObject("collect_user");
+                if (null != item) {
+                    String pk = item.getPkValue() + "";
+                    if (pk.equals(userId)) {
+                        userObj.set("collect_user_org", orgId);
+                        curUserFlag = Boolean.TRUE;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!curUserFlag) {
+            item = userEntry.addNew();
+            item.set("collect_user", userId);
+            item.set("collect_user_org", orgId);
+        }
+
+        DynamicObjectCollection orgEntry = mainDynamicObject.getDynamicObjectCollection("collect_org_entry");
+        Boolean curOrgFlag = Boolean.FALSE;
+        if (!CollectionUtils.isEmpty(orgEntry)) {
+            Iterator var15 = orgEntry.iterator();
+
+            while(var15.hasNext()) {
+                DynamicObject orgObj = (DynamicObject)var15.next();
+                DynamicObject org = orgObj.getDynamicObject("collect_org");
+                if (org != null) {
+                    String pk = org.getPkValue() + "";
+                    if (pk.equals(orgId)) {
+                        orgObj.set("collect_org_user", userId);
+                        curOrgFlag = Boolean.TRUE;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!curOrgFlag) {
+            item = orgEntry.addNew();
+            item.set("collect_org_user", userId);
+            item.set("collect_org", orgId);
+        }
+
+    }
+
+    private Boolean checkIdIsNull(Object id) {
+        if (id == null) {
+            return Boolean.TRUE;
+        } else {
+            try {
+                return Integer.parseInt(id.toString()) == 0;
+            } catch (Exception var3) {
+                return Boolean.FALSE;
+            }
+        }
+    }
+
+    private Boolean overrideOrg(DynamicObject dynamicObject) {
+        if (dynamicObject == null) {
+            return Boolean.TRUE;
+        } else {
+            Object expense_status = DynamicObjectUtil.getValue(dynamicObject, "expense_status");
+            Boolean isUsed = ExpenseConstant.isUsed(expense_status);
+            Long orgId = DynamicObjectUtil.getDynamicObjectLongValue(dynamicObject.get("org"));
+            logger.info("orgId:{},isUsed:{}", orgId, isUsed);
+            return orgId > 0L && isUsed ? Boolean.FALSE : Boolean.TRUE;
+        }
+    }
+
+    public Long getInvoiceType() {
+        return this.invoiceType;
+    }
+
+    public void setInvoiceType(Long invoiceType) {
+        this.invoiceType = invoiceType;
+    }
+
+}

+ 722 - 0
main/java/kd/cosmic/jkjt/imc/rim/formplugin/message/service/InvoiceImportOpenServiceEx.java

@@ -0,0 +1,722 @@
+package kd.cosmic.jkjt.imc.rim.formplugin.message.service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
+import kd.bos.bill.IBillWebApiPlugin;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.api.ApiResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiParam;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.imc.rim.common.constant.DeductionConstant;
+import kd.imc.rim.common.constant.ErrorType;
+import kd.imc.rim.common.constant.InputInvoiceTypeEnum;
+import kd.imc.rim.common.expense.domain.ExpenseDTO;
+import kd.imc.rim.common.expense.domain.ExpenseInvoiceDTO;
+import kd.imc.rim.common.expense.service.ExpenseService;
+import kd.imc.rim.common.expense.service.VoucherService;
+import kd.imc.rim.common.message.error.OpenAPIErrorContant;
+import kd.imc.rim.common.message.exception.MsgException;
+import kd.imc.rim.common.utils.DateUtils;
+import kd.imc.rim.common.utils.InvoiceCheckUtils;
+import kd.imc.rim.common.utils.StringUtils;
+
+import javax.validation.Valid;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+@ApiController(value = "/", desc = "")
+public class InvoiceImportOpenServiceEx implements IBillWebApiPlugin {
+
+    private static Log logger = LogFactory.getLog(InvoiceImportOpenServiceEx.class);
+    private static final List<String> FOMATDATEFIELD = Collections.unmodifiableList(Lists.newArrayList(new String[]{"invoiceDate", "selectTime", "authenticateTime", "taxPeriod"}));
+    private static final List<String> FOMATAMOUNTFIELD = Collections.unmodifiableList(Lists.newArrayList(new String[]{"airportConstructionFee", "fuelSurcharge", "insurancePremium"}));
+
+    @ApiPostMapping(value = "fp")
+    public CustomApiResult<Object> importFpInfo(@Valid @ApiParam(value = "请求参数") Map<String, Object> params) {
+        CustomApiResult result = new CustomApiResult();
+        Boolean success = Boolean.TRUE;
+        result.setStatus(success);
+        result.setErrorCode("0000");
+
+        try {
+            return this.doBusiness(params);
+        } catch (MsgException var5) {
+            logger.info("【发票云API】导入全票池失败:", var5);
+            result.setErrorCode(var5.getErrorCode());
+            result.setMessage(var5.getMessage());
+        } catch (Exception var6) {
+            logger.error("【发票云API】导入全票池失败:", var6);
+            result.setMessage(var6.getMessage());
+            result.setErrorCode(ErrorType.FAIL.getCode());
+        }
+
+        return result;
+    }
+
+    public CustomApiResult<Object> doBusiness(Object reqObject) {
+        CustomApiResult result = new CustomApiResult();
+        JSONArray fail = new JSONArray();
+        JSONArray success = new JSONArray();
+        int failCount = 0;
+        //int invoiceTotal = 0;
+        JSONObject param = JSONObject.parseObject(SerializationUtils.toJsonString(reqObject));
+        logger.info("param:{}", param);
+        if (param == null) {
+            logger.info("【发票云API】导入全票池失败,传入参数param为空");
+            //return this.getErrorResult(ErrorType.PARAM_NULL);
+            return CustomApiResult.fail("201", "【发票云API】导入全票池失败,传入参数param为空");
+        } else {
+            JSONArray invoices = param.getJSONArray("invoice");
+            if (invoices == null) {
+                logger.info("【发票云API】导入全票池失败,传入发票为空");
+                //return this.getErrorResult(ErrorType.PARAM_NULL);
+                return CustomApiResult.fail("202", "【发票云API】导入全票池失败,传入发票为空");
+            } else if (invoices.size() > 100) {
+                logger.info("【发票云API】导入全票池失败,传入发票数量超过100请分批导入");
+                //return this.getErrorResult(OpenAPIErrorContant.INVOICECOUNT_OVER);
+                return CustomApiResult.fail("202", "【发票云API】导入全票池失败,传入发票数量超过100请分批导入");
+            } else {
+                List<JSONObject> noNeedCheckInvoiceToDB = Lists.newArrayListWithExpectedSize(invoices.size());
+                int invoiceTotal = invoices.size();
+
+                JSONObject invoice;
+                int invoiceIndex;
+                for(int i = 0; i < invoiceTotal; ++i) {
+                    invoice = invoices.getJSONObject(i);
+                    invoiceIndex = invoice.getIntValue("invoiceIndex");
+
+                    try {
+                        JSONObject convertInvoice = this.verifyInvoice(invoice);
+                        if (StringUtils.isNotBlank(convertInvoice.getString("msg"))) {
+                            logger.info("【发票云API】导入全票池失败,invoice:{},msg:{}", JSONObject.toJSONString(invoice), convertInvoice.getString("msg"));
+                            fail.add(this.getFailInfo(invoiceIndex, convertInvoice.getString("msg"), "1"));
+                            ++failCount;
+                        } else {
+                            String msg = this.checkStandardRule(convertInvoice);
+                            if (StringUtils.isNotBlank(msg)) {
+                                logger.info("【发票云API】导入全票池失败,convertInvoice:{},msg:{}", JSONObject.toJSONString(convertInvoice), msg);
+                                fail.add(this.getFailInfo(invoiceIndex, msg, "1"));
+                                ++failCount;
+                            } else {
+                                Long invoiceType = convertInvoice.getLong("invoice_type");
+                                convertInvoice.put("invoiceType", invoiceType);
+                                String warmTips = this.checkDetailRule(convertInvoice);
+                                if (StringUtils.isNotBlank(warmTips)) {
+                                    fail.add(this.getFailInfo(invoiceIndex, warmTips, "1"));
+                                    ++failCount;
+                                } else {
+                                    noNeedCheckInvoiceToDB.add(convertInvoice);
+                                }
+                            }
+                        }
+                    } catch (Exception var17) {
+                        logger.error("【发票云API】导入全票池失败,格式转换异常:", var17);
+                        fail.add(this.getFailInfo(invoiceIndex, String.format(ResManager.loadKDString("格式转换异常:%1$s", "InvoiceImportOpenService_85", "imc-rim-formplugin", new Object[0]), var17.getMessage()), "1"));
+                        ++failCount;
+                    }
+                }
+
+                Iterator var19 = noNeedCheckInvoiceToDB.iterator();
+
+                while(var19.hasNext()) {
+                    invoice = (JSONObject)var19.next();
+                    invoiceIndex = invoice.getIntValue("invoiceIndex");
+
+                    try {
+                        logger.info("【发票云API】转换之后的发票数据:{}", JSONObject.toJSONString(invoice));
+                        ExcelInvoiceSaveServiceEx ex = new ExcelInvoiceSaveServiceEx(invoice.getLong("invoice_type"));
+                        this.initRequestData(invoice);
+                        JSONObject saveInvoice = ex.save(invoice);
+                        String serialNo = saveInvoice.getString("serialNo");
+                        invoice.put("serial_no", serialNo);
+                        this.saveExpense(invoice);
+                        this.saveVoucher(invoice);
+                        success.add(this.getSuccessInfo(invoiceIndex, serialNo));
+                    } catch (Exception var16) {
+                        logger.error("【发票云API】导入全票池失败,入库失败:", var16);
+                        fail.add(this.getFailInfo(invoiceIndex, String.format(ResManager.loadKDString("入库失败:%1$s", "InvoiceImportOpenService_86", "imc-rim-formplugin", new Object[0]), var16.getMessage()), "2"));
+                        ++failCount;
+                    }
+                }
+
+                //ApiResult apiResult = this.successResult();
+                invoice = new JSONObject();
+                invoice.put("fail", fail);
+                invoice.put("success", success);
+                invoice.put("failCount", failCount);
+                invoice.put("invoiceCount", invoiceTotal);
+                //apiResult.setData(invoice);
+                result.setData(invoice);
+                return result;
+            }
+        }
+    }
+
+    private void initRequestData(JSONObject invoice) {
+        invoice.put("resoureseAPI", "1");
+        if (StringUtils.isEmpty(invoice.getString("check_status"))) {
+            invoice.put("check_status", "4");
+        }
+
+    }
+
+    private JSONObject verifyInvoice(JSONObject invoice) {
+        JSONObject result = new JSONObject();
+        ApiResult checkResult = InvoiceCheckUtils.checkMust(invoice, false);
+        if (checkResult != null) {
+            result.put("msg", checkResult.getMessage());
+            return result;
+        } else {
+            checkResult = InvoiceCheckUtils.checkFieldFormat(invoice);
+            if (checkResult != null) {
+                result.put("msg", checkResult.getMessage());
+                return result;
+            } else {
+                Iterator var4 = invoice.entrySet().iterator();
+
+                String statusMax;
+                while(var4.hasNext()) {
+                    Map.Entry<String, Object> entry = (Map.Entry)var4.next();
+                    statusMax = this.checkField((String)entry.getKey(), entry.getValue(), invoice);
+                    if (statusMax != null) {
+                        result.clear();
+                        result.put("msg", statusMax);
+                        return result;
+                    }
+                }
+
+                this.formatFiled(invoice);
+                JSONArray expenseArr = invoice.getJSONArray("expenseInfo");
+                if (expenseArr != null) {
+                    StringBuilder allBillNo = new StringBuilder();
+                    statusMax = "";
+                    int i = 0;
+
+                    while(true) {
+                        String maxDate;
+                        if (i >= expenseArr.size()) {
+                            if (StringUtils.isNotBlank(statusMax)) {
+                                invoice.put("expense_status", statusMax);
+                            } else {
+                                invoice.put("expense_status", "1");
+                            }
+
+                            if (allBillNo.length() > 0) {
+                                invoice.put("expense_num", allBillNo.toString());
+                            }
+
+                            JSONArray vouchInfos = invoice.getJSONArray("voucherInfo");
+                            if (vouchInfos == null) {
+                                break;
+                            }
+
+                            StringBuilder allVouchNo = new StringBuilder();
+                            maxDate = "";
+
+                            for(int m = 0; m < vouchInfos.size(); ++m) {
+                                JSONObject vouchNum = vouchInfos.getJSONObject(m);
+                                String vouchNo = vouchNum.getString("vouchNo");
+                                String accountDate = vouchNum.getString("accountDate");
+                                if (StringUtils.isNotEmpty(statusMax) && "65".equals(statusMax) && (ObjectUtils.isEmpty(accountDate) || ObjectUtils.isEmpty(vouchNo))) {
+                                    result.put("msg", ResManager.loadKDString("发票业务状态为'已入账'状态时会计属期和凭证号均不允许为空。", "InvoiceImportOpenService_90", "imc-rim-formplugin", new Object[0]));
+                                    return result;
+                                }
+
+                                if (ObjectUtils.isEmpty(accountDate) && !ObjectUtils.isEmpty(vouchNo) || !ObjectUtils.isEmpty(accountDate) && ObjectUtils.isEmpty(vouchNo)) {
+                                    result.put("msg", ResManager.loadKDString("会计属期和凭证号要么都填,要么都不填。", "InvoiceImportOpenService_91", "imc-rim-formplugin", new Object[0]));
+                                    return result;
+                                }
+
+                                if (allVouchNo.length() > 0) {
+                                    allVouchNo.append(',');
+                                }
+
+                                allVouchNo.append(vouchNo);
+                                if (StringUtils.isBlank(maxDate)) {
+                                    maxDate = accountDate;
+                                }
+                            }
+
+                            if (StringUtils.isNotBlank(maxDate)) {
+                                invoice.put("account_date", maxDate);
+                            }
+
+                            if (StringUtils.isNotBlank(allVouchNo.toString())) {
+                                invoice.put("vouch_no", allVouchNo.toString());
+                            }
+                            break;
+                        }
+
+                        JSONObject expense = expenseArr.getJSONObject(i);
+                        maxDate = expense.getString("status");
+                        String billNo = expense.getString("billNo");
+                        if (StringUtils.isNotBlank(maxDate) && ("30".equals(maxDate) || "60".equals(maxDate)) && StringUtils.isBlank(billNo)) {
+                            result.put("msg", ResManager.loadKDString("发票业务状态为'审核中','已报销'状态时业务单据号码不允许为空。", "InvoiceImportOpenService_88", "imc-rim-formplugin", new Object[0]));
+                            return result;
+                        }
+
+                        if (StringUtils.isNotBlank(billNo) && StringUtils.isBlank(maxDate)) {
+                            result.put("msg", ResManager.loadKDString("业务单据号码不为空时,报销状态不允许为空。", "InvoiceImportOpenService_89", "imc-rim-formplugin", new Object[0]));
+                            return result;
+                        }
+
+                        if (StringUtils.isNotBlank(maxDate) && (StringUtils.isBlank(statusMax) || Integer.parseInt(maxDate) > Integer.parseInt(statusMax))) {
+                            statusMax = maxDate;
+                        }
+
+                        if (StringUtils.isNotBlank(billNo)) {
+                            if (allBillNo.length() > 0) {
+                                allBillNo.append(',');
+                            }
+
+                            allBillNo.append(billNo);
+                        }
+
+                        ++i;
+                    }
+                }
+
+                return InvoiceCheckUtils.conver(invoice);
+            }
+        }
+    }
+
+    public CustomApiResult successResult() {
+        CustomApiResult result = new CustomApiResult();
+        result.setStatus(true);
+        result.setErrorCode(ErrorType.SUCCESS.getCode());
+        result.setMessage(ErrorType.SUCCESS.getName());
+        return result;
+    }
+
+    private CustomApiResult getErrorResult(ErrorType error) {
+        CustomApiResult result = CustomApiResult.fail(error.getName(), error.getCode());
+        return result;
+    }
+
+    private JSONObject getFailInfo(int index, String msg, String level) {
+        JSONObject result = new JSONObject();
+        result.put("invoiceIndex", index);
+        result.put("msg", msg);
+        result.put("level", level);
+        return result;
+    }
+
+    private JSONObject getSuccessInfo(int index, String serialNo) {
+        JSONObject result = new JSONObject();
+        result.put("invoiceIndex", index);
+        result.put("serialNo", serialNo);
+        return result;
+    }
+
+    private CustomApiResult getErrorResult(OpenAPIErrorContant error) {
+        CustomApiResult result = CustomApiResult.fail(error.getName(), error.getCode());
+        return result;
+    }
+
+    private String checkField(String fieldName, Object value, JSONObject invoiceJson) {
+        try {
+            String tag;
+            if (StringUtils.equals("checkCode", fieldName)) {
+                tag = (String)value;
+                if (value != null && tag.length() > 6) {
+                    this.getOrgId(tag.substring(tag.length() - 6));
+                }
+            } else if (StringUtils.equals("expenseStatus", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.EXPENSE_STATUS_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("报销状态未按指定格式填入", "InvoiceImportOpenService_92", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("authenticateFlag", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ZERO_FOUR_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("勾选状态未按指定格式填入", "InvoiceImportOpenService_93", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("invoiceStatus", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ZERO_FOUR_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("发票状态未按指定格式填入", "InvoiceImportOpenService_94", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("deductionPurpose", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ONE_TWO_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("抵扣用途未按指定格式填入", "InvoiceImportOpenService_95", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("notDeductibleType", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ONE_FIVE_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("不抵扣原因未按指定格式填入", "InvoiceImportOpenService_96", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("deductionFlag", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ZERO_ONE_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("是否可抵扣未按指定格式填入", "InvoiceImportOpenService_97", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("transportDeduction", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ZERO_TWO_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("旅客运输抵扣状态未按指定格式填入", "InvoiceImportOpenService_98", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("originalState", fieldName)) {
+                tag = (String)value;
+                if (!InvoiceCheckUtils.ZERO_ONE_CHECK.contains(tag.trim())) {
+                    return ResManager.loadKDString("是否签收未按指定格式填入", "InvoiceImportOpenService_99", "imc-rim-formplugin", new Object[0]);
+                }
+            } else if (StringUtils.equals("tag", fieldName)) {
+                tag = (String)value;
+                if (tag != null && tag.length() > 50) {
+                    return ResManager.loadKDString("发票标注字段长度超过50", "InvoiceImportOpenService_100", "imc-rim-formplugin", new Object[0]);
+                }
+            }
+
+            return null;
+        } catch (Exception var5) {
+            logger.error("参数名为:" + fieldName + "的字段出错{}", var5);
+            return String.format(ResManager.loadKDString("解析字段%1$s出错,请检查该字段格式是否正确", "InvoiceImportOpenService_87", "imc-rim-formplugin", new Object[0]), fieldName);
+        }
+    }
+
+    private void formatFiled(JSONObject invoice) {
+        Iterator var2 = FOMATDATEFIELD.iterator();
+
+        String field;
+        while(var2.hasNext()) {
+            field = (String)var2.next();
+            if (invoice.containsKey(field)) {
+                String date = invoice.getString(field);
+                if (StringUtils.isNotBlank(date)) {
+                    date = date.replaceAll("-", "");
+                    if (!Objects.isNull(date)) {
+                        invoice.put(field, date);
+                    }
+                }
+            }
+        }
+
+        Object orgId;
+        if (invoice.getString("orgNumber") != null) {
+            orgId = this.getOrgId(invoice.getString("orgNumber"));
+            if (!Objects.isNull(orgId)) {
+                invoice.put("org", orgId);
+            }
+        }
+
+        if (invoice.getString("taxOrgNumber") != null) {
+            orgId = this.getOrgId(invoice.getString("taxOrgNumber"));
+            if (!Objects.isNull(orgId)) {
+                invoice.put("tax_org", orgId);
+            }
+        }
+
+        var2 = FOMATAMOUNTFIELD.iterator();
+
+        while(var2.hasNext()) {
+            field = (String)var2.next();
+            if (invoice.containsKey(field)) {
+                Object amount = invoice.get(field);
+                if (amount == null) {
+                    Object amount2 = BigDecimal.ZERO;
+                    invoice.put(field, amount2);
+                }
+            }
+        }
+
+    }
+
+    private String checkStandardRule(JSONObject invoice) {
+        String invoiceStatus = invoice.getString("invoice_status");
+        String deductionFlag = invoice.getString("deduction_flag");
+        String deductionPurpose = invoice.getString("deduction_purpose");
+        String authenticateFlag = invoice.getString("authenticate_flag");
+        Object selectTime = invoice.get("select_time");
+        Object authenticateTime = invoice.get("authenticate_time");
+        Object taxPeriod = invoice.get("tax_period");
+        String notDeductibleType = invoice.getString("not_deductible_type");
+        if (!"0".equals(invoiceStatus) && InputInvoiceTypeEnum.canDeduction(invoice.getLong("invoiceType")) && (!StringUtils.isEmpty(deductionFlag) || !StringUtils.isEmpty(deductionPurpose) || !StringUtils.isEmpty(authenticateFlag) || !ObjectUtils.isEmpty(selectTime) || !ObjectUtils.isEmpty(authenticateTime) || !ObjectUtils.isEmpty(taxPeriod))) {
+            return ResManager.loadKDString("发票状态为非'正常'状态时,【是否可抵扣】,【抵扣用途】,【不抵扣原因】,【勾选状态】,【勾选时间】,【认证时间】,【抵扣税期】字段不允许填入任何值", "InvoiceImportOpenService_101", "imc-rim-formplugin", new Object[0]);
+        } else if (StringUtils.isNotEmpty(deductionPurpose) && "0".equals(deductionFlag)) {
+            return ResManager.loadKDString("抵扣状态为'不可抵扣'状态'时,没有抵扣用途", "InvoiceImportOpenService_102", "imc-rim-formplugin", new Object[0]);
+        } else if (!StringUtils.isNotEmpty(deductionPurpose) || !StringUtils.isEmpty(authenticateFlag) && !"0".equals(authenticateFlag) && !ResManager.loadKDString("未勾选", "InvoiceImportOpenService_103", "imc-rim-formplugin", new Object[0]).equals(authenticateFlag)) {
+            if (("1".equals(deductionPurpose) || ResManager.loadKDString("抵扣", "InvoiceImportOpenService_105", "imc-rim-formplugin", new Object[0]).equals(deductionPurpose)) && !StringUtils.isEmpty(notDeductibleType)) {
+                return ResManager.loadKDString("导入失败:抵扣用途为'抵扣'时,不抵扣原因必须为空", "InvoiceImportOpenService_106", "imc-rim-formplugin", new Object[0]);
+            } else {
+                JSONArray items = invoice.getJSONArray("items");
+                BigDecimal taxRate;
+                if (items != null) {
+                    for(int i = 0; i < items.size(); ++i) {
+                        JSONObject item = items.getJSONObject(i);
+                        taxRate = item.getBigDecimal("tax_rate");
+                        if (taxRate != null && (BigDecimal.ONE.compareTo(taxRate) < 0 || BigDecimal.ZERO.compareTo(taxRate) > 0)) {
+                            return ResManager.loadKDString("税率格式有误,税率的值只能在0%到100%之间", "InvoiceImportOpenService_107", "imc-rim-formplugin", new Object[0]);
+                        }
+                    }
+                }
+
+                BigDecimal effectiveTaxAmount = invoice.getBigDecimal("effective_tax_amount");
+                BigDecimal totalTaxAmount = invoice.getBigDecimal("total_tax_amount");
+                if (!ObjectUtils.isEmpty(effectiveTaxAmount) && !ObjectUtils.isEmpty(totalTaxAmount) && totalTaxAmount.compareTo(effectiveTaxAmount) < 0) {
+                    return ResManager.loadKDString("有效税额只能小于等于税额", "InvoiceImportOpenService_108", "imc-rim-formplugin", new Object[0]);
+                } else {
+                    taxRate = invoice.getBigDecimal("invoice_amount");
+                    BigDecimal totalAmount = invoice.getBigDecimal("total_amount");
+                    String invoiceTypeStr = invoice.getString("invoice_type");
+                    Long invoiceType = InputInvoiceTypeEnum.getInvoiceTypeByAwsType(invoiceTypeStr);
+                    return !InputInvoiceTypeEnum.AIR_INVOICE.getCode().equals(invoiceType) && !ObjectUtils.isEmpty(taxRate) && !ObjectUtils.isEmpty(totalTaxAmount) && !ObjectUtils.isEmpty(totalAmount) && totalAmount.compareTo(taxRate.add(totalTaxAmount)) != 0 ? ResManager.loadKDString("价税合计 = 税额 + 不含税金额", "InvoiceImportOpenService_109", "imc-rim-formplugin", new Object[0]) : this.checkDeduction(invoice);
+                }
+            }
+        } else {
+            return ResManager.loadKDString("抵扣用途不为空时,勾选状态不能为空且不能为‘未勾选’状态", "InvoiceImportOpenService_104", "imc-rim-formplugin", new Object[0]);
+        }
+    }
+
+    private String checkDeduction(JSONObject invoice) {
+        if (!"4".equals(invoice.getString("authenticate_flag")) || !ObjectUtils.isEmpty(invoice.getString("tax_period")) && !ObjectUtils.isEmpty(invoice.getString("deduction_purpose"))) {
+            if (!"1".equals(invoice.getString("authenticate_flag")) || !ObjectUtils.isEmpty(invoice.getDate("select_time")) && !ObjectUtils.isEmpty(invoice.getString("tax_period")) && !ObjectUtils.isEmpty(invoice.getString("deduction_purpose"))) {
+                if (!"2".equals(invoice.getString("authenticate_flag")) && !"3".equals(invoice.getString("authenticate_flag")) || !ObjectUtils.isEmpty(invoice.getDate("select_time")) && !ObjectUtils.isEmpty(invoice.getString("tax_period")) && !ObjectUtils.isEmpty(invoice.getString("deduction_purpose")) && !ObjectUtils.isEmpty(invoice.getDate("authenticate_time"))) {
+                    return ("1".equals(invoice.getString("transport_deduction")) || "2".equals(invoice.getString("transport_deduction"))) && ObjectUtils.isEmpty(invoice.getString("tax_period")) ? ResManager.loadKDString("导入失败:旅客运输抵扣为预抵扣或已抵扣时,抵扣税期不能为空", "InvoiceImportOpenService_113", "imc-rim-formplugin", new Object[0]) : null;
+                } else {
+                    return ResManager.loadKDString("导入失败:认证状态,抵扣用途,勾选时间,税期和认证时间均不允许为空", "InvoiceImportOpenService_112", "imc-rim-formplugin", new Object[0]);
+                }
+            } else {
+                return ResManager.loadKDString("导入失败:已勾选状态,抵扣用途,勾选时间和税期均不允许为空", "InvoiceImportOpenService_111", "imc-rim-formplugin", new Object[0]);
+            }
+        } else {
+            return ResManager.loadKDString("导入失败:预勾选状态,抵扣用途和税期均不允许为空", "InvoiceImportOpenService_110", "imc-rim-formplugin", new Object[0]);
+        }
+    }
+
+    private Object getOrgId(String number) {
+        QFilter filter = new QFilter("number", "=", number);
+        DynamicObject obj = QueryServiceHelper.queryOne("bos_org", "id", new QFilter[]{filter});
+        return obj != null ? obj.get("id") : null;
+    }
+
+    private String checkDetailRule(JSONObject invoice) {
+        if (ObjectUtils.isEmpty(invoice.get("total_amount"))) {
+            return ResManager.loadKDString("价税合计为空", "InvoiceImportOpenService_114", "imc-rim-formplugin", new Object[0]);
+        } else {
+            try {
+                JSONArray items = invoice.getJSONArray("items");
+
+                for(int i = 0; items != null && i < items.size(); ++i) {
+                    JSONObject item = items.getJSONObject(i);
+                    BigDecimal num = BigDecimal.ZERO;
+
+                    try {
+                        num = item.getBigDecimal("num");
+                    } catch (Exception var17) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【数量】格式不正确,value:", "InvoiceImportOpenService_115", "imc-rim-formplugin", new Object[0]), item.get("num")});
+                    }
+
+                    BigDecimal unitPrice = BigDecimal.ZERO;
+
+                    try {
+                        unitPrice = item.getBigDecimal("unit_price");
+                    } catch (Exception var16) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【单价】字段格式不正确,value:", "InvoiceImportOpenService_116", "imc-rim-formplugin", new Object[0]), item.get("unit_price")});
+                    }
+
+                    BigDecimal detailAmount = BigDecimal.ZERO;
+
+                    try {
+                        detailAmount = item.getBigDecimal("detail_amount");
+                    } catch (Exception var15) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【金额】字段格式不正确,value:", "InvoiceImportOpenService_117", "imc-rim-formplugin", new Object[0]), item.get("detail_amount")});
+                    }
+
+                    BigDecimal taxAmount = BigDecimal.ZERO;
+
+                    try {
+                        taxAmount = item.getBigDecimal("tax_amount");
+                    } catch (Exception var14) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【税额】字段格式不正确,value:", "InvoiceImportOpenService_118", "imc-rim-formplugin", new Object[0]), item.get("tax_amount")});
+                    }
+
+                    BigDecimal taxRate = BigDecimal.ZERO;
+
+                    try {
+                        taxRate = item.getBigDecimal("tax_rate");
+                    } catch (Exception var13) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【税率】字段格式不正确,value:", "InvoiceImportOpenService_119", "imc-rim-formplugin", new Object[0]), item.get("tax_rate")});
+                    }
+
+                    String goodsCode = "";
+
+                    try {
+                        goodsCode = item.getString("goods_code");
+                    } catch (Exception var12) {
+                        return StringUtils.append(new Object[]{ResManager.loadKDString("【税收分类编码】字段格式不正确,value:", "InvoiceImportOpenService_120", "imc-rim-formplugin", new Object[0]), item.get("goods_code")});
+                    }
+
+                    if (num == null && unitPrice != null || num != null && unitPrice == null) {
+                        return ResManager.loadKDString("数量和单价需同时必填或者同时不填", "InvoiceImportOpenService_121", "imc-rim-formplugin", new Object[0]);
+                    }
+
+                    BigDecimal offsetPrice;
+                    if (num != null && unitPrice != null) {
+                        offsetPrice = detailAmount.subtract(unitPrice.multiply(num).setScale(2, RoundingMode.HALF_UP));
+                        if (BigDecimal.valueOf(0.06D).compareTo(offsetPrice.abs()) < 0) {
+                            return ResManager.loadKDString("明细行数量*单价和金额的误差超过0.06", "InvoiceImportOpenService_122", "imc-rim-formplugin", new Object[0]);
+                        }
+                    }
+
+                    if (num != null && unitPrice != null && taxAmount != null && taxRate != null) {
+                        offsetPrice = taxAmount.subtract(unitPrice.multiply(num).multiply(taxRate).setScale(2, 4));
+                        if (offsetPrice.abs().compareTo(BigDecimal.valueOf(0.06D)) > 0) {
+                            return ResManager.loadKDString("计算的税额与实际填入的税额的偏差值超过了0.06", "InvoiceImportOpenService_123", "imc-rim-formplugin", new Object[0]);
+                        }
+                    }
+
+                    if (StringUtils.isNotBlank(goodsCode) && goodsCode.length() != 19) {
+                        return ResManager.loadKDString("税收分类编码应该为19位长度", "InvoiceImportOpenService_124", "imc-rim-formplugin", new Object[0]);
+                    }
+                }
+
+                String deductionFlagResult = this.checkDeductionFlag(invoice);
+                if (StringUtils.isNotEmpty(deductionFlagResult)) {
+                    return deductionFlagResult;
+                } else {
+                    return null;
+                }
+            } catch (Exception var18) {
+                logger.error("明细转化有误:", var18);
+                return ResManager.loadKDString("数量,单价,金额,税率,税收分类编码或税额的格式不正确", "InvoiceImportOpenService_125", "imc-rim-formplugin", new Object[0]);
+            }
+        }
+    }
+
+    private String checkDeductionFlag(JSONObject invoice) {
+        Long invoiceType = invoice.getLong("invoiceType");
+        if (InputInvoiceTypeEnum.canDeduction(invoiceType)) {
+            String userDeductionFlag = invoice.getString("deduction_flag");
+            JSONObject invoiceParam = new JSONObject();
+            invoiceParam.put("invoiceStatus", invoice.getString("invoice_status"));
+            if (!ObjectUtils.isEmpty(invoice.getDate("invoice_date"))) {
+                invoiceParam.put("invoiceDate", DateUtils.format(invoice.getDate("invoice_date"), "yyyy-MM-dd"));
+            }
+
+            invoiceParam.put("totalTaxAmount", invoice.get("total_tax_amount"));
+            invoiceParam.put("taxAmount", invoice.get("tax_amount"));
+            invoiceParam.put("invoiceType", invoiceType);
+            String sysDeductionFlag = DeductionConstant.canBeDeductionForTaxInvoice(invoiceParam);
+            if ("1".equals(userDeductionFlag) && "0".equals(sysDeductionFlag)) {
+                invoice.put("deduction_purpose", "");
+                invoice.put("not_deductible_type", "");
+                invoice.put("authenticate_flag", "");
+                invoice.put("select_time", (Object)null);
+                invoice.put("authenticate_time", (Object)null);
+                invoice.put("tax_period", "");
+                return ResManager.loadKDString("系统判断该发票不可抵扣,与填入的值不符,保存该发票时已自动清除标绿字段的内容", "InvoiceImportOpenService_126", "imc-rim-formplugin", new Object[0]);
+            }
+        }
+
+        return null;
+    }
+
+    private void saveExpense(JSONObject invoice) {
+        JSONArray expenseArr = invoice.getJSONArray("expenseInfo");
+        List<ExpenseDTO> dtoList = Lists.newArrayListWithExpectedSize(0);
+        if (expenseArr != null) {
+            dtoList = Lists.newArrayListWithExpectedSize(expenseArr.size());
+
+            for(int i = 0; i < expenseArr.size(); ++i) {
+                JSONObject expenseInfo = expenseArr.getJSONObject(i);
+                ExpenseDTO dto = new ExpenseDTO();
+                dto.setExpenseNum(expenseInfo.getString("billNo"));
+                dto.setStatus(expenseInfo.getString("status"));
+                if (StringUtils.isNotBlank(expenseInfo.getString("billId"))) {
+                    dto.setExpenseId(expenseInfo.getString("billId"));
+                } else {
+                    dto.setExpenseId(expenseInfo.getString("billNo"));
+                }
+
+                dto.setResource("api");
+                if (StringUtils.isNotBlank(expenseInfo.getString("billType"))) {
+                    dto.setExpenseType(expenseInfo.getString("billType"));
+                }
+
+                if (StringUtils.isNotBlank(expenseInfo.getString("orgNumber"))) {
+                    dto.setOrgId((Long)this.getOrgId(expenseInfo.getString("orgNumber")));
+                }
+
+                List<ExpenseInvoiceDTO> list = new ArrayList(1);
+                ExpenseInvoiceDTO invoiceDto = new ExpenseInvoiceDTO();
+                invoiceDto.setSerialNo(invoice.getString("serial_no"));
+                list.add(invoiceDto);
+                dto.setInvoiceList(list);
+                dtoList.add(dto);
+            }
+        }
+
+        ExpenseService service = new ExpenseService();
+        service.saveInvoiceAndExpenseOfAPI(dtoList, invoice.getString("serial_no"));
+    }
+
+    private void saveVoucher(JSONObject invoice) {
+        String serialNo = invoice.getString("serial_no");
+        JSONArray voucherInfo = invoice.getJSONArray("voucherInfo");
+        List<Map<String, Object>> paramList = Lists.newArrayListWithExpectedSize(0);
+        if (voucherInfo != null) {
+            paramList = Lists.newArrayListWithExpectedSize(voucherInfo.size());
+            List<String> list = Lists.newArrayList(new String[]{serialNo});
+
+            for(int i = 0; i < voucherInfo.size(); ++i) {
+                JSONObject vouch = voucherInfo.getJSONObject(i);
+                String vouchNo = vouch.getString("vouchNo");
+                String accountDate = vouch.getString("accountDate");
+                accountDate = accountDate.replaceAll("-", "");
+                if (accountDate.length() == 6) {
+                    accountDate = accountDate + "01";
+                }
+
+                String vouchId;
+                if (StringUtils.isNotBlank(vouch.getString("vouchId"))) {
+                    vouchId = vouch.getString("vouchId");
+                } else {
+                    vouchId = vouch.getString("vouchNo");
+                }
+
+                String businessDate;
+                if (StringUtils.isNotBlank(vouch.getString("businessDate"))) {
+                    businessDate = vouch.getString("businessDate");
+                } else {
+                    businessDate = vouch.getString("accountDate");
+                }
+
+                businessDate = businessDate.replaceAll("-", "");
+                Map<String, Object> vouchparam = new HashMap(8);
+                vouchparam.put("vouchNo", vouchNo);
+                vouchparam.put("vouchId", vouchId);
+                vouchparam.put("accountDate", accountDate.replaceAll("-", ""));
+                vouchparam.put("businessDate", businessDate.replaceAll("-", ""));
+                vouchparam.put("resource", "api");
+                Map<String, Object> param = new HashMap(8);
+                param.put("voucherInfo", vouchparam);
+                param.put("serialNoArray", list);
+                param.put("resoureseAPI", "1");
+                paramList.add(param);
+            }
+        }
+
+        VoucherService service = new VoucherService();
+        service.deleteVoucherOfAPI(serialNo);
+        Iterator var15 = paramList.iterator();
+
+        while(var15.hasNext()) {
+            Map<String, Object> param = (Map)var15.next();
+            service.saveVoucher(param);
+        }
+
+    }
+
+}

+ 364 - 0
main/java/kd/cosmic/jkjt/msg/ecology/ChangeMessageStatePlugin.java

@@ -0,0 +1,364 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bec.api.IEventServicePlugin;
+import kd.bos.bec.model.EntityEvent;
+import kd.bos.bec.model.JsonEvent;
+import kd.bos.bec.model.KDBizEvent;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.engine.WfUtils;
+import kd.bos.workflow.exception.WFErrorCode;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/22
+ * 插件说明:业务事件中心插件
+ * 表单标识:消息阅读状态变更事件.执行插件(wf.AfterChangeMessageStateEvent.executePlugin)
+ */
+public class ChangeMessageStatePlugin implements IEventServicePlugin {
+    private static Log logger = LogFactory.getLog(ChangeMessageStatePlugin.class);
+    private static String KEY_ENTITY_LOG = "nckd_ecology";
+    private static String FANWEI_URL = "";
+    private static String FANWEI_URL_MSG = "";
+    private static String FANWEI_CODE = "";
+    private static String FANWEI_MSGCODE = "";
+    private static String FANWEI_APPID = "";
+    private static String FANWEI_SECRET = "";
+    private static String FANWEI_SPK = "";
+
+    private static void setParmamValue(){
+        String selectField = "nckd_ecologyurl, nckd_ecologycode, nckd_ecologmsgcode, nckd_ecologappid, nckd_ecologsecret, nckd_ecologspk, nckd_ecologyurlmsg";
+        DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectField, null);
+        if(commonParams.length > 0){
+            FANWEI_URL = commonParams[0].getString("nckd_ecologyurl");
+            FANWEI_URL_MSG = commonParams[0].getString("nckd_ecologyurlmsg");
+            FANWEI_CODE = commonParams[0].getString("nckd_ecologycode");
+            FANWEI_MSGCODE = commonParams[0].getString("nckd_ecologmsgcode");
+            FANWEI_APPID = commonParams[0].getString("nckd_ecologappid");
+            FANWEI_SECRET = commonParams[0].getString("nckd_ecologsecret");
+            FANWEI_SPK = commonParams[0].getString("nckd_ecologspk");
+        }
+    }
+
+    @Override
+    public Object handleEvent(KDBizEvent evt) {
+        logger.info("泛微消息阅读状态变更事件" + evt.toString());
+
+        if (evt instanceof EntityEvent) {//苍穹事件
+            EntityEvent entityEvent = (EntityEvent) evt;//类型转换
+            String businesskey = entityEvent.getBusinesskeys().get(0);
+            String entityNumber = entityEvent.getEntityNumber();
+            DynamicObject obj = BusinessDataServiceHelper.loadSingle(businesskey, entityNumber);
+            Long evtID = evt.getEventId();
+            String source = evt.getSource();//传递的事件参数
+        } else {//自定义事件
+            JsonEvent jsonEvent = (JsonEvent) evt;//类型转换
+            String source = jsonEvent.getSource();//传递的事件参数
+            if (WfUtils.isNotEmpty(source)) {
+                setParmamValue();
+
+                JSONArray arr = (JSONArray) JSON.parse(source);
+                for (int i = 0; i < arr.size(); i++) {
+                    if(arr.getJSONObject(i) == null) {
+                        continue;
+                    }
+                    //消息id
+                    String msgIds = arr.getJSONObject(i).getString("msgIds");
+
+                    logger.info("泛微消息阅读状态变更事件:msgIds" + msgIds);
+
+                    //用户id
+                    String userId = arr.getJSONObject(i).getString("userId");
+                    //状态
+                    String state = arr.getJSONObject(i).getString("state");
+
+                    if(StringUtils.isEmpty(msgIds)){
+                        continue;
+                    }
+
+                    updateEcologyMsgState(userId, msgIds);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void updateEcologyMsgState(String userId, String msgIds){
+        QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(userId));
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingle("bos_user", qFilter.toArray());
+        String personNo = user.getString("number");
+
+        msgIds = msgIds.replace("[", "").replace("]", "");
+        String[] msgIdList = msgIds.split(",");
+        if(msgIdList.length == 0){
+            return;
+        }
+
+        for(String msgId : msgIdList){
+            logger.info("泛微消息阅读状态变更事件:替换前 msgId:" + msgId);
+            msgId = msgId.replace("\"", "");
+            logger.info("泛微消息阅读状态变更事件:替换后msgId:" + msgId);
+
+            qFilter = new QFilter("channel", QCP.equals, "fanwei");
+            qFilter.and(new QFilter("messageid", QCP.equals, Long.valueOf(msgId)));
+            DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+            if(msgFail == null){
+                continue;
+            }
+            Long channelMsgId = msgFail.getLong("id");
+            String tplscene = msgFail.getString("tplscene");
+            String oaId = "";
+
+            if("circulation".equals(tplscene)){
+                oaId = "KD-" + channelMsgId + "-" + personNo;
+            }
+
+            sendMsg2Ecology(Long.valueOf(userId), Long.valueOf(msgId), oaId);
+        }
+    }
+
+    private void sendMsg2Ecology(Long userId, Long msgId, String oaId){
+        JSONObject fanweiBodyData = null;
+        QFilter qFilter = new QFilter("nckd_oaid", QCP.equals, oaId);
+        DynamicObject ecologyLog = BusinessDataServiceHelper.loadSingle("nckd_ecology", qFilter.toArray());
+
+        if(ecologyLog == null){
+            //根据消息Id组合OA消息对象
+            fanweiBodyData = buildMessageBodyData(userId, msgId, oaId);
+        } else {
+            String sendData = ecologyLog.getString("nckd_senddata_tag");
+            if(StringUtils.isEmpty(sendData)){
+                return;
+            } else {
+                fanweiBodyData = JSONObject.parseObject(sendData);
+            }
+        }
+
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String createTime = sdf.format(new Date());
+        String curTimestamp = String.valueOf(System.currentTimeMillis());
+
+        //流程处理状态:0:待办,2:已办,4:办结,8:抄送(待阅)
+        fanweiBodyData.put("isremark", "2");
+        //流程查看状态:0:未读,1:已读
+        fanweiBodyData.put("viewtype", "1");
+        //接收时间
+        fanweiBodyData.put("receivedatetime", createTime);
+        //接收时间戳
+        fanweiBodyData.put("receivets", curTimestamp);
+
+
+        logger.info("泛微消息阅读状态变更事件:" + fanweiBodyData.toString());
+
+        String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+        JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+        if (result == null) {
+            throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+        }
+
+        //失败
+        if("0".equals(result.getString("operResult"))){
+            logger.info("泛微消息阅读状态更新失败 :" + result.getString("message"));
+        } else {
+            logger.info("泛微消息阅读状态更新成功, 消息id: " + oaId);
+        }
+
+        logger.info("消息阅读状态变更事件,发送消息:{}, 结果:{}", fanweiBodyData.toJSONString(), result.toJSONString());
+    }
+
+    /**
+     * 根据 messageid 构造OA消息对象
+     * @param userId
+     * @param msgId
+     * @param oaId
+     * @return
+     */
+    public static JSONObject buildMessageBodyData(Long userId, Long msgId, String oaId) {
+        JSONObject bodyData = new JSONObject();
+
+        String isremark = "2";
+        String viewtype = "1";
+
+        QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgId));
+        DynamicObject info = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+        if(info == null){
+            info = BusinessDataServiceHelper.loadSingle("wf_msg_himessage", qFilter.toArray());
+        }
+
+        if(info == null){
+            return null;
+        }
+
+        String content = StringUtils.isEmpty(info.getString("content")) ?
+                info.getString("content_summary") : info.getString("content");
+        if(content == null){
+            content = "";
+        }
+
+        //发送人员
+        Long createUserId = 0L;
+        if(info != null && !StringUtils.isEmpty(info.getString("sender"))){
+            createUserId = Long.valueOf(info.getString("sender"));
+        }
+
+        DynamicObject createUser = BusinessDataServiceHelper.loadSingleFromCache(createUserId, "bos_user");
+        String creator = createUser == null ? "" : createUser.getString("number");
+
+        String requestname = info.getString("title") + ", 发送人:" + creator + ", " + content;
+        String nodename = info.getString("operation") == null ? "circulation" : info.getString("operation");
+        String workflowname = "财务系统审批流";
+        String workflowcode = "nobill";
+
+        if(info.getString("config") != null){
+            JSONObject config = JSONObject.parseObject(info.getString("config"));
+            if(config != null) {
+                if(config.get("messageContext") != null){
+                    JSONObject messageContext = JSONObject.parseObject(config.get("messageContext").toString());
+                    if(messageContext.get("taskId") != null){
+                        DynamicObject taskInfo = BusinessDataServiceHelper.loadSingleFromCache(Long.valueOf(messageContext.getString("taskId")), "wf_task");
+                        if(taskInfo != null){
+                            nodename = taskInfo.getString("name");
+
+                            if(taskInfo.getLong("processdefinitionid") > 0){
+                                qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                                if(processdef != null){
+                                    workflowcode = processdef.getString("key");
+                                    workflowname = processdef.getString("name");
+                                }
+                            }
+
+                            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                                workflowcode = taskInfo.getString("entityname");
+                                workflowname = taskInfo.getString("entityname") + "审批流程";
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
+        String personNo = user.getString("number");
+        String receiverLong = user.getString("name") + "(" + user.getString("number") + ")";
+
+        //创建时间
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String createTime = sdf.format(new Date());
+        String curTimestamp = String.valueOf(System.currentTimeMillis());
+        String flowid = oaId;
+
+        String pcUrl = info.getString("contenturl") == null ? "" : info.getString("contenturl");
+        String mobUrl = info.getString("mobcontenturl") == null ? pcUrl : info.getString("mobcontenturl");
+
+        String contextUrl = System.getProperty("domain.contextUrl").replace("/ierp", "");
+        pcUrl = pcUrl.replace("http://127.0.0.1:8080", "").replace(contextUrl, "");
+        mobUrl = mobUrl.replace("http://127.0.0.1:8080", "").replace(contextUrl, "");
+
+        logger.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = "";//ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileUrl = "";//ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        if(StringUtils.isNotEmpty(pcUrl)){
+            pcUrl = pcUrl.replace(mobileUrl, curUrl);
+        }
+
+        if(StringUtils.isNotEmpty(mobUrl)){
+            mobUrl = mobUrl.replace(curUrl, mobileUrl);
+        }
+
+        if("".equals(pcUrl)||pcUrl==null){
+            pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+        }
+
+        try {
+            pcUrl = pcUrl + "&msgId=" + msgId + "&closeType=closeWin";
+            mobUrl = mobUrl + "&msgId=" + msgId + "&closeType=closeWin";
+
+            pcUrl = URLEncoder.encode(pcUrl, "utf-8");
+            mobUrl = URLEncoder.encode(mobUrl, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        //异构系统标识
+        bodyData.put("syscode", FANWEI_CODE);
+        //流程实例id
+        bodyData.put("flowid", flowid);
+        //标题
+        bodyData.put("requestname", requestname);
+        //流程类型编码
+        bodyData.put("workflowcode", workflowcode);
+        //流程类型名称
+        bodyData.put("workflowname", workflowname);
+        //接收人所属节点
+        bodyData.put("receivenodename", "");
+        //节点名称
+        bodyData.put("nodename", nodename);
+        //PC地址
+        bodyData.put("pcurl", pcUrl);
+        //APP地址
+        bodyData.put("appurl", mobUrl);
+        //创建人
+        bodyData.put("creator", creator);
+        //创建时间
+        bodyData.put("createdatetime", createTime);
+        //接收人
+        bodyData.put("receiver", personNo);
+        //接收时间
+        bodyData.put("receivedatetime", createTime);
+        //流程处理状态:0:待办,2:已办,4:办结,8:抄送(待阅)
+        bodyData.put("isremark", isremark);
+        //流程查看状态:0:未读,1:已读
+        bodyData.put("viewtype", viewtype);
+        //接收时间戳
+        bodyData.put("receivets", curTimestamp);
+        //接收人+编码,保存日志使用,oa接口不需要此字段。
+        bodyData.put("receiverLong", receiverLong);
+
+        return bodyData;
+    }
+
+    /**
+     *
+     * @param url
+     * @param bodyData
+     * @return
+     */
+    public static JSONObject doPostByHttpClient(String url, JSONObject bodyData) {
+        try {
+            Map<String, String> headers = new HashMap();
+            headers.put("Content-Type", "application/json; charset=utf-8");
+            headers.put("Accept", "application/json");
+            logger.info(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+            System.out.println(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+
+            String responseEntify = HttpUtils.postjson(url, headers, bodyData.toJSONString());
+            JSONObject result = (JSONObject)JSONObject.parse(responseEntify);
+            return result;
+        } catch (IOException var5) {
+            throw new KDException(var5, WFErrorCode.httpRequestException(), new Object[]{var5.getMessage()});
+        }
+    }
+}

+ 34 - 0
main/java/kd/cosmic/jkjt/msg/ecology/EarlyWarnMessageHandler.java

@@ -0,0 +1,34 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.earlywarn.EarlyWarnContext;
+import kd.bos.entity.earlywarn.warn.EarlyWarnMessageInfo;
+import kd.bos.entity.earlywarn.warn.plugin.IEarlyWarnMessageHandler;
+import kd.bos.url.UrlService;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/07/03
+ * 自定义消息插件
+ */
+public class EarlyWarnMessageHandler implements IEarlyWarnMessageHandler {
+    @Override
+    public EarlyWarnMessageInfo singleMessageBuilder(DynamicObject dynamicObject, EarlyWarnContext earlyWarnContext) {
+        RequestContext ctx = RequestContext.get();
+        String clientPath = UrlService.getDomainContextUrlByTenantCode(ctx.getTenantCode());//拿到完整的客户端网址
+        String pkId = dynamicObject.get("id").toString();//对应单据的内码
+        EarlyWarnMessageInfo info = new EarlyWarnMessageInfo();
+        String url = clientPath + "?formId=bei_betransdetail_imp&pkId=" + pkId;
+        info.setContentUrl(url);
+        String mobUrl = clientPath + "/mobile.html?form=bei_betransdetail_imp_mob&pkId=" + pkId;
+        info.setMobContentUrl(mobUrl);
+
+        return info;
+    }
+
+    @Override
+    public EarlyWarnMessageInfo mergeMessageBuilder(EarlyWarnContext earlyWarnContext) {
+        return null;
+    }
+}

+ 38 - 0
main/java/kd/cosmic/jkjt/msg/ecology/EcologyListPlugin.java

@@ -0,0 +1,38 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import com.alibaba.druid.util.StringUtils;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+
+import java.util.List;
+
+public class EcologyListPlugin extends AbstractBillPlugIn {
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        if(StringUtils.equals("sendmsg",afterDoOperationEventArgs.getOperateKey())){
+            OperationResult operationResult = afterDoOperationEventArgs.getOperationResult();
+            operationResult.setShowMessage(false);
+            String message = operationResult.getMessage();
+
+            List<IOperateInfo> errorInfos = operationResult.getAllErrorOrValidateInfo();
+            boolean isSuccess = operationResult.isSuccess();
+            if(errorInfos.size() > 0){
+                operationResult.setSuccess(false);
+                StringBuffer msg = new StringBuffer();
+                for (IOperateInfo errInfo: errorInfos) {
+                    msg.append(errInfo.getMessage()).append("\r\n");
+                }
+                message = message + "\r\n" + msg.toString();
+            }
+            if(!StringUtils.isEmpty(message)) {
+                this.getView().showErrorNotification(message);
+            } else {
+                this.getView().showTipNotification("所选记录不需要重发!");
+            }
+        }
+    }
+}

+ 121 - 0
main/java/kd/cosmic/jkjt/msg/ecology/EcologyOpPlugin.java

@@ -0,0 +1,121 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import com.alibaba.druid.util.StringUtils;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.workflow.exception.WFErrorCode;
+import kd.sdk.plugin.Plugin;
+
+import java.io.IOException;
+import java.util.*;
+
+public class EcologyOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+    private static Log logger = LogFactory.getLog(EcologyOpPlugin.class);
+    private static final String KEY_ENTITY_LOG = "nckd_ecology";
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+
+        StringBuffer errMsg = new StringBuffer();
+
+        //获取选中行
+        DynamicObject[] dynamicObjects = e.getDataEntities();
+
+        //获取操作按钮操作编码
+        String operationKey = e.getOperationKey();
+        if (StringUtils.equals( "sendmsg",operationKey)){
+            List<DynamicObject> listObj = new ArrayList<>();
+            String selectField = "nckd_ecologyurl, nckd_ecologycode, nckd_ecologmsgcode, nckd_ecologappid, nckd_ecologsecret, nckd_ecologspk, nckd_ecologyurlmsg";
+            DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectField, null);
+            String FANWEI_URL = "";
+            if(commonParams.length > 0){
+                FANWEI_URL = commonParams[0].getString("nckd_ecologyurl");
+            } else {
+                return;
+            }
+
+            for(int i = 0; i < dynamicObjects.length; i++) {
+                String id = dynamicObjects[i].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, KEY_ENTITY_LOG);
+                Boolean needSend = false;
+                if(dynamicObject.getString("nckd_logtype").matches("1|5")
+                    && "fail".equals(dynamicObject.getString("nckd_sendstatus"))){
+                    needSend = true;
+                }
+
+                if(!needSend){
+                    continue;
+                }
+
+                JSONObject fanweiBodyData = JSONObject.parseObject(dynamicObject.getString("nckd_senddata_tag"));
+//                FANWEI_URL = dynamicObject.getString("nckd_sendurl");
+                String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+                JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+                if (result == null) {
+                    throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+                }
+
+                String sendStatus = "fail";
+                //失败
+                if ("1".equals(result.getString("operResult"))) {
+                    sendStatus = "success";
+                    logger.info("EcologyOpPlugin:待办(" + dynamicObject.getString("number") + ")重发成功!");
+                    errMsg.append(dynamicObject.getString("number")).append(":").append("重发成功!").append("\r\n");
+                } else {
+                    logger.info("EcologyOpPlugin:待办(" + dynamicObject.getString("number") + ")重发失败" + result.getString("message"));
+                    errMsg.append(dynamicObject.getString("number")).append(":").append("重发失败!").append("\r\n");
+                }
+
+                String response = result.toJSONString();
+                if (response.length() < 200) {
+                    dynamicObject.set("nckd_resultdata", response);
+                } else {
+                    dynamicObject.set("nckd_resultdata", response.substring(0, 200) + "...");
+                }
+                dynamicObject.set("nckd_resultdata_tag", response);
+                dynamicObject.set("nckd_sendstatus", sendStatus);
+                dynamicObject.set("nckd_sendurl", FANWEI_URL);
+
+                listObj.add(dynamicObject);
+
+                if(listObj.size() == 500) {
+                    SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+                    listObj.clear();
+                }
+            }
+            if(listObj.size() > 0) {
+                SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+            }
+        }
+
+        if(errMsg != null && errMsg.length() > 0) {
+            ////将错误信息返回到前端
+            e.setCancelMessage(errMsg.toString());
+            e.setCancel(true);
+            System.out.println("EcologyOpPlugin 错误信息:" + errMsg.toString());
+        }
+    }
+
+    public static JSONObject doPostByHttpClient(String url, JSONObject bodyData) {
+        try {
+            Map<String, String> headers = new HashMap();
+            headers.put("Content-Type", "application/json; charset=utf-8");
+            headers.put("Accept", "application/json");
+            logger.info(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+            System.out.println(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+
+            String responseEntify = HttpUtils.postjson(url, headers, bodyData.toJSONString());
+            JSONObject result = (JSONObject)JSONObject.parse(responseEntify);
+            return result;
+        } catch (IOException var5) {
+            throw new KDException(var5, WFErrorCode.httpRequestException(), new Object[]{var5.getMessage()});
+        }
+    }
+}

+ 1002 - 0
main/java/kd/cosmic/jkjt/msg/ecology/FanweiCommonUtil.java

@@ -0,0 +1,1002 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.TypeReference;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.exception.ErrorCode;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.engine.WfConfigurationUtil;
+import kd.bos.workflow.engine.msg.MessageServiceUtil;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.bos.workflow.engine.msg.info.MessageInfo;
+import kd.bos.workflow.engine.msg.info.ToDoInfo;
+import kd.bos.workflow.exception.WFErrorCode;
+import kd.bos.workflow.exception.WFMessageServiceException;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.interfaces.RSAPublicKey;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public final class FanweiCommonUtil {
+    private static Log logger = LogFactory.getLog(FanweiCommonUtil.class);
+    private static String KEY_ENTITY_LOG = "nckd_ecology";
+    private static String FANWEI_URL = "";
+    private static String FANWEI_URL_MSG = "";
+    private static String FANWEI_CODE = "";
+    private static String FANWEI_MSGCODE = "";
+    private static String FANWEI_APPID = "";
+    private static String FANWEI_SECRET = "";
+    private static String FANWEI_SPK = "";
+
+    private static void setParmamValue(){
+        String selectField = "nckd_ecologyurl, nckd_ecologycode, nckd_ecologmsgcode, nckd_ecologappid, nckd_ecologsecret, nckd_ecologspk, nckd_ecologyurlmsg";
+        DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectField, null);
+        if(commonParams.length > 0){
+            FANWEI_URL = commonParams[0].getString("nckd_ecologyurl");
+            FANWEI_URL_MSG = commonParams[0].getString("nckd_ecologyurlmsg");
+            FANWEI_CODE = commonParams[0].getString("nckd_ecologycode");
+            FANWEI_MSGCODE = commonParams[0].getString("nckd_ecologmsgcode");
+            FANWEI_APPID = commonParams[0].getString("nckd_ecologappid");
+            FANWEI_SECRET = commonParams[0].getString("nckd_ecologsecret");
+            FANWEI_SPK = commonParams[0].getString("nckd_ecologspk");
+        }
+    }
+
+    /**
+     * 创建待办
+     * @param ctx
+     * @param info
+     * @param params
+     */
+    public static void createUserToDo(MessageContext ctx, ToDoInfo info, Map<String, String> params) {
+        if (WfConfigurationUtil.isEnabled("fanwei")) {
+            setParmamValue();
+            if(StringUtils.isEmpty(FANWEI_URL)){
+                logger.info("泛微createToDo is fail :未配置泛微服务器地址!");
+                return;
+            }
+
+            try {
+                JSONObject fanweiBodyData = buildToDoBodyData(ctx, info, params);
+
+                logger.info("待办发送消息:" + fanweiBodyData.toString());
+
+                String receiverLong = fanweiBodyData.getString("receiverLong");
+                String title = fanweiBodyData.getString("requestname");
+                String oaId = fanweiBodyData.getString("flowid");
+                String uuid = saveLogData(receiverLong, title, oaId, info.getContent(), fanweiBodyData.toString(), "1", FANWEI_URL);
+                logger.info("泛微推送数据日志保存成功");
+
+                String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+                JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+                if (result == null) {
+                    throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+                }
+
+                String sendStatus = "fail";
+                //失败
+                if("1".equals(result.getString("operResult"))){
+                    sendStatus = "success";
+                    logger.info("泛微createToDo is ok , url is:" + info.getUrl());
+                } else {
+                    logger.info("泛微createToDo is fail :" + result.getString("message"));
+                }
+
+                updateLogData(uuid, result.toJSONString(), sendStatus);
+
+                logger.info("泛微门户消息发送成功,发送消息:{}, 结果:{}", fanweiBodyData.toJSONString(), result.toJSONString());
+            } catch (Exception var15) {
+                logger.info(var15.getMessage());
+                String message = ResManager.loadKDString("泛微创建待办失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var15, error, new Object[]{var15.getMessage()});
+            } finally {
+            }
+        }
+    }
+
+    /**
+     * 处理待办
+     * @param ctx
+     * @param info
+     * @param params
+     */
+    public static void dealUserToDo(MessageContext ctx, ToDoInfo info, Map<String, String> params) {
+        if (WfConfigurationUtil.isEnabled("fanwei")) {
+            setParmamValue();
+            if(StringUtils.isEmpty(FANWEI_URL)){
+                logger.info("泛微dealToDo is fail :未配置泛微服务器地址!");
+                return;
+            }
+            try {
+                JSONObject fanweiBodyData = buildToDoBodyData(ctx, info, params);
+
+                logger.info("待办发送消息:" + fanweiBodyData.toString());
+
+                String receiverLong = fanweiBodyData.getString("receiverLong");
+                String title = fanweiBodyData.getString("requestname");
+                String oaId = fanweiBodyData.getString("flowid");
+                String uuid = saveLogData(receiverLong, title, oaId, info.getContent(), fanweiBodyData.toString(), "5", FANWEI_URL);
+                logger.info("泛微推送数据日志保存成功");
+
+                String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+                JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+                if (result == null) {
+                    throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+                }
+
+                String sendStatus = "fail";
+                //失败
+                if("1".equals(result.getString("operResult"))){
+                    sendStatus = "success";
+                    logger.info("泛微dealToDo is ok , url is:" + info.getUrl());
+                } else {
+                    logger.info("泛微dealToDo is fail :" + result.getString("message"));
+                }
+
+                updateLogData(uuid, result.toJSONString(), sendStatus);
+
+                logger.info("泛微门户消息发送成功,发送消息:{}, 结果:{}", fanweiBodyData.toJSONString(), result.toJSONString());
+            } catch (Exception var15) {
+                logger.info(var15.getMessage());
+                String message = ResManager.loadKDString("泛微处理待办失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var15, error, new Object[]{var15.getMessage()});
+            } finally {
+            }
+        }
+    }
+
+    /**
+     * 撤销待办、转交、加签等
+     * @param ctx
+     * @param info
+     * @param userId
+     */
+    public static void deleteUserToDo(MessageContext ctx, ToDoInfo info, Long userId) {
+        if (WfConfigurationUtil.isEnabled("fanwei")) {
+            setParmamValue();
+            if(StringUtils.isEmpty(FANWEI_URL)){
+                logger.info("泛微deleteToDo is fail :未配置泛微服务器地址!");
+                return;
+            }
+            try {
+                JSONObject fanweiBodyData = new JSONObject();
+
+                //接收人员
+                DynamicObject user = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
+                String personNo = user.getString("number");
+                String flowid = "KD-" + info.getTaskId().toString() + "-" + personNo;
+
+                fanweiBodyData.put("syscode", FANWEI_CODE);
+                fanweiBodyData.put("flowid", flowid);
+                fanweiBodyData.put("userid", personNo);
+
+                String url = FANWEI_URL + "/rest/ofs/deleteUserRequestInfoByJson";
+                JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+                if (result == null) {
+                    throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+                }
+
+                //失败
+                if(!"1".equals(result.getString("operResult"))){
+                    logger.info("泛微deleteToDo is fail :" + result.getString("message"));
+                }
+
+                logger.info("泛微deleteToDo is ok , url is:" + info.getUrl());
+            } catch (Exception var15) {
+                logger.info(var15.getMessage());
+                String message = ResManager.loadKDString("泛微创建待办失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var15, error, new Object[]{var15.getMessage()});
+            } finally {
+            }
+        }
+    }
+
+    /**
+     * 发送消息
+     * @param ctx
+     * @param info
+     * @param params
+     */
+    public static void sendMessage(MessageContext ctx, MessageInfo info, Map<String, String> params) {
+        if (WfConfigurationUtil.isEnabled("fanwei")) {
+            setParmamValue();
+            if(StringUtils.isEmpty(FANWEI_URL)){
+                logger.info("泛微createToDo is fail :未配置泛微服务器地址!");
+                return;
+            }
+            //发送传阅消息
+            if("circulation".equals(info.getTplScene())) {
+                sendFlowMessage(ctx, info, params);
+            } else {
+                sendCustomMessage(ctx, info, params);
+            }
+        }
+    }
+
+    /**
+     * 发送传阅消息
+     * @param ctx
+     * @param info
+     * @param params
+     */
+    private static void sendFlowMessage(MessageContext ctx, MessageInfo info, Map<String, String> params){
+        try {
+            JSONObject fanweiBodyData = buildMessageBodyData(ctx, info, params);
+
+            logger.info("发送传阅消息到泛微:" + fanweiBodyData.toString());
+
+            String receiverLong = fanweiBodyData.getString("receiverLong");
+            String title = fanweiBodyData.getString("requestname");
+            String oaId = fanweiBodyData.getString("flowid");
+            String uuid = saveLogData(receiverLong, title, oaId, info.getContent(), fanweiBodyData.toString(), "2", FANWEI_URL);
+            logger.info("泛微推送数据日志保存成功");
+
+            String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+            JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+            if (result == null) {
+                throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+            }
+
+            //失败
+            if("0".equals(result.getString("operResult"))){
+                logger.info("泛微createToDo is fail :" + result.getString("message"));
+            } else {
+                logger.info("泛微createToDo is ok , url is:" + info.getContentUrl() + ",mobileurl:" + info.getMobContentUrl());
+            }
+
+            updateLogData(uuid, result.toJSONString(), "success");
+
+            logger.info("泛微门户消息发送成功,发送消息:{}, 结果:{}", fanweiBodyData.toJSONString(), result.toJSONString());
+        } catch (Exception var15) {
+            logger.info(var15.getMessage());
+            String message = ResManager.loadKDString("泛微创建待办失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+            ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+            throw new WFMessageServiceException(var15, error, new Object[]{var15.getMessage()});
+        } finally {
+        }
+    }
+
+    /**
+     * 发送消息
+     */
+    private static void sendCustomMessage(MessageContext ctx, MessageInfo info, Map<String, String> params){
+        try {
+            Map<String,String> fanweiBodyData = buildCustomMessageBodyData(ctx, info, params);
+
+            logger.info("发送传阅消息到泛微:" + fanweiBodyData.toString());
+
+            String receiverLong = fanweiBodyData.get("createrLong");
+            String title = fanweiBodyData.get("title");
+            String context = fanweiBodyData.get("context");
+            String oaId = fanweiBodyData.get("targetId");
+            String uuid = saveLogData(receiverLong, title, oaId, context, fanweiBodyData.toString(), "3", FANWEI_URL_MSG);
+            logger.info("泛微推送数据日志保存成功");
+
+            String url = FANWEI_URL_MSG + "/api/ec/dev/message/sendCustomMessageSingle";
+            Map<String, String> heads = getHttpHeads();
+
+            String data = doPostByHttpClient(url, fanweiBodyData, heads);
+
+            if (data != null && data.contains("消息发送至OA异常")) {
+                updateLogData(uuid, data, "fail");
+                return;
+            }
+
+            JSONObject result = JSONObject.parseObject(data);
+            if (result == null) {
+                throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+            }
+
+            //失败
+            if("true".equals(result.getString("status"))){
+                logger.info("泛微createToDo is ok , url is:" + info.getContentUrl() + ",mobileurl:" + info.getMobContentUrl());
+            } else {
+                logger.info("泛微createToDo is fail :" + result.getString("message"));
+            }
+
+            updateLogData(uuid, result.toJSONString(), "success");
+
+            logger.info("泛微门户消息发送成功,发送消息:{}, 结果:{}", fanweiBodyData.toString(), result.toJSONString());
+        } catch (Exception var15) {
+            logger.info(var15.getMessage());
+            String message = ResManager.loadKDString("泛微发送消息失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+            ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+            throw new WFMessageServiceException(var15, error, new Object[]{var15.getMessage()});
+        } finally {
+        }
+    }
+
+    public static Map<String, String> getHttpHeads() throws Exception {
+        Map<String, String> heads = new HashMap<>();
+        heads.put("appid", FANWEI_APPID);
+        heads.put("skipsession", "1");
+        Map<String, Object> datas = getToken();
+        //封装参数到请求头
+        if(datas == null){
+            heads.put("token", "");
+        } else {
+            //ECOLOGY返回的token
+            String token = (String) (datas.get("token"));
+            heads.put("token", token);
+        }
+
+        return heads;
+    }
+
+    public static Map<String, Object> getToken() throws Exception {
+        Map<String, String> heads = new HashMap<>();
+        RSAPublicKey publicKey = RSAUtils.getPublicKey(FANWEI_SPK);
+        String secret = RSAUtils.encryptByPublicKey(FANWEI_SECRET, publicKey);
+        //封装参数到请求头
+        heads.put("appid", FANWEI_APPID);
+        heads.put("secret", secret);
+        //调用ECOLOGY系统接口进行注册
+        String url = FANWEI_URL_MSG + "/api/ec/dev/auth/applytoken";
+
+        String uuid = saveLogData("管理员", "调用获取TOKEN接口", "", heads.toString(), heads.toString(), "4", FANWEI_URL_MSG);
+
+        String data = doPostByHttpClient(url,null, heads);
+
+        updateLogData(uuid, data, "success");
+        if (data != null && data.contains("消息发送至OA异常")) {
+            return null;
+        }
+
+        logger.info("=====testGetoken=====" + data);
+        return JSONObject.parseObject(data, new TypeReference<Map<String, Object>>() {});
+    }
+
+    /**
+     * 组装消息发送
+     * @param ctx
+     * @param info
+     * @param params
+     * @return
+     */
+    public static Map<String,String> buildCustomMessageBodyData(MessageContext ctx, MessageInfo info, Map<String, String> params) {
+        Map<String, String> map = new HashMap<>();
+
+        DynamicObject msgInfo = BusinessDataServiceHelper.loadSingleFromCache(info.getId(), "wf_msg_message");
+
+        //发送人员
+        Long createUserId = 0L;
+//        if(taskInfo != null){
+//            createUserId = taskInfo.getLong("starterid");
+//        }
+        if(ctx != null){
+            createUserId = ctx.getStartUserId();
+        }
+
+        DynamicObject createUser = BusinessDataServiceHelper.loadSingleFromCache(createUserId, "bos_user");
+        String creator = createUser == null ? "admin" : createUser.getString("number");
+
+        Long userId = Long.valueOf(params.get("userId"));
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
+        String personNo = user.getString("number");
+        String personNoLong = user.getString("name") + "(" + user.getString("number") + ")";
+        String title = info.getTitle() == null ? "" : info.getTitle();
+        String content = info.getContent() == null ? "" : info.getContent();
+
+        String linkUrl = info.getContentUrl();
+        String linkMobileUrl = info.getMobContentUrl();
+
+        if(msgInfo != null) {
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkUrl = msgInfo.getString("contenturl");
+            }
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkMobileUrl = msgInfo.getString("mobcontenturl");
+            }
+            if(StringUtils.isNotEmpty(linkMobileUrl)){
+                linkMobileUrl = linkUrl;
+            }
+        }
+
+        logger.info("消息pcurl:" + linkUrl + ", moburl" + linkMobileUrl);
+
+        String targetId = FANWEI_MSGCODE + "|KD-" + info.getChannelMsgId().toString() + "-" + personNo;
+
+        if(StringUtils.isNotEmpty(linkMobileUrl)){
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+            linkUrl = linkUrl.replace(mobileUrl, curUrl);
+            linkMobileUrl = linkMobileUrl.replace(curUrl, mobileUrl);
+        }
+
+        try {
+            linkUrl = URLEncoder.encode(linkUrl, "utf-8");
+            linkMobileUrl = URLEncoder.encode(linkMobileUrl, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        map.put("code", FANWEI_MSGCODE); // 消息来源,新建消息来源获取code 请查看文档第四大点补充
+        map.put("workCodeList", personNo);
+        map.put("creater", creator);
+        map.put("createrLong", personNoLong);
+        map.put("title", title);
+        map.put("context", content);
+        map.put("linkUrl", linkUrl);
+        map.put("linkMobileUrl", linkMobileUrl);
+        map.put("targetId", targetId); //消息来源code +“|”+业务id  消息需要打上已处理标记
+//        map.put("bizState","0"); //0 表示消息初始状态是待处理  消息需要打上已处理标记
+        return map;
+    }
+
+
+    /**
+     * 组装传阅消息
+     * @param ctx
+     * @param info
+     * @param params
+     * @return
+     */
+    public static JSONObject buildMessageBodyData(MessageContext ctx, MessageInfo info, Map<String, String> params) {
+        JSONObject bodyData = new JSONObject();
+        Long userId = Long.valueOf(params.get("userId"));
+        String isremark = params.get("isremark").toString();
+        String viewtype = params.get("viewtype").toString();
+        String content = info.getContent() == null ? "" : info.getContent();
+
+        //发送人员
+        Long createUserId = 0L;
+        if(info != null && info.getSenderId() != null){
+            createUserId = info.getSenderId();
+        }
+
+        DynamicObject createUser = BusinessDataServiceHelper.loadSingleFromCache(createUserId, "bos_user");
+        String creator = createUser == null ? "" : createUser.getString("number");
+
+        String requestname = info.getTitle() + ", 发送人:" + creator + ", " + content;
+        String nodename = info.getOperation() == null ? "circulation" : info.getOperation();
+        String workflowname = "财务系统审批流";
+
+        if(info.getParams() != null){
+            Map<String, Object> config = info.getParams();
+            if(config != null) {
+                if(config.get("messageContext") != null){
+                    JSONObject messageContext = JSONObject.parseObject(config.get("messageContext").toString());
+                    if(messageContext.get("taskId") != null){
+                        DynamicObject taskInfo = BusinessDataServiceHelper.loadSingleFromCache(Long.valueOf(messageContext.getString("taskId")), "wf_task");
+                        if(taskInfo != null){
+                            nodename = taskInfo.getString("name");
+
+                            if(taskInfo.getLong("processdefinitionid") > 0){
+                                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                                if(processdef != null){
+                                    workflowname = processdef.getString("name");
+                                }
+                            }
+
+                            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                                workflowname = taskInfo.getString("entityname") + "审批流程";
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
+        String personNo = user.getString("number");
+        String receiverLong = user.getString("name") + "(" + user.getString("number") + ")";
+
+        //创建时间
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String createTime = sdf.format(new Date());
+        String curTimestamp = String.valueOf(System.currentTimeMillis());
+        String flowid = "KD-" + info.getChannelMsgId().toString() + "-" + personNo;
+
+        String pcUrl = info.getContentUrl() == null ? "" : info.getContentUrl();
+        String mobUrl = info.getMobContentUrl() == null ? pcUrl : info.getMobContentUrl();
+
+        logger.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        if(StringUtils.isNotEmpty(pcUrl)){
+            pcUrl = pcUrl.replace(mobileUrl, curUrl);
+        }
+
+        if(StringUtils.isNotEmpty(mobUrl)){
+            mobUrl = mobUrl.replace(curUrl, mobileUrl);
+        }
+
+        if("".equals(pcUrl)||pcUrl==null){
+            pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+        }
+
+        try {
+            pcUrl = URLEncoder.encode(pcUrl, "utf-8");
+            mobUrl = URLEncoder.encode(mobUrl, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        //接收人+编码,保存日志使用,oa接口不需要此字段。
+        bodyData.put("receiverLong", receiverLong);
+        //创建人
+        bodyData.put("creator", creator);
+        //创建时间
+        bodyData.put("createdatetime", createTime);
+        //接收人
+        bodyData.put("receiver", personNo);
+        //接收时间
+        bodyData.put("receivedatetime", createTime);
+        //接收时间戳
+        bodyData.put("receivets", curTimestamp);
+        //流程处理状态:0:待办,2:已办,4:办结,8:抄送(待阅)
+        bodyData.put("isremark", isremark);
+        //流程查看状态:0:未读,1:已读
+        bodyData.put("viewtype", viewtype);
+        //异构系统标识
+        bodyData.put("syscode", FANWEI_CODE);
+        //流程实例id
+        bodyData.put("flowid", flowid);
+        //标题
+        bodyData.put("requestname", requestname);
+        //流程类型名称
+        bodyData.put("workflowname", workflowname);
+        //节点名称
+        bodyData.put("nodename", nodename);
+        //PC地址
+        bodyData.put("pcurl", pcUrl);
+        //APP地址
+        bodyData.put("appurl", mobUrl);
+
+        return bodyData;
+    }
+
+    /**
+     * 组装待办
+     * @param todoInfo
+     * @return
+     */
+    public static JSONObject buildToDoBodyData(MessageContext ctx, ToDoInfo todoInfo, Map<String, String> params) {
+
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(todoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(todoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        JSONObject bodyData = new JSONObject();
+        Long userId = Long.valueOf(params.get("userId"));
+        String isremark = params.get("isremark").toString();
+        String viewtype = params.get("viewtype").toString();
+
+        //发送人员
+        Long createUserId = 0L;
+        if(taskInfo != null){
+            createUserId = taskInfo.getLong("starterid");
+        }
+        if(createUserId.compareTo(0L) == 0){
+            createUserId = ctx.getStartUserId();
+        }
+
+        DynamicObject createUser = BusinessDataServiceHelper.loadSingleFromCache(createUserId, "bos_user");
+        String creator = createUser == null ? "" : createUser.getString("number");
+
+        String title = todoInfo.getTitle();
+        if(todoInfo.getParams() != null && todoInfo.getParams().get("subject") != null){
+            JSONObject subject = JSONObject.parseObject(todoInfo.getParams().get("subject").toString());
+            title = subject.getString("zh_CN");
+        }
+
+        if(StringUtils.isEmpty(title) && taskInfo != null){
+            title = "请处理" + taskInfo.getString("startname")
+                    + "提交的" + taskInfo.getString("entityname") + taskInfo.getString("billno");
+        }
+        String requestname = title;
+        String nodename = todoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+        }
+
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingleFromCache(userId, "bos_user");
+        String personNo = user.getString("number");
+        String receiverLong = user.getString("name") + "(" + user.getString("number") + ")";
+
+        //创建时间
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String createTime = sdf.format(new Date());
+        String curTimestamp = String.valueOf(System.currentTimeMillis());
+        String flowid = "KD-" + todoInfo.getTaskId().toString() + "-" + personNo;
+
+        //add by wanghaiw_kd 2024/04/28
+        //如果获取的任务对象为空,则获取flowid相同的待办消息中的发送参数,修改状态、接收时间,做为待办处理的发送参数
+        if(taskInfo == null){
+            QFilter qFilter = new QFilter("nckd_oaid", QCP.equals, flowid);
+            qFilter.and(new QFilter("nckd_logtype", QCP.equals, "1"));
+
+            DynamicObject ecologyLog = BusinessDataServiceHelper.loadSingle("nckd_ecology", qFilter.toArray());
+
+            if(ecologyLog != null && StringUtils.isEmpty(ecologyLog.getString("nckd_senddata_tag"))){
+                JSONObject fanweiBodyData = JSONObject.parseObject(ecologyLog.getString("nckd_senddata_tag"));
+
+                //流程处理状态:0:待办,2:已办,4:办结,8:抄送(待阅)
+                fanweiBodyData.put("isremark", "2");
+                //流程查看状态:0:未读,1:已读
+                fanweiBodyData.put("viewtype", "1");
+                //接收时间
+                fanweiBodyData.put("receivedatetime", createTime);
+                //接收时间戳
+                fanweiBodyData.put("receivets", curTimestamp);
+
+                return fanweiBodyData;
+            }
+        }
+
+
+        String url = MessageServiceUtil.buildWebPageUrlForMyApplyed(ctx.getProcessInstanceId());
+
+        logger.info("消息pcurl:" + url);
+
+        String pcUrl = url;
+        String mobUrl = url;
+
+        if(StringUtils.isNotEmpty(pcUrl)){
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+            pcUrl = pcUrl.replace(mobileUrl, curUrl);
+            mobUrl = mobUrl.replace(curUrl, mobileUrl);
+        }
+
+        try {
+            pcUrl = URLEncoder.encode(pcUrl, "utf-8");
+            mobUrl = URLEncoder.encode(mobUrl, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+
+        //接收人+编码,保存日志使用,oa接口不需要此字段。
+        bodyData.put("receiverLong", receiverLong);
+        //创建人
+        bodyData.put("creator", creator);
+        //创建时间
+        bodyData.put("createdatetime", createTime);
+        //接收人
+        bodyData.put("receiver", personNo);
+        //接收时间
+        bodyData.put("receivedatetime", createTime);
+        //接收时间戳
+        bodyData.put("receivets", curTimestamp);
+        //流程处理状态:0:待办,2:已办,4:办结,8:抄送(待阅)
+        bodyData.put("isremark", isremark);
+        //流程查看状态:0:未读,1:已读
+        bodyData.put("viewtype", viewtype);
+        //异构系统标识
+        bodyData.put("syscode", FANWEI_CODE);
+        //流程实例id
+        bodyData.put("flowid", flowid);
+        //标题
+        bodyData.put("requestname", requestname);
+        //流程类型名称
+        bodyData.put("workflowname", workflowname);
+        //节点名称
+        bodyData.put("nodename", nodename);
+        //PC地址
+        bodyData.put("pcurl", pcUrl);
+        //APP地址
+        bodyData.put("appurl", mobUrl);
+
+        return bodyData;
+    }
+
+    /**
+     * 保存日志
+     * @param userid
+     * @param title
+     * @param msgContent
+     * @param sendData
+     * @return
+     */
+    private static String saveLogData(String userid, String title, String oaId, String msgContent, String sendData, String logType, String url) {
+        try {
+            logger.info("记录新增泛微推送数据日志");
+
+            msgContent = msgContent == null ? "" : msgContent;
+            sendData = sendData == null ? "" : sendData;
+
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+
+            uuid = uuid == null ? "uuid" + String.valueOf(System.currentTimeMillis()) : uuid;
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_LOG);
+
+            dynamicObject.set("enable", "1");
+            dynamicObject.set("status", "C");
+            dynamicObject.set("number", uuid);
+            dynamicObject.set("nckd_logtype", logType);
+            dynamicObject.set("nckd_sendurl", url);
+            dynamicObject.set("nckd_receiver", userid);
+            dynamicObject.set("nckd_title", title);
+            dynamicObject.set("nckd_oaid", oaId);
+
+            dynamicObject.set("nckd_sendtime", new Date());
+            if (sendData.length() < 200) {
+                dynamicObject.set("nckd_senddata", sendData);
+            } else {
+                dynamicObject.set("nckd_senddata", sendData.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_senddata_tag", sendData);
+
+            if (msgContent.length() < 200) {
+                dynamicObject.set("nckd_msgcontent", msgContent);
+            } else {
+                dynamicObject.set("nckd_msgcontent", msgContent.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_msgcontent_tag", msgContent);
+
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            logger.info("记录新增泛微推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            logger.info("记录新增泛微推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * 更新日志
+     * @param uuid
+     * @param response
+     */
+    private static void updateLogData(String uuid, String response, String sendStatus) {
+        try {
+            logger.info("记录更新泛微推送结果数据日志");
+            response = response == null ? "" : response;
+
+            QFilter qFilterCas = new QFilter("number", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load(KEY_ENTITY_LOG, "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, KEY_ENTITY_LOG);
+                if (response.length() < 200) {
+                    dynamicObject.set("nckd_resultdata", response);
+                } else {
+                    dynamicObject.set("nckd_resultdata", response.substring(0, 200) + "...");
+                }
+                dynamicObject.set("nckd_resultdata_tag", response);
+                dynamicObject.set("nckd_sendstatus", sendStatus);
+
+                SaveServiceHelper.update(dynamicObject);
+
+                logger.info("记录更新泛微推送结果数据日志成功");
+            }
+        } catch (Exception e) {
+            logger.info("记录更新泛微推送结果数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     *
+     * @param url
+     * @param bodyData
+     * @return
+     */
+    public static JSONObject doPostByHttpClient(String url, JSONObject bodyData) {
+        try {
+            Map<String, String> headers = new HashMap();
+            headers.put("Content-Type", "application/json; charset=utf-8");
+            headers.put("Accept", "application/json");
+            logger.info(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+            System.out.println(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+
+            String responseEntify = HttpUtils.postjson(url, headers, bodyData.toJSONString());
+            JSONObject result = (JSONObject)JSONObject.parse(responseEntify);
+            return result;
+        } catch (IOException var5) {
+            throw new KDException(var5, WFErrorCode.httpRequestException(), new Object[]{var5.getMessage()});
+        }
+    }
+
+    /**
+     * 发送普通消息到泛微OA
+     * @param path
+     * @param params
+     * @param data
+     * @return
+     */
+    public static String doPostByHttpClient(String path, Map<String, String> params, Map<String, String> data) {
+        if(path.toLowerCase().startsWith("https")){
+            return doPostByHttps(path, params, data);
+        }
+        return doPostByHttp(path, params, data);
+    }
+
+    /**
+     * http服务
+     * @param path
+     * @param params
+     * @param data
+     * @return
+     */
+    public static String doPostByHttp(String path, Map<String, String> params, Map<String, String> data) {
+        String errMsg = "";
+        try {
+            String str = "";
+            URL url = new URL(path);
+            //打开和url之间的连接
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            // 请求参数 编码为 utf-8
+            //请求方式
+            conn.setRequestMethod("POST");
+            //设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
+            if (data != null) {
+                for (Map.Entry<String, String> entry : data.entrySet()) {
+                    conn.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            //设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
+            //最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
+            //post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
+            if (params != null) {
+                out.write(mapToStr(params));
+            }
+            //缓冲数据
+            out.flush();
+            out.close();
+            //获取URLConnection对象对应的输入流
+            InputStream is = conn.getInputStream();
+            //构造一个字符流缓存
+            BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
+            String result = "";
+            while ((str = br.readLine()) != null) {
+                result = str;
+            }
+            //关闭流
+            is.close();
+            //断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被其他线程使用就不切断。
+            //固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上disconnect后正常一些。
+            conn.disconnect();
+            return result;
+        } catch (Exception e) {
+            errMsg = "消息发送至OA异常:" + e;
+            logger.warn("消息发送至OA异常:", e);
+            //e.printStackTrace();
+//            return errMsg;
+        }
+        return errMsg;
+    }
+
+    /**
+     * htts服务
+     * @param path
+     * @param params
+     * @param data
+     * @return
+     */
+    public static String doPostByHttps(String path, Map<String, String> params, Map<String, String> data) {
+        String errMsg = "";
+        try {
+            String str = "";
+            TrustManager[] tm = { new MyX509TrustManager() };
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(null, tm, new java.security.SecureRandom());
+            // 从上述SSLContext对象中得到SSLSocketFactory对象
+            SSLSocketFactory ssf = sslContext.getSocketFactory();
+            HttpsURLConnection.setDefaultSSLSocketFactory(ssf);
+            // 打开和URL之间的连接
+            URL realUrl = new URL(path);
+            HttpsURLConnection conn = (HttpsURLConnection) realUrl.openConnection();
+            // 避免只要域名提交
+            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+            // 请求参数 编码为 utf-8
+            //请求方式
+            conn.setRequestMethod("POST");
+            //设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
+            if (data != null) {
+                for (Map.Entry<String, String> entry : data.entrySet()) {
+                    conn.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            //设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
+            //最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
+            //post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
+            if (params != null) {
+                out.write(mapToStr(params));
+            }
+            //缓冲数据
+            out.flush();
+            out.close();
+            //获取URLConnection对象对应的输入流
+            InputStream is = conn.getInputStream();
+            //构造一个字符流缓存
+            BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
+            String result = "";
+            while ((str = br.readLine()) != null) {
+                result = str;
+            }
+            //关闭流
+            is.close();
+            //断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被其他线程使用就不切断。
+            //固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上disconnect后正常一些。
+            conn.disconnect();
+            return result;
+        } catch (Exception e) {
+            errMsg = "消息发送至OA异常:" + e;
+            logger.warn("消息发送至OA异常:", e);
+            //e.printStackTrace();
+//            return errMsg;
+        }
+        return errMsg;
+    }
+
+    /**
+     * 将Map转换成字符串参数,用于POST GET 请求
+     * @param map
+     * @return
+     */
+    public static String mapToStr(Map<String, String> map) {
+        StringBuilder stringBuilder = new StringBuilder();
+        if (map != null) {
+            for (Map.Entry<String, String> entry : map.entrySet()) {
+                stringBuilder.append(entry.getKey());
+                if (entry.getValue() != null) {
+                    stringBuilder.append("=").append(entry.getValue());
+                }
+                stringBuilder.append("&");
+            }
+        }
+        if (stringBuilder.length() > 0) {
+            return stringBuilder.substring(0, stringBuilder.length() - 1);
+        }
+        return null;
+    }
+}

+ 189 - 0
main/java/kd/cosmic/jkjt/msg/ecology/FanweiServiceHelper.java

@@ -0,0 +1,189 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.exception.ErrorCode;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.workflow.engine.msg.AbstractMessageServiceHandler;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.bos.workflow.engine.msg.info.MessageInfo;
+import kd.bos.workflow.engine.msg.info.ToDoInfo;
+import kd.bos.workflow.exception.WFMessageServiceException;
+import org.apache.commons.collections.CollectionUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 插件说明:消息渠道插件,泛微消息处理
+ * @author wanghaiwu_kd
+ * @date 2023/04/10
+ */
+public class FanweiServiceHelper extends AbstractMessageServiceHandler {
+    private static Log logger = LogFactory.getLog(FanweiServiceHelper.class);
+
+    public FanweiServiceHelper() {
+    }
+
+    /**
+     * 待办创建
+     * @param ctx
+     * @param info
+     */
+    @Override
+    public void createToDo(MessageContext ctx, ToDoInfo info) {
+        logger.info("泛微待办创建开始 ctx: " + ctx.toString() + ",taskId:" + ctx.getTaskId() + ", taskId:" + info.getTaskId());
+
+        DynamicObject taskInfo = BusinessDataServiceHelper.loadSingleFromCache(info.getTaskId(), "wf_task");
+        //获取到的taskInfo可能是空的
+        String handlestate = taskInfo == null ? "" : taskInfo.getString("handlestate");
+
+        List<Long> userIds = info.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            logger.info("人员为空,推送失败!");
+            return;
+        }
+
+        Map<String, String> params = new HashMap<>(3);
+        params.put("isremark", "0");
+        params.put("viewtype", "0");
+
+        for(int i = 0; i < userIds.size(); i++){
+            Long userId = userIds.get(i);
+            params.put("userId", userId.toString());
+
+            try {
+                FanweiCommonUtil.createUserToDo(ctx, info, params);
+                logger.info("create todo content=" + info.getTaskId());
+            } catch (Exception var8) {
+                String message = ResManager.loadKDString("泛微待办创建失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var8, error, new Object[]{var8.getMessage()});
+            }
+        }
+    }
+
+    /**
+     * 待办处理
+     * @param ctx
+     * @param info
+     */
+    @Override
+    public void dealToDo(MessageContext ctx, ToDoInfo info) {
+        logger.info("泛微待办处理开始 ctx: " + ctx.toString() + ",taskId:" + ctx.getTaskId() + ", taskId:" + info.getTaskId());
+
+//        DynamicObject taskInfo = BusinessDataServiceHelper.loadSingleFromCache(info.getTaskId(), "wf_task");
+//        //获取到的taskInfo可能是空的
+//        String handlestate = taskInfo == null ? "" : taskInfo.getString("handlestate");
+
+        //驳回状态的任务做提交处理时不做待办更新推送。
+//        if("dismissed".equals(handlestate)){
+//            return;
+//        }
+
+        List<Long> userIds = info.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            logger.info("人员为空,推送失败!");
+            return;
+        }
+
+        Map<String, String> params = new HashMap<>(3);
+        params.put("isremark", "2");
+        params.put("viewtype", "1");
+
+        for(int i = 0; i < userIds.size(); i++){
+            Long userId = userIds.get(i);
+            params.put("userId", userId.toString());
+
+            try {
+                FanweiCommonUtil.dealUserToDo(ctx, info, params);
+                logger.info("deal todo content=" + info.getTaskId());
+            } catch (Exception var8) {
+                String message = ResManager.loadKDString("泛微待办处理失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var8, error, new Object[]{var8.getMessage()});
+            }
+        }
+    }
+
+    /**
+     * 待办删除
+     * @param ctx
+     * @param info
+     */
+    @Override
+    public void deleteToDo(MessageContext ctx, ToDoInfo info) {
+        logger.info("泛微待删除办开始 ctx: " + ctx.toString() + ",taskId:" + ctx.getTaskId() + ", taskId:" + info.getTaskId());
+
+        List<Long> userIds = info.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            logger.info("人员为空,删除失败!");
+            return;
+        }
+
+        for(int i = 0; i < userIds.size(); i++){
+            Long userId = userIds.get(i);
+
+            try {
+                FanweiCommonUtil.deleteUserToDo(ctx, info, userId);
+                logger.info("delete todo content=" + info.getTaskId());
+            } catch (Exception var8) {
+                String message = ResManager.loadKDString("泛微待办删除失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var8, error, new Object[]{var8.getMessage()});
+            }
+        }
+    }
+
+    /**
+     * 发送消息
+     * @param ctx
+     * @param info
+     */
+    @Override
+    public void sendMessage(MessageContext ctx, MessageInfo info) {
+        //消息渠道
+        String channel = info.getNotifyType();
+        if(!"fanwei".equals(channel)){
+            return;
+        }
+
+//        String type = info.getType();
+//        //只发送传阅的消息
+//        if(!"circulation".equals(info.getTplScene())){
+//            return;
+//        }
+
+        if (ctx != null){
+            logger.info("泛微消息url:" + info.getContentUrl() + ",mobileurl:" + info.getMobContentUrl()+ ", pk:" + ctx.getBusinessKey());
+        }else {
+            logger.info("泛微消息url:" + info.getContentUrl() + ",mobileurl:" + info.getMobContentUrl());
+        }
+        List<Long> userIds = info.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            logger.info("人员为空,删除失败!");
+            return;
+        }
+
+        Map<String, String> params = new HashMap<>(3);
+        params.put("isremark", "8");
+        params.put("viewtype", "0");
+
+        for(int i = 0; i < userIds.size(); i++){
+            Long userId = userIds.get(i);
+            params.put("userId", userId.toString());
+
+            try {
+                FanweiCommonUtil.sendMessage(ctx, info, params);
+                logger.info("create todo content=" + info.getChannelMsgId());
+            } catch (Exception var8) {
+                String message = ResManager.loadKDString("泛微消息创建失败,原因:%s", "WFErrorCode_10001", "bos-wf-engine", new Object[0]);
+                ErrorCode error = new ErrorCode("bos.wf.rpa.", message);
+                throw new WFMessageServiceException(var8, error, new Object[]{var8.getMessage()});
+            }
+        }
+    }
+}

+ 188 - 0
main/java/kd/cosmic/jkjt/msg/ecology/HttpUtils.java

@@ -0,0 +1,188 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.cert.CertificateException;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.devportal.common.util.HttpClientUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.GzipDecompressingEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpUtils {
+    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+    private static int default_connectionTimeout = Integer.getInteger("httpclient.connectionTimeout", 3000);
+    private static int default_readTimeout = Integer.getInteger("httpclient.readTimeout", 5000);
+    private static String SCHEME_HTTPS = "https";
+
+    public static String postjson(String url, Map<String, String> header, String json) throws IOException {
+        return postjson(url, header, json, default_connectionTimeout, default_readTimeout);
+    }
+
+
+
+    public static String postjson(String url, Map<String, String> header, String json, int connectionTimeout, int readTimeout) throws IOException {
+        String CONTENT_TYPE_TEXT_JSON = "text/json;";
+        String data = "";
+        HttpClient client = null;
+        HttpPost post = new HttpPost(url);
+        URI uri = post.getURI();
+        if (SCHEME_HTTPS.equals(uri.getScheme())) {
+            client = wrapperHttpClient(connectionTimeout, readTimeout);
+            if (client == null) {
+                return data;
+            }
+        } else {
+            client = createHttpClient(connectionTimeout, readTimeout);
+            HttpParams httpParams = client.getParams();
+            HttpConnectionParams.setConnectionTimeout(httpParams, connectionTimeout);
+            HttpConnectionParams.setSoTimeout(httpParams, readTimeout);
+        }
+
+        try {
+            if (header != null && header.size() != 0) {
+                Iterator<String> iterator = header.keySet().iterator();
+
+                while(iterator.hasNext()) {
+                    String key = (String)iterator.next();
+                    post.setHeader(key, (String)header.get(key));
+                }
+            }
+
+            StringEntity se = new StringEntity(json, ContentType.APPLICATION_JSON);
+            se.setContentType("text/json;");
+            post.setEntity(se);
+            HttpResponse response = client.execute(post);
+            if (response.getStatusLine().getStatusCode() == 200) {
+                HttpEntity resEntity = response.getEntity();
+                Header respHeader = resEntity.getContentEncoding();
+                if (respHeader == null || !"gzip".equalsIgnoreCase(respHeader.getValue()) && !"x-gzip".equalsIgnoreCase(respHeader.getValue())) {
+                    data = EntityUtils.toString(resEntity);
+                } else {
+                    GzipDecompressingEntity gzipEntity = new GzipDecompressingEntity(resEntity);
+                    InputStream in = gzipEntity.getContent();
+                    data = getHTMLContent(in);
+                }
+            }
+        } catch (IOException var19) {
+            logger.warn("消息发送至OA异常:", var19);
+            JSONObject errObj = new JSONObject();
+            errObj.put("operResult", "0");
+            errObj.put("message", "消息发送至OA异常:" + var19);
+            data = errObj.toJSONString();
+//            throw var19;
+        } catch (Exception e){
+            logger.warn("消息发送至OA异常:", e);
+            JSONObject errObj = new JSONObject();
+            errObj.put("operResult", "0");
+            errObj.put("message", "消息发送至OA异常:" + e);
+            data = errObj.toJSONString();
+        } finally {
+            post.releaseConnection();
+        }
+
+        return data;
+    }
+
+    private static String getHTMLContent(InputStream in) {
+        StringBuffer sb = new StringBuffer();
+        BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+        try {
+            String line = null;
+
+            while((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+        } catch (IOException var12) {
+            logger.error("getHTMLContent error", var12);
+        } finally {
+            try {
+                br.close();
+            } catch (IOException var11) {
+                logger.error("getHTMLContent error", var11);
+            }
+
+        }
+
+        return sb.toString();
+    }
+
+    private static HttpClient createHttpClient(int connectionTimeout, int readTimeout) {
+        HttpClient client = new DefaultHttpClient();
+        HttpParams httpParams = client.getParams();
+        HttpConnectionParams.setConnectionTimeout(httpParams, connectionTimeout);
+        HttpConnectionParams.setSoTimeout(httpParams, readTimeout);
+        return client;
+    }
+
+    public static HttpClient wrapperHttpClient(int connectionTimeout, int readTimeout) {
+        try {
+            final X509TrustManager trustManager = new X509TrustManager() {
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                public void checkClientTrusted(final javax.security.cert.X509Certificate[] arg0, final String arg1) throws CertificateException {
+                }
+
+                public void checkServerTrusted(final javax.security.cert.X509Certificate[] arg0, final String arg1) throws CertificateException {
+                }
+
+                @Override
+                public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws java.security.cert.CertificateException {
+                }
+
+                @Override
+                public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws java.security.cert.CertificateException {
+                }
+            };
+            SSLContext ctx = SSLContext.getInstance("TLS");
+            ctx.init((KeyManager[])null, new TrustManager[]{trustManager}, (SecureRandom)null);
+            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
+            RequestConfig requestConfig = RequestConfig.custom().setCookieSpec("standard-strict").setConnectTimeout(connectionTimeout).setConnectionRequestTimeout(connectionTimeout).setSocketTimeout(readTimeout).setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList("NTLM", "Digest")).setProxyPreferredAuthSchemes(Arrays.asList("Basic")).build();
+            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", socketFactory).build();
+            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+            CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig).build();
+            return closeableHttpClient;
+        } catch (Exception var9) {
+            logger.error("包装无证书校验客户端失败:" + var9.getMessage());
+            return null;
+        }
+    }
+}

+ 95 - 0
main/java/kd/cosmic/jkjt/msg/ecology/MessageCompiler.java

@@ -0,0 +1,95 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.earlywarn.EarlyWarnContext;
+import kd.bos.entity.earlywarn.kit.StringTemplateParser;
+import kd.bos.entity.earlywarn.kit.StringUtil;
+import kd.bos.entity.earlywarn.warn.plugin.IEarlyWarnMessageCompiler;
+import kd.bos.i18n.mservice.I18nServiceHelper;
+import kd.bos.i18n.mservice.utils.AmountConvertResult;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * @add by wanghaiwu_kd
+ * @date 2023/06/08
+ * @description 预警消息插件,消息字段格式化
+ * 表单标识:业务预警对象:JK-LargeTransactionWarning
+ */
+public class MessageCompiler implements IEarlyWarnMessageCompiler{
+    @Override
+    public String getSingleMessage(String expression, List<String> fields, DynamicObject data, EarlyWarnContext context) {
+        if (expression == null || data == null){
+            return "";
+        }
+
+        Map<String, String> map = new HashMap<>();
+        for (String field : fields) {
+            String value = "";
+            String[] arr = StringUtil.split(field, ".");
+            Object objValue = getValue(data, arr);
+            value = objValue == null ? "" : objValue.toString();
+            map.put(field,value);
+        }
+        StringTemplateParser parser = new StringTemplateParser();
+        return parser.parse(expression,name->map.get(name));
+    }
+
+    public static BigDecimal getBigDecimal(Object value) {
+        BigDecimal val = null;
+        if( value != null ) {
+            if( value instanceof BigDecimal ) {
+                val = (BigDecimal) value;
+            } else if( value instanceof String ) {
+                val = new BigDecimal( (String) value );
+            } else if( value instanceof BigInteger) {
+                val = new BigDecimal( (BigInteger) value );
+            } else if( value instanceof Number ) {
+                val = new BigDecimal( ((Number)value).doubleValue() );
+            } else {
+                throw new ClassCastException("Not possible to coerce ["+value+"] from class "+value.getClass()+" into a BigDecimal.");
+            }
+        }
+        return val;
+    }
+
+    private String getValue(DynamicObject data, String[] arr) {
+        if(null == arr || arr.length == 0){
+            return "";
+        }
+        Object obj = data.get(arr[0]);
+        if(obj instanceof DynamicObject){
+            return getValue((DynamicObject)obj, Arrays.copyOfRange(arr, 1, arr.length));
+        }
+
+        if(obj instanceof Date){
+            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+            obj = df.format(obj);
+        } else if(obj instanceof BigDecimal || obj instanceof Number){
+            BigDecimal value1 = getBigDecimal(obj);
+            obj = value1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
+        }
+
+        if("bankcheckflag".equals(arr[0])){
+            BigDecimal debitAmount = getBigDecimal(data.get("debitamount"));
+            String amountStr = debitAmount.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
+
+            AmountConvertResult result = I18nServiceHelper.amountConvertUppercase("ZH", "CNY", amountStr,"false");
+            if(result != null){
+                obj = result.getResult();
+            }
+        }
+
+
+
+        return StringUtil.toSafeString(obj);
+    }
+
+    @Override
+    public String getMergeMessage(String s, List<String> list, EarlyWarnContext earlyWarnContext) {
+        return null;
+    }
+}

+ 20 - 0
main/java/kd/cosmic/jkjt/msg/ecology/MyX509TrustManager.java

@@ -0,0 +1,20 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+public class MyX509TrustManager implements X509TrustManager {
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+    }
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+    }
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return null;
+    }
+}

+ 163 - 0
main/java/kd/cosmic/jkjt/msg/ecology/RSAUtils.java

@@ -0,0 +1,163 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/05/15
+ * @description RSA算法工具类
+ */
+public class RSAUtils {
+    // 加密算法
+    private final static String ALGORITHM_RSA = "RSA";
+
+    /**
+     * 直接生成公钥、私钥对象
+     * @param modulus
+     * @throws NoSuchAlgorithmException
+     */
+    public static List<Key> getRSAKeyObject(int modulus) throws NoSuchAlgorithmException{
+        List<Key> keyList = new ArrayList<>(2);
+        // 创建RSA密钥生成器
+        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM_RSA);
+        // 设置密钥的大小,此处是RSA算法的模长 = 最大加密数据的大小
+        keyPairGen.initialize(modulus);
+        KeyPair keyPair = keyPairGen.generateKeyPair();
+        // keyPair.getPublic() 生成的是RSAPublic的是咧
+        keyList.add(keyPair.getPublic());
+        // keyPair.getPrivate() 生成的是RSAPrivateKey的实例
+        keyList.add(keyPair.getPrivate());
+        return keyList;
+    }
+
+    /**
+     * 生成公钥、私钥的字符串
+     * 方便传输
+     * @param modulus 模长
+     * @return
+     * @throws NoSuchAlgorithmException
+     */
+    public static List<String> getRSAKeyString(int modulus) throws NoSuchAlgorithmException{
+
+        List<String> keyList = new ArrayList<>(2);
+        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM_RSA);
+        keyPairGen.initialize(modulus);
+        KeyPair keyPair = keyPairGen.generateKeyPair();
+        String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
+        String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
+        keyList.add(publicKey);
+        keyList.add(privateKey);
+        return keyList;
+    }
+
+    // Java中RSAPublicKeySpec、X509EncodedKeySpec支持生成RSA公钥
+    // 此处使用X509EncodedKeySpec生成
+    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
+
+        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
+        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
+        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
+        return (RSAPublicKey) keyFactory.generatePublic(spec);
+    }
+
+    // Java中只有RSAPrivateKeySpec、PKCS8EncodedKeySpec支持生成RSA私钥
+    // 此处使用PKCS8EncodedKeySpec生成
+    public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
+
+        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
+        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
+        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
+        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param data
+     * @param publicKey
+     * @return
+     * @throws Exception
+     */
+    public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
+            throws Exception {
+        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
+        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+        // 模长n转换成字节数
+        int modulusSize = publicKey.getModulus().bitLength() / 8;
+        // PKCS Padding长度为11字节,所以实际要加密的数据不能要 - 11byte
+        int maxSingleSize = modulusSize - 11;
+        // 切分字节数组,每段不大于maxSingleSize
+        byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        // 分组加密,并将加密后的内容写入输出字节流
+        for (byte[] s : dataArray) {
+            out.write(cipher.doFinal(s));
+        }
+        // 使用Base64将字节数组转换String类型
+        return Base64.getEncoder().encodeToString(out.toByteArray());
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param data
+     * @param privateKey
+     * @return
+     * @throws Exception
+     */
+    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)
+            throws Exception {
+        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+        // RSA加密算法的模长 n
+        int modulusSize = privateKey.getModulus().bitLength() / 8;
+        byte[] dataBytes = data.getBytes();
+        // 之前加密的时候做了转码,此处需要使用Base64进行解码
+        byte[] decodeData = Base64.getDecoder().decode(dataBytes);
+        // 切分字节数组,每段不大于modulusSize
+        byte[][] splitArrays = splitArray(decodeData, modulusSize);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        for(byte[] arr : splitArrays){
+            out.write(cipher.doFinal(arr));
+        }
+        return new String(out.toByteArray());
+    }
+
+    /**
+     * 按指定长度切分数组
+     *
+     * @param data
+     * @param len 单个字节数组长度
+     * @return
+     */
+    private static byte[][] splitArray(byte[] data,int len) {
+        int dataLen = data.length;
+        if (dataLen <= len) {
+            return new byte[][]{data};
+        }
+        byte[][] result = new byte[(dataLen - 1) / len + 1][];
+        int resultLen = result.length;
+        for (int i = 0; i < resultLen; i++) {
+            if (i == resultLen - 1) {
+                int slen = dataLen - len * i;
+                byte[] single = new byte[slen];
+                System.arraycopy(data, len * i, single, 0, slen);
+                result[i] = single;
+                break;
+            }
+            byte[] single = new byte[len];
+            System.arraycopy(data, len * i, single, 0, len);
+            result[i] = single;
+        }
+        return result;
+    }
+}

+ 106 - 0
main/java/kd/cosmic/jkjt/msg/ecology/RefreshFanWeiMsgStatusTask.java

@@ -0,0 +1,106 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.workflow.exception.WFErrorCode;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RefreshFanWeiMsgStatusTask extends AbstractTask {
+    private static Log logger = LogFactory.getLog(RefreshFanWeiMsgStatusTask.class);
+    private static String KEY_ENTITY_LOG = "nckd_ecology";
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        String selectField = "nckd_ecologyurl, nckd_ecologycode, nckd_ecologmsgcode, nckd_ecologappid, nckd_ecologsecret, nckd_ecologspk, nckd_ecologyurlmsg";
+        DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectField, null);
+        String FANWEI_URL = "";
+        if(commonParams.length > 0){
+            FANWEI_URL = commonParams[0].getString("nckd_ecologyurl");
+        } else {
+            return;
+        }
+
+        QFilter qFilter = new QFilter("nckd_logtype", QCP.in, new String[]{"1", "5"});
+        qFilter.and(new QFilter("nckd_sendstatus", QCP.equals, "fail"));
+
+        DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load(KEY_ENTITY_LOG, "id", new QFilter[]{qFilter});
+        logger.info("RefreshFanWeiMsgStatusTask:同步待办状态start");
+        if (dynamicObjects != null && dynamicObjects.length > 0) {
+            logger.info("RefreshFanWeiMsgStatusTask:共【" + dynamicObjects.length + "】条记录!");
+
+            List<DynamicObject> listObj = new ArrayList<>();
+            for(int i = 0; i < dynamicObjects.length; i++) {
+                String id = dynamicObjects[i].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, KEY_ENTITY_LOG);
+
+                JSONObject fanweiBodyData = JSONObject.parseObject(dynamicObject.getString("nckd_senddata_tag"));
+//                FANWEI_URL = dynamicObject.getString("nckd_sendurl");
+                String url = FANWEI_URL + "/rest/ofs/ReceiveRequestInfoByJson";
+                JSONObject result = doPostByHttpClient(url, fanweiBodyData);
+                if (result == null) {
+                    throw new KDException(WFErrorCode.httpRequestWrongResponse(), new Object[]{result != null ? result.toJSONString() : "result is null!"});
+                }
+
+                String sendStatus = "fail";
+                //失败
+                if ("1".equals(result.getString("operResult"))) {
+                    sendStatus = "success";
+                    logger.info("RefreshFanWeiMsgStatusTask:待办(" + dynamicObject.getString("number") + ")重发成功!");
+                } else {
+                    logger.info("RefreshFanWeiMsgStatusTask:待办(" + dynamicObject.getString("number") + ")重发失败" + result.getString("message"));
+                }
+
+                String response = result.toJSONString();
+                if (response.length() < 200) {
+                    dynamicObject.set("nckd_resultdata", response);
+                } else {
+                    dynamicObject.set("nckd_resultdata", response.substring(0, 200) + "...");
+                }
+                dynamicObject.set("nckd_resultdata_tag", response);
+                dynamicObject.set("nckd_sendstatus", sendStatus);
+                dynamicObject.set("nckd_sendurl", FANWEI_URL);
+
+                listObj.add(dynamicObject);
+
+                if(listObj.size() == 500) {
+                    SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+                    listObj.clear();
+                }
+            }
+            if(listObj.size() > 0) {
+                SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+            }
+        }
+        logger.info("RefreshFanWeiMsgStatusTask:同步待办状态end");
+    }
+
+    public static JSONObject doPostByHttpClient(String url, JSONObject bodyData) {
+        try {
+            Map<String, String> headers = new HashMap();
+            headers.put("Content-Type", "application/json; charset=utf-8");
+            headers.put("Accept", "application/json");
+            logger.info(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+            System.out.println(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+
+            String responseEntify = HttpUtils.postjson(url, headers, bodyData.toJSONString());
+            JSONObject result = (JSONObject)JSONObject.parse(responseEntify);
+            return result;
+        } catch (IOException var5) {
+            throw new KDException(var5, WFErrorCode.httpRequestException(), new Object[]{var5.getMessage()});
+        }
+    }
+}

+ 64 - 0
main/java/kd/cosmic/jkjt/msg/ecology/SSLClient.java

@@ -0,0 +1,64 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+public class SSLClient extends DefaultHttpClient
+{
+    public SSLClient()
+            throws Exception
+    {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        X509TrustManager tm = new X509TrustManager()
+        {
+            @Override
+            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
+                    throws CertificateException
+            {
+            }
+            @Override
+            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
+            {
+            }
+            @Override
+            public X509Certificate[] getAcceptedIssuers()
+            {
+                return null;
+            }
+        };
+        sslContext.init(null, new TrustManager[] { tm }, null);
+        SSLSocketFactory ssf = new SSLSocketFactory(sslContext, new X509HostnameVerifier()
+        {   @Override
+            public boolean verify(String s, SSLSession sslSession) {
+                return true;
+            }
+            @Override
+            public void verify(String host, SSLSocket ssl)
+                    throws IOException
+            {
+            }
+            @Override
+            public void verify(String host, X509Certificate cert)
+                    throws SSLException
+            {
+            }
+            @Override
+            public void verify(String host, String[] cns, String[] subjectAlts)
+                    throws SSLException
+            {
+            }
+        });
+        ClientConnectionManager ccm = getConnectionManager();
+        SchemeRegistry sr = ccm.getSchemeRegistry();
+        sr.register(new Scheme("https", 443, ssf));
+    }
+}

+ 13 - 0
main/java/kd/cosmic/jkjt/msg/ecology/TrustAnyHostnameVerifier.java

@@ -0,0 +1,13 @@
+package kd.cosmic.jkjt.msg.ecology;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+public class TrustAnyHostnameVerifier implements HostnameVerifier {
+
+    @Override
+    public boolean verify(String arg0, SSLSession arg1) {
+        // TODO Auto-generated method stub
+        return true;//访问域名更改,可使用IP访问
+    }
+
+}

+ 420 - 0
main/java/kd/cosmic/jkjt/msg/feishu/ChangeMessageStatePlugin.java

@@ -0,0 +1,420 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bec.api.IEventServicePlugin;
+import kd.bos.bec.model.EntityEvent;
+import kd.bos.bec.model.JsonEvent;
+import kd.bos.bec.model.KDBizEvent;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.engine.WfUtils;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import org.apache.commons.lang.time.DateFormatUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.rmi.ConnectException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/07/09
+ * 插件说明:业务事件中心插件,修改飞书待办状态,生产类
+ * 表单标识:消息阅读状态变更事件.执行插件(wf.AfterChangeMessageStateEvent.executePlugin)
+ */
+public class ChangeMessageStatePlugin implements IEventServicePlugin {
+    private static Log logger = LogFactory.getLog(ChangeMessageStatePlugin.class);
+    private static final String USER_FORM_ID = "bos_user";
+    private static final String USERNAME = "username";
+    private List<Integer> interErrCodeArr = Arrays.asList(new Integer[]{
+            65001,90203,90235,90242,91201,95001,95003,95005,95010,95011,
+            95203,95204,95205,95206,95207,95208,95209,
+            105001,190003,230020,1050002,1050008,1069301,1069399
+    });
+
+    public static final String createInstanceUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+    private static final String TRAGETNAME = "云苍穹移动审批";
+    private static final String SPDYNUMBER = "jxjkxh";//审批定义number
+    private static final String SPDYID = "E7DCCD77-4465-44F0-B99A-6504F0C433DC";//审批定义id-正式
+    private static final String APP_ID = "cli_a6b7577f9c1d500b";//"cli_a6f4afb94db0d00c";//正式
+    private static final String APP_SERCRET = "PYTelbYp8riFxcvvd3FkHZX0xTGV5R6h";//"fG1XpSMGjpLO3riRCGfMnclM7KRjRsq2";
+
+    @Override
+    public Object handleEvent(KDBizEvent evt) {
+        logger.info("飞书消息阅读状态变更事件" + evt.toString());
+
+        if (evt instanceof EntityEvent) {//苍穹事件
+            EntityEvent entityEvent = (EntityEvent) evt;//类型转换
+            String businesskey = entityEvent.getBusinesskeys().get(0);
+            String entityNumber = entityEvent.getEntityNumber();
+            DynamicObject obj = BusinessDataServiceHelper.loadSingle(businesskey, entityNumber);
+            Long evtID = evt.getEventId();
+            String source = evt.getSource();//传递的事件参数
+        } else {//自定义事件
+            JsonEvent jsonEvent = (JsonEvent) evt;//类型转换
+            String source = jsonEvent.getSource();//传递的事件参数
+            if (WfUtils.isNotEmpty(source)) {
+                JSONArray arr = (JSONArray) JSON.parse(source);
+                for (int i = 0; i < arr.size(); i++) {
+                    if(arr.getJSONObject(i) == null) {
+                        continue;
+                    }
+                    //消息id
+                    String msgIds = arr.getJSONObject(i).getString("msgIds");
+
+                    logger.info("飞书消息阅读状态变更事件:msgIds" + msgIds);
+
+                    //用户id
+                    String userId = arr.getJSONObject(i).getString("userId");
+
+                    if(StringUtils.isEmpty(msgIds)){
+                        continue;
+                    }
+
+                    updateFeiShuMsgState(userId, msgIds);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void updateFeiShuMsgState(String userId, String msgIds){
+        QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(userId));
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingle("bos_user", qFilter.toArray());
+        String personNo = user.getString("number");
+
+        msgIds = msgIds.replace("[", "").replace("]", "");
+        String[] msgIdList = msgIds.split(",");
+        if(msgIdList.length == 0){
+            return;
+        }
+
+        for(String msgId : msgIdList){
+            logger.info("飞书消息阅读状态变更事件:替换前 msgId:" + msgId);
+            msgId = msgId.replace("\"", "");
+            logger.info("飞书消息阅读状态变更事件:替换后msgId:" + msgId);
+
+            qFilter = new QFilter("channel", QCP.equals, "feishu");
+            qFilter.and(new QFilter("messageid", QCP.equals, Long.valueOf(msgId)));
+            DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+            if(msgFail == null){
+                continue;
+            }
+            qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgId));
+            DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+            if(msg == null || StringUtils.isEmpty(msg.getString("contenturl"))){
+                continue;
+            }
+
+            Long channelMsgId = msgFail.getLong("id");
+            String tplscene = msgFail.getString("tplscene");
+
+            logger.info("飞书消息阅读状态变更事件:tplscene:" + tplscene);
+
+            if("circulation".equals(tplscene)){
+                String token = getToken();
+
+                sendFlowMessage(channelMsgId.toString(), msg, user, token);
+            }
+
+
+        }
+    }
+
+    public void sendFlowMessage(String channelMsgId, DynamicObject message, DynamicObject userInfo, String token){
+        try {
+            JSONObject m = new JSONObject(true);
+            JSONObject c = new JSONObject(true);
+
+            c.put("approval_code", SPDYID);
+            c.put("instance_id","cq"+ channelMsgId);
+            c.put("status", "HIDDEN");
+
+            //url连接
+            JSONObject links = new JSONObject();
+
+            String pcUrl = StringUtils.isEmpty(message.getString("contenturl")) ? "" : message.getString("contenturl");
+            String mobUrl = StringUtils.isEmpty(message.getString("mobcontenturl")) ? pcUrl : message.getString("mobcontenturl");
+
+            logger.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+            if(StringUtils.isNotEmpty(pcUrl)){
+                pcUrl = pcUrl.replace(mobileUrl, curUrl);
+            }
+
+            if(StringUtils.isNotEmpty(mobUrl)){
+                mobUrl = mobUrl.replace(curUrl, mobileUrl);
+            }
+
+            if("".equals(pcUrl)||pcUrl==null){
+                pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+            }
+
+
+            links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+            c.put("links", links);
+
+            String content = message.getString("content");
+            String title = message.getString("title");
+            //标题
+            c.put("title", title);
+
+            Long createTime = (new Date()).getTime();
+
+            c.put("start_time", createTime);
+            c.put("update_time", createTime);
+            c.put("update_mode", "UPDATE");
+
+
+
+            /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+            String workflowname = "财务系统审批流";
+            String nodename = "";
+            String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            if(message != null && StringUtils.isNotEmpty(message.getString("config"))){
+                JSONObject messateContext = JSONObject.parseObject(message.getString("config")).getJSONObject("messageContext");
+                Long taskId = messateContext.getLong("taskId");
+                /*************处理消息格式*****************/
+                DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+                //add by wnaghaiwu_kd 2024/04/28
+                //如果任务中找不到,就找历史任务
+                if(taskInfo == null){
+                    taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+                }
+
+                if(taskInfo != null){
+                    QFilter qFilter;
+                    nodename = taskInfo.getString("name");
+
+                    if(taskInfo.getLong("processdefinitionid") > 0) {
+                        qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                        DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                        if (processdef != null) {
+                            workflowname = processdef.getString("name");
+                        }
+                    }
+
+                    if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                        workflowname = taskInfo.getString("entityname") + "审批流程";
+                    }
+                    if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                            && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                        qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                        DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                        if(bill != null){
+                            if(bill.getDynamicObjectType().getProperties().contains("createtime")) {
+                                createDate = DateFormatUtils.format(bill.getDate("createtime"), "yyyy-MM-dd HH:mm:ss");
+                            } else if(bill.getDynamicObjectType().getProperties().contains("createdate")) {
+                                createDate = DateFormatUtils.format(bill.getDate("createdate"), "yyyy-MM-dd HH:mm:ss");
+                            }
+                        }
+
+                    }
+                }
+            }
+
+            c.put("title", workflowname);
+
+            JSONArray formArr = new JSONArray();
+            JSONObject form1 = new JSONObject();
+            form1.put("name", "流程标题");
+            form1.put("value", StringUtils.isEmpty(message.getString("title")) ? workflowname : message.getString("title"));
+            formArr.add(form1);
+
+            JSONObject form2 = new JSONObject();
+            form2.put("name", "提单时间");
+            form2.put("value", createDate);
+            formArr.add(form2);
+
+            JSONObject form3 = new JSONObject();
+            form3.put("name", "当前节点");
+            form3.put("value", nodename);
+            formArr.add(form3);
+
+            c.put("form", formArr);
+
+            /*************处理消息格式*****************/
+
+            //cc_list	抄送人相关
+            JSONArray ccArr = new JSONArray();
+
+            JSONObject cc = new JSONObject();
+            cc.put("cc_id", userInfo.getPkValue()+"_"+ channelMsgId);
+            cc.put("open_id", getUserId(userInfo.getString("phone"),token));
+            cc.put("read_status", "READ");
+            cc.put("title", title);
+            cc.put("create_time", createTime);
+            cc.put("update_time", createTime);
+
+            cc.put("title", workflowname);
+
+            JSONObject links1 = new JSONObject();
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+            cc.put("links", links1);
+
+            ccArr.add(cc);
+
+            c.put("cc_list", ccArr);
+
+            m.put("content", c);
+
+            logger.info("财务系统飞书推送传阅状态更新:"+m.toJSONString());
+
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token);
+
+            logger.info("财务系统飞书推送传阅状态更新:"+response);
+        } catch (ConnectException e) {
+            logger.info("财务系统飞书,推送失败");
+            logger.error(e.getMessage(), e);
+            e.printStackTrace();
+        }
+    }
+
+    //pc链接特殊处理
+    public String pcSrcToFeishu(String src){
+        String dandianStr="";
+        if(dandianStr != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://applink.feishu.cn/client/web_url/open?mode=window&url=" + encodesrc;
+            } catch (UnsupportedEncodingException e) {
+                logger.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    public static String changeDanDianSrc(String src){
+        String dandianStr="";
+        if(src != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=" + encodesrc + "&app_id=" + APP_ID;
+            } catch (UnsupportedEncodingException e) {
+                logger.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+    /**
+     * 获取飞书token
+     * @return
+     */
+    public static String getToken(){
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        tokenJson.put("app_id",APP_ID);
+        tokenJson.put("app_secret",APP_SERCRET);
+        try {
+            String returnSt =  doPostByHttpClient(url,tokenJson.toJSONString(),false,null);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if("0".equals(returnJson.getString("code"))){//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (ConnectException e) {
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            logger.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+
+            logger.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    public static String doPostByHttpClient(String url, String data, boolean isBearer,String token) throws ConnectException {
+        System.out.println("url =" + url);
+        System.out.println("data =" + data);
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            DefaultHttpClient httpClient = null;
+            if (url.toLowerCase().startsWith("https://")) {
+                System.out.println("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                System.out.println("use nonssl");
+                httpClient = new DefaultHttpClient();
+            }
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if(isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+            String body = EntityUtils.toString(response.getEntity());
+            System.out.println(body);
+            return body;
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println(" ===== doPostByHttpClient() ERROR ===== ");
+            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+}

+ 245 - 0
main/java/kd/cosmic/jkjt/msg/feishu/DateUtil.java

@@ -0,0 +1,245 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DateUtil {
+    private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new ConcurrentHashMap();
+
+    public DateUtil() {
+    }
+
+    public static Date getFirstDayOfMonth(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(5, 1);
+        return calendar.getTime();
+    }
+
+    public static Date getCurMonthStartTime() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(calendar.get(1), calendar.get(2), 1, 0, 0, 0);
+        return calendar.getTime();
+    }
+
+    private static SimpleDateFormat getSdf(final String pattern) {
+        ThreadLocal<SimpleDateFormat> tl = (ThreadLocal)sdfMap.get(pattern);
+        if (tl == null) {
+            tl = new ThreadLocal<SimpleDateFormat>() {
+                protected SimpleDateFormat initialValue() {
+                    return new SimpleDateFormat(pattern);
+                }
+            };
+            sdfMap.put(pattern, tl);
+        }
+
+        return (SimpleDateFormat)tl.get();
+    }
+
+    public static String format(Object date, String pattern) {
+        return getSdf(pattern).format(date);
+    }
+
+    public static Date parse(String dateStr, String pattern) throws ParseException {
+        return getSdf(pattern).parse(dateStr);
+    }
+
+    public static Date getFirstDayOfYear(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.set(6, 1);
+        return calendar.getTime();
+    }
+
+    public static Date getFirstDayOfWeek(Date date) {
+        Calendar c = new GregorianCalendar();
+        c.setFirstDayOfWeek(2);
+        c.setTime(date);
+        c.set(7, c.getFirstDayOfWeek());
+        c.set(11, 0);
+        c.set(12, 0);
+        c.set(13, 0);
+        c.set(14, 0);
+        return c.getTime();
+    }
+
+    public static int getYear(Date d) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(d);
+        int year = cal.get(1);
+        int era = cal.get(0);
+        return era == 0 ? -1 * year : year;
+    }
+
+    public static int getMonth(Date d) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(d);
+        return cal.get(2) + 1;
+    }
+
+    public static int getDay(Date d) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(d);
+        return cal.get(5);
+    }
+
+    public static long dateDiff(String interval, Date dDate1, Date dDate2) {
+        int desiredField = 0;
+        int coef = 1;
+        Date date1;
+        Date date2;
+        if (dDate1.getTime() > dDate2.getTime()) {
+            coef = -1;
+            date1 = dDate2;
+            date2 = dDate1;
+        } else {
+            date1 = dDate1;
+            date2 = dDate2;
+        }
+
+        byte field;
+        if (interval.equals("yyyy")) {
+            field = 1;
+        } else if (interval.equals("m")) {
+            field = 2;
+        } else if (interval.equals("d")) {
+            field = 5;
+        } else if (interval.equals("y")) {
+            field = 5;
+        } else if (interval.equals("w")) {
+            field = 4;
+        } else if (interval.equals("ww")) {
+            field = 3;
+        } else if (interval.equals("h")) {
+            field = 5;
+            desiredField = 11;
+        } else if (interval.equals("n")) {
+            field = 5;
+            desiredField = 12;
+        } else {
+            if (!interval.equals("s")) {
+                return -1L;
+            }
+
+            field = 5;
+            desiredField = 13;
+        }
+
+        Calendar calTmp = Calendar.getInstance();
+        calTmp.setTime(date1);
+        long nbOccurence = 0L;
+        calTmp.add(field, 1);
+
+        Date dateTemp;
+        for(dateTemp = calTmp.getTime(); dateTemp.getTime() <= date2.getTime(); ++nbOccurence) {
+            calTmp.add(field, 1);
+            dateTemp = calTmp.getTime();
+        }
+
+        if (desiredField == 11 || desiredField == 12 || desiredField == 13) {
+            calTmp.setTime(date1);
+            calTmp.add(field, (int)nbOccurence);
+            dateTemp = calTmp.getTime();
+            switch (desiredField) {
+                case 11:
+                    nbOccurence *= 24L;
+                    break;
+                case 12:
+                    nbOccurence = nbOccurence * 24L * 60L;
+                    break;
+                case 13:
+                    nbOccurence = nbOccurence * 24L * 60L * 60L;
+            }
+
+            calTmp.add(desiredField, 1);
+
+            for(dateTemp = calTmp.getTime(); dateTemp.getTime() <= date2.getTime(); ++nbOccurence) {
+                calTmp.add(desiredField, 1);
+                dateTemp = calTmp.getTime();
+            }
+        }
+
+        return nbOccurence * (long)coef;
+    }
+
+    public static String getNumberDateStr(String dateStr) {
+        String[] date = dateStr.split("/");
+        String month = date[1];
+        String day = "";
+        if (date.length > 2) {
+            day = date[2];
+        }
+
+        if (Integer.parseInt(month) < 10) {
+            month = "0" + month;
+        }
+
+        if (!"".equals(day) && Integer.parseInt(day) < 10) {
+            day = "0" + day;
+        }
+
+        return date[0] + month + day;
+    }
+
+    public static Date addDay(Date dDate, long iNbDay) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(dDate);
+        cal.add(5, (int)iNbDay);
+        Date result = cal.getTime();
+        return result;
+    }
+
+    public static Date addMonth(Date dDate, int iNbMonth) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(dDate);
+        int month = cal.get(2);
+        month += iNbMonth;
+        int year = month / 12;
+        month %= 12;
+        cal.set(2, month);
+        if (year != 0) {
+            int oldYear = cal.get(1);
+            cal.set(1, year + oldYear);
+        }
+
+        return cal.getTime();
+    }
+
+    public static Date getCurDateForm(Date date, boolean isStart) throws ParseException {
+        SimpleDateFormat shortformat = new SimpleDateFormat("yyyy-MM-dd");
+        SimpleDateFormat longformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String dateStr = shortformat.format(date);
+        String suffix = " 00:00:00";
+        if (!isStart) {
+            suffix = " 23:59:59";
+        }
+
+        return longformat.parse(dateStr + suffix);
+    }
+
+    public static String taskefficiencyFormMart(BigDecimal taskefficiency) {
+        StringBuilder sb = new StringBuilder();
+        BigDecimal coefficient = new BigDecimal(60);
+        int hour = taskefficiency.intValue();
+        sb.append(hour > 9 ? hour : "0" + hour).append(" : ");
+        BigDecimal taskMinute = taskefficiency.subtract(new BigDecimal(hour)).multiply(coefficient);
+        int minute = taskMinute.intValue();
+        sb.append(minute > 9 ? minute : "0" + minute).append(" : ");
+        BigDecimal taskSecond = taskMinute.subtract(new BigDecimal(minute)).multiply(coefficient);
+        int second = taskSecond.intValue();
+        sb.append(second > 9 ? second : "0" + second);
+        return sb.toString();
+    }
+
+    public static int convertIntFormate(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        return calendar.get(1) * 10000 + (calendar.get(2) + 1) * 100 + calendar.get(5);
+    }
+}

+ 450 - 0
main/java/kd/cosmic/jkjt/msg/feishu/DeleteFeiShuPlugin.java

@@ -0,0 +1,450 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.db.DB;
+import kd.bos.db.DBRoute;
+import kd.bos.exception.KDException;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.rmi.ConnectException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 正式环境类
+ * 删除飞书待办插件新版本
+ */
+public class DeleteFeiShuPlugin extends AbstractFormPlugin {
+    private static final Logger log = LoggerFactory.getLogger(DeleteFeiShuPlugin.class);
+
+    private static String createInstanceUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+    private static final String SPDYID = "E7DCCD77-4465-44F0-B99A-6504F0C433DC";//审批定义id
+    private static final String APP_ID = "cli_a6b7577f9c1d500b";//"cli_a6f4afb94db0d00c";//正式
+    private static final String APP_SERCRET = "PYTelbYp8riFxcvvd3FkHZX0xTGV5R6h";//"fG1XpSMGjpLO3riRCGfMnclM7KRjRsq2";//正式
+    private static final String ACCOUNTID = "1647272243664781312";
+
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+        FormOperate formOperate = (FormOperate) afterDoOperationEventArgs.getSource();
+        String operateKey = formOperate.getOperateKey();
+        if ("deletedaiban".equals(operateKey)) {
+            //获得用户
+            DynamicObject userInfo = (DynamicObject) this.getModel().getValue("nckd_userfield");
+            if (userInfo == null) {
+                this.getView().showMessage("请输入用户");
+                return;
+            }
+            //获得飞书token
+            String token = FeishuUtil.getToken(APP_ID, APP_SERCRET);
+            //调用接口获得用户的所有待办
+            List<String> formidmsgList = getUserDaiBan(userInfo.getString("username"), token);
+//            List<String> formidmsgList = getUserDaiBan("liangfan01",token);
+
+            if (formidmsgList == null) {
+                this.getView().showMessage("该用户oa号在飞书没有找到,请检查");
+                return;
+            } else {
+                int count = 0;
+                String taskidstr = "";
+                for (int i = 0; i < formidmsgList.size(); i++) {
+                    String instanseId = formidmsgList.get(i);
+                    if (instanseId.startsWith("cq")){
+                        instanseId = instanseId.substring(2);
+                    }
+                    //直接删除
+                    //直接删除
+                    if (StringUtils.isNotEmpty(instanseId)) {
+                        String taskId = instanseId;
+                        //判断任务taskId在苍穹存不存在
+                        DynamicObject taskInfo = QueryServiceHelper.queryOne("wf_hitaskinst", "handlestate", new QFilter[]{
+                                new QFilter("id", QFilter.equals, taskId)
+                        });
+                        if (taskInfo == null){
+                            // 查询流程实例状态
+                            taskInfo = QueryServiceHelper.queryOne("wf_task", "processinstanceid", new QFilter[]{
+                                    new QFilter("processinstanceid", QFilter.equals, taskId),
+                                    new QFilter("handlestate", QFilter.in, Arrays.asList("willApproval", "willHandled"))
+                            });
+                            if (taskInfo == null){
+                                // 不存在记录执行删除
+                                deleteDaiBanByProcInstId(taskId, token, count);
+                            }else {
+                                // 存在,判断当前审批人是否一致
+                                DynamicObject dynamicObject = QueryServiceHelper.queryOne("wf_hiparticipant", "*", new QFilter[]{
+                                        new QFilter("taskid", QFilter.equals, taskInfo.getPkValue()),
+                                        new QFilter("userid", QFilter.equals, userInfo.getPkValue())
+                                });
+                                if (dynamicObject == null){
+                                    // 不存在则执行删除
+                                    deleteDaiBanByProcInstId(taskId, token, count);
+                                }
+                            }
+                        }else {
+                            // 存在判断状态
+                            String handlestate = taskInfo.getString("handlestate");
+                            if ("willApproval".equalsIgnoreCase(handlestate) || "willHandled".equalsIgnoreCase(handlestate)){
+                                // 待办状态,不处理
+                                // 如果在 t_wf_task 表不存在同样删除
+                                taskInfo = QueryServiceHelper.queryOne("wf_task", "processinstanceid", new QFilter[]{
+                                        new QFilter("id", QFilter.equals, taskId),
+                                        new QFilter("handlestate", QFilter.in, Arrays.asList("willApproval", "willHandled"))
+                                });
+                                if (taskInfo == null){
+                                    deleteDaiBan(taskId, token);
+                                }
+                            }else {
+                                // 删除任务
+                                deleteDaiBan(taskId, token);
+                            }
+                        }
+                    }
+                }
+                this.getView().showMessage("找到" + formidmsgList.size() + "条,成功删除" + count + "条");
+
+            }
+        } else if ("adddaiban".equals(operateKey)) {
+            //获得用户
+            DynamicObject userInfo = (DynamicObject) this.getModel().getValue("nckd_userfield");
+            if (userInfo == null) {
+                this.getView().showMessage("请输入用户");
+                return;
+            }
+            //  获取对应的人员的任务
+            DynamicObject[] hiparticipantList = BusinessDataServiceHelper.load("wf_hiparticipant", "processinstanceid, taskid, userid", new QFilter[]{
+                    new QFilter("userid", QCP.equals, userInfo.getString("id")),
+                    new QFilter("taskid", QFilter.large_than, 0),
+                    new QFilter("endtime", QFilter.is_notnull, null)
+            });
+            if (!ObjectUtils.isEmpty(hiparticipantList)) {
+                for (DynamicObject obj : hiparticipantList) {
+                    long taskId = obj.getLong("taskid");
+                    long processInstId = obj.getLong("processinstanceid");
+                    QFilter hiactinstFilter = new QFilter("taskid", QCP.equals, taskId);
+                    DynamicObject[] hiactinstList = BusinessDataServiceHelper.load("wf_hiactinst", "taskid", hiactinstFilter.toArray());
+                    if (ObjectUtils.isEmpty(hiactinstList)) {
+                        continue;
+                    }
+                    DynamicObject hiactinstObj = hiactinstList[0];
+                    Long activityId = hiactinstObj.getLong("id");
+                    QFilter executionFilter = new QFilter("currenttaskid", QCP.equals, taskId);
+                    DynamicObject[] executionList = BusinessDataServiceHelper.load("wf_execution", " currenttaskid, businesskey, subject", executionFilter.toArray());
+                    if (ObjectUtils.isEmpty(executionList)) {
+                        continue;
+                    }
+                    DynamicObject executionObj = executionList[0];
+                    Long businessKey = executionObj.getLong("businesskey");
+                    String subject = executionObj.getString("subject");
+
+                    String redirectUrl = System.getProperty("domain.contextUrl") + "/mobile.html?form=wf_approvalpagemobile_bac&assigntype=participant&taskId=%s&billformid=wf_approvalpagemobile_bac&billid=%s&type=toHandle&modelType=MobileBillFormModel&cometype=next";
+                    redirectUrl = String.format(redirectUrl, taskId, businessKey);
+                    String url = redirectUrl;
+                    String token = FeishuUtil.getToken(APP_ID, APP_SERCRET);
+                    // long processInstId, Long taskId, Long activityId, Long businessKey, String subject, String url, String token
+                    JSONObject m = getToDaiBanJson(processInstId, taskId, activityId, businessKey, subject, url, token);
+                    if (m == null) {
+                        continue;
+                    }
+                    try {
+                        log.info("财务系统飞书推送待办:" + m.toJSONString());
+                        String response = FeiShuDaiBanServiceHandler.doPostByHttpClient(FeiShuDaiBanServiceHandler.createInstanceUrl, m.toJSONString(), true, token, 3);
+                        log.info("财务系统飞书推送待办结果:" + response);
+                        JSONObject instanceRes = JSONObject.parseObject(response);
+                    } catch (Exception e) {
+                        log.info("财务系统飞书,推送失败");
+                        log.error(e.getMessage(), e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    private void deleteDaiBanByProcInstId(String processInstId, String token, int num) {
+        // TODO 重新组装待办json 需判断流程状态
+        // 是否已归档
+        // 不在流程执行表
+        QFilter[] filters = new QFilter[]{
+                new QFilter("processinstanceid", QFilter.equals, processInstId),
+                new QFilter("endtime", QFilter.is_notnull, null),
+                new QFilter("endactivityid", QFilter.is_notnull, null)
+        };
+        long businessKey = 0L;
+        String status = "PENDING";
+        long starterId = 0L;
+        DynamicObject[] histList = BusinessDataServiceHelper.load("wf_hiprocinst", "processinstanceid,billno,startuserid,businesskey", filters);
+        if (histList != null && histList.length > 0){
+            // 已归档
+            status = "APPROVED";
+            starterId = histList[0].getLong("startuserid");
+            businessKey = histList[0].getLong("businesskey");
+        }
+        DynamicObject taskInfo = QueryServiceHelper.queryOne("wf_task", "processinstanceid,subject,entityname,starterid,businesskey", new QFilter[]{
+                new QFilter("processinstanceid", QFilter.equals, processInstId),
+                new QFilter("handlestate", QFilter.in, Arrays.asList("willApproval", "willHandled"))
+        });
+        if (taskInfo == null){
+//            status = "CANCELED";
+        }else {
+            starterId = taskInfo.getLong("starterid");
+            businessKey = taskInfo.getLong("businesskey");
+        }
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id", "cq" + processInstId);
+        c.put("update_time", System.currentTimeMillis());
+        c.put("update_mode", "UPDATE");
+        c.put("status", status);
+        c.put("start_time", System.currentTimeMillis());
+        String title = "";
+        if (taskInfo != null){
+            title = taskInfo.getString("entityname") + ":" + taskInfo.getString("subject");
+        }else {
+            title = "来自ERP待办";
+        }
+        c.put("title", title);
+
+        if (starterId > 0) {
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingle(starterId, "bos_user");
+            c.put("open_id", FeishuUtil.getUserId(faqiren.getString("phone"), token));
+        }
+        // taskList
+        JSONArray taskArr = getTaskListByProcInstId(Long.valueOf(processInstId), businessKey + "", title, status);
+        c.put("task_list", taskArr);
+        m.put("content", c);
+        log.info("财务系统飞书推送待办:" + m.toJSONString());
+        try {
+            String response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 1);
+            log.info("财务系统飞书推送待办结果:" + response);
+        }catch (ConnectException e){
+            log.error("财务系统飞书推送待办异常:" + e.getMessage(), e);
+            e.printStackTrace();
+            throw new KDException(e.getMessage()   );
+        }
+
+    }
+
+    /**
+     * 拼接taskList
+     * @param processInstanceId
+     * @param businessKey
+     * @param title
+     * @return
+     */
+    private JSONArray getTaskListByProcInstId(Long processInstanceId, String businessKey, String title, String passStatus){
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        StringBuilder sql = new StringBuilder();
+        sql.append("/*dialect*/ select tp.fuserid,tp.fusername,ht.fid as activityId,t.* from t_wf_hitaskinst t ")
+                .append("left join t_wf_hiparticipant tp on t.fprocinstid=tp.fprocinstid and t.fid=tp.ftaskid ")
+                .append("left join T_WF_HIACTINST ht on ht.ftaskid=t.fid ")
+                .append("where t.fprocinstid=?");
+        DataSet dataSet = DB.queryDataSet("msg", DBRoute.basedata, sql.toString(), new Object[]{processInstanceId});
+        if (dataSet == null){
+            return taskArr;
+        }
+        // pc 端 url
+        String contextUrl = System.getProperty("domain.contextUrl");
+        String pcUrl = contextUrl + "/integration/yzjShareOpen.do?formId=wf_approvalpage_bac&mb_formId=wf_approvalpagemobile_bac" +
+                "&pkId=%s&src=wf&accountId=" + ACCOUNTID + "&activityId=%s&tId=%s";
+        // mobile url
+        String redirectUrl = contextUrl + "/mobile.html?form=wf_approvalpagemobile_bac&assigntype=participant&taskId=%s" +
+                "&billformid=wf_approvalpagemobile_bac&billid=%s&type=toHandle&modelType=MobileBillFormModel&cometype=next";
+        for (Row row : dataSet){
+            // 遍历数据行  拼接taskList
+            JSONObject task = new JSONObject();
+
+            Long userId = row.getLong("fuserid");
+            task.put("task_id", userId.longValue() + "_" + row.getLong("fid"));
+            if (userId != null && userId.longValue() > 0){
+                DynamicObject bosUser = BusinessDataServiceHelper.loadSingle("bos_user", "username", new QFilter[]{new QFilter("id", QFilter.equals, userId)});
+                if (bosUser != null){
+                    task.put("user_id", bosUser.getString("username"));
+                }
+            }
+
+            JSONObject links = new JSONObject();
+            String handleState = row.getString("fhandlestate");
+            if ("willApproval".equalsIgnoreCase(handleState) || "willHandled".equalsIgnoreCase(handleState)){
+                // 如果在 wf_task不存在则跳过
+                DynamicObject dynamicObject = QueryServiceHelper.queryOne("wf_task", "processinstanceid", new QFilter[]{
+                        new QFilter("id", QFilter.equals, row.getLong("fid")),
+                        new QFilter("handlestate", QFilter.in, Arrays.asList("willApproval", "willHandled"))
+                });
+                if (dynamicObject == null){
+                    continue;
+                }
+            }
+            String status = FeishuUtil.getFeishuTaskStateByTaskHandleState(handleState);
+            if ("PENDING".equalsIgnoreCase(status) && ("CANCELED".equalsIgnoreCase(passStatus) || "DELETED".equalsIgnoreCase(passStatus))){
+                status = "DONE";
+            }
+            String currPCUrl = String.format(pcUrl, businessKey, row.getLong("activityId"), row.getLong("fid"));
+            links.put("pc_link", FeishuUtil.changeDanDianSrc(currPCUrl + "&apptype=feishu", APP_ID));
+            String url = String.format(redirectUrl, row.getLong("fid"), businessKey);
+            links.put("mobile_link", FeishuUtil.changeDanDianSrc(url + "&apptype=feishu", APP_ID));
+
+            task.put("links", links);
+            task.put("status", status);
+            task.put("title", title);
+            task.put("create_time", row.getDate("fcreatedate").getTime());
+            if (row.getDate("fendtime") != null){
+                task.put("end_time", row.getDate("fendtime").getTime());
+            }else {
+                task.put("end_time", System.currentTimeMillis());
+            }
+            task.put("update_time", System.currentTimeMillis());
+            taskArr.add(task);
+        }
+        return taskArr;
+    }
+
+    /**
+     * 删除待办
+     */
+    public static void deleteDaiBan(String taskId, String token) {
+        List<String> returnidArray = new ArrayList<String>();
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id", "cq" + taskId);
+        c.put("status", "APPROVED");
+        Date d = new Date();
+        c.put("start_time", d.getTime());
+        c.put("update_time", d.getTime());
+        c.put("update_mode", "REPLACED");
+        m.put("content", c);
+
+        String userIdUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+        try {
+            log.info("删除待办json: " + m.toString());
+            String response = FeiShuDaiBanServiceHandler.doPostByHttpClient(userIdUrl, m.toJSONString(), true, token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("删除待办json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * 根据用户oa号获得所有的待办
+     */
+    public static List<String> getUserDaiBan(String oaNumber, String token) {
+        List<String> returnidArray = new ArrayList<String>();
+        JSONObject m = new JSONObject();
+        JSONArray approval_codes = new JSONArray();
+        approval_codes.add(SPDYID);
+        m.put("approval_codes", approval_codes);
+
+        JSONArray user_ids = new JSONArray();
+        user_ids.add(oaNumber);
+        m.put("user_ids", user_ids);
+
+        m.put("status", "PENDING");
+
+        String userIdUrl = "https://www.feishu.cn/approval/openapi/v2/external/list";
+        try {
+            log.info("获取用户待办json: " + m.toString());
+            String response = FeiShuDaiBanServiceHandler.doPostByHttpClient(userIdUrl, m.toJSONString(), true, token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+
+            if ("success".equals(userRes.getString("msg"))) {
+                JSONArray datas = userRes.getJSONObject("data").getJSONArray("data");
+                for (int i = 0; i < datas.size(); i++) {
+                    String formidmsg = datas.getJSONObject(i).getString("instance_id");
+                    returnidArray.add(formidmsg);
+                }
+            } else {
+                return null;
+            }
+            log.info("获取用户待办json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnidArray;
+    }
+
+    /**
+     * 获取创建审批实例json
+     *
+     * @return
+     */
+    /**
+     * 获取创建审批实例json
+     *
+     * @return
+     */
+    public JSONObject getToDaiBanJson(long processInstId, Long taskId, Long activityId, Long businessKey, String subject, String url, String token) {
+        boolean isExist = QueryServiceHelper.exists("wf_task", taskId);
+        if (!isExist) {
+            return null;
+        }
+        DynamicObject taskInfo = BusinessDataServiceHelper.loadSingle(taskId, "wf_task");
+
+
+        JSONObject m = new JSONObject(true);
+
+
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id", "cq" + processInstId);
+        c.put("status", "PENDING");
+
+
+        //url连接
+        JSONObject links = new JSONObject();
+        String contextUrl = System.getProperty("domain.contextUrl");
+        String pcUrl = contextUrl + "/integration/yzjShareOpen.do?formId=wf_approvalpage_bac&mb_formId=wf_approvalpagemobile_bac&pkId=%s&src=wf&accountId=" + ACCOUNTID + "&activityId=%s&tId=%s";
+        pcUrl = String.format(pcUrl, businessKey, activityId, taskId);
+        links.put("pc_link", FeishuUtil.changeDanDianSrc(pcUrl + "&apptype=feishu", APP_ID));
+        links.put("mobile_link", FeishuUtil.changeDanDianSrc(url + "&apptype=feishu", APP_ID));
+        c.put("links", links);
+
+        //标题
+        c.put("title", subject);
+        //发起人
+        Long faqirenid = taskInfo.getLong("starterid");
+        if (faqirenid != null) {
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingle(faqirenid, "bos_user");
+            c.put("open_id", FeishuUtil.getUserId(faqiren.getString("phone"), token));
+
+        }
+
+        c.put("start_time", ((Timestamp) taskInfo.get("createdate")).getTime());
+        c.put("update_time", ((Timestamp) taskInfo.get("createdate")).getTime());
+        c.put("update_mode", "REPLACED");
+
+        //task_list	审批人相关
+        JSONArray taskArr = getTaskListByProcInstId(processInstId, businessKey.toString(), subject, "PENDING");
+        c.put("task_list", taskArr);
+        m.put("content", c);
+        return m;
+    }
+}

+ 100 - 0
main/java/kd/cosmic/jkjt/msg/feishu/FeiShuAuthtication.java

@@ -0,0 +1,100 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import kd.bos.dc.api.model.Account;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.login.thirdauth.app.AppAuthResult;
+import kd.bos.login.thirdauth.app.ThirdAppAuthtication;
+import kd.bos.login.thirdauth.app.UserType;
+import kd.bos.login.thirdauth.app.feishu.FeiShuUtils;
+import kd.bos.login.utils.JSONUtils;
+import kd.bos.login.utils.ParametersUtils;
+import kd.bos.util.StringUtils;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FeiShuAuthtication extends ThirdAppAuthtication {
+    private static Log logger = LogFactory.getLog(kd.bos.login.thirdauth.app.feishu.FeiShuAuthtication.class);
+
+    public FeiShuAuthtication() {
+    }
+
+    public boolean isNeedHandle(HttpServletRequest request, Account currentCenter) {
+        logger.error("feishutest:1111 !");
+
+        String app_type = request.getParameter("apptype");
+        String code = request.getParameter("code");
+
+
+        logger.error("feishutest:1111 !" + (app_type == null ? "" : app_type) + (code == null ? "" : code));
+        return "feishu".equalsIgnoreCase(app_type);
+    }
+
+    public AppAuthResult appAuthtication(HttpServletRequest request, Account currentCenter) {
+        logger.error("feishutest:2222 !");
+        AppAuthResult result = new AppAuthResult();
+        result.setSucceed(false);
+        String code = request.getParameter("code");
+
+        logger.error("feishutest:2222 ! code = " + code);
+
+        if (StringUtils.isEmpty(code)) {
+            result.setErrorMessage("feishu:code is null");
+            return result;
+        } else {
+            Map<String, Object> parameters = ParametersUtils.getCommonParameters(currentCenter);
+            if (null == parameters) {
+                result.setErrorMessage("feishu:parameters is null");
+                return result;
+            } else {
+                if (null == parameters.get("feishu_api_url") || "".equals(parameters.get("feishu_api_url"))) {
+                    parameters.put("feishu_api_url", FeiShuUtils.getFlyUrl(currentCenter));
+                }
+
+                String appId = request.getParameter("flyBookAppId");
+                if (StringUtils.isNotEmpty(appId)) {
+                    parameters.put("flyBookAppId", appId);
+                }
+
+                String appAccessToken = FeiShuUtils.getAppAccessToken(parameters, currentCenter);
+                if (StringUtils.isEmpty(appAccessToken)) {
+                    logger.error("feishu:appAccessToken is null !");
+                    return result;
+                } else {
+                    logger.error("feishutest:2222 ! appAccessToken = " + appAccessToken);
+                    logger.error("feishutest:2222 ! parameters = " + parameters.toString());
+
+                    Map<String, Object> authUserInfo = FeiShuUtils.getAuthUserInfo(parameters, appAccessToken, code);
+                    logger.error("feishutest:2222 ! authUserInfo = " + authUserInfo == null ? "未获取" : authUserInfo.toString());
+
+                    if (authUserInfo != null && authUserInfo.size() > 0) {
+                        logger.error("feishutest:2222 ! authUserInfo = " + authUserInfo.toString());
+
+                        if ("0".equals(authUserInfo.get("code").toString())) {
+                            try {
+                                Map<String, Object> data = (Map) JSONUtils.cast(authUserInfo.get("data").toString(), HashMap.class);
+                                Object mobile = data.get("mobile");
+
+                                if (null != mobile && !"".equals(mobile)) {
+                                    result.setUserType(UserType.MOBILE_PHONE);
+                                    result.setSucceed(true);
+                                    result.setUserFlag(FeiShuUtils.getMobile(mobile.toString()));
+                                }
+                            } catch (IOException var11) {
+                                logger.error("feishu:", var11);
+                            }
+                        }
+
+                        result.setAppType(request.getParameter("apptype"));
+                        return result;
+                    } else {
+                        logger.error("feishu:authUserInfo is null !");
+                        return result;
+                    }
+                }
+            }
+        }
+    }
+}

+ 1920 - 0
main/java/kd/cosmic/jkjt/msg/feishu/FeiShuDaiBanServiceHandler.java

@@ -0,0 +1,1920 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.dataentity.metadata.clr.DataEntityPropertyCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.BasedataEntityType;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.workflow.WorkflowServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.component.approvalrecord.IApprovalRecordItem;
+import kd.bos.workflow.engine.WfUtils;
+import kd.bos.workflow.engine.impl.persistence.entity.task.component.ApprovalRecordGroup;
+import kd.bos.workflow.engine.msg.AbstractMessageServiceHandler;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.bos.workflow.engine.msg.info.MessageInfo;
+import kd.bos.workflow.engine.msg.info.ToDoInfo;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.rmi.ConnectException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * 正式环境类
+ * 插件说明:飞书待办类
+ */
+public class FeiShuDaiBanServiceHandler extends AbstractMessageServiceHandler {
+
+    public FeiShuDaiBanServiceHandler() {
+
+    }
+
+    private static final Logger log = LoggerFactory.getLogger(FeiShuDaiBanServiceHandler.class);
+
+    /**
+     * 人员表标识
+     */
+    private static final String USER_FORM_ID = "bos_user";
+    /**
+     * 用户名
+     */
+    private static final String USERNAME = "username";
+
+    private List<Integer> interErrCodeArr = Arrays.asList(new Integer[]{
+            65001,90203,90235,90242,91201,95001,95003,95005,95010,95011,
+            95203,95204,95205,95206,95207,95208,95209,
+            105001,190003,230020,1050002,1050008,1069301,1069399
+    });
+
+    public static final String createInstanceUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+    private static final String TRAGETNAME = "云苍穹移动审批";
+    private static final String SPDYNUMBER = "jxjkxh";//审批定义number
+    private static final String SPDYID = "E7DCCD77-4465-44F0-B99A-6504F0C433DC";//审批定义id-正式
+    private static final String APP_ID = "cli_a6b7577f9c1d500b";//"cli_a6f4afb94db0d00c";//正式
+    private static final String APP_SERCRET = "PYTelbYp8riFxcvvd3FkHZX0xTGV5R6h";//"fG1XpSMGjpLO3riRCGfMnclM7KRjRsq2";
+
+
+    public static String doPostByHttpClient(String url, String data, boolean isBearer,String token, int times) throws ConnectException {
+        System.out.println("url =" + url);
+        System.out.println("data =" + data);
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            DefaultHttpClient httpClient = null;
+            if (url.toLowerCase().startsWith("https://")) {
+                System.out.println("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                System.out.println("use nonssl");
+                httpClient = new DefaultHttpClient();
+            }
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if(isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+            String body = EntityUtils.toString(response.getEntity());
+            System.out.println(body);
+            return body;
+        } catch (Exception e) {
+            String errMsg = e.getMessage() == null ? "未知异常" : e.getMessage();
+            log.error("请求url " + url + " 超时", errMsg);
+            // 执行重试
+            if (times > 0){
+                times--;
+                if (times > 3){
+                    times = 3;
+                }
+                return doPostByHttpClient(url, data, isBearer, token, times);
+            }else {
+                throw new ConnectException("请求飞书接口失败:" + errMsg);
+            }
+
+//            e.printStackTrace();
+//            System.out.println(" ===== doPostByHttpClient() ERROR ===== ");
+//            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+
+    /**
+     * 获取飞书token
+     * @return
+     */
+    public static String getToken(){
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        tokenJson.put("app_id",APP_ID);
+        tokenJson.put("app_secret",APP_SERCRET);
+        try {
+            String returnSt =  doPostByHttpClient(url,tokenJson.toJSONString(),false,null, 3);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if("0".equals(returnJson.getString("code"))){//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserIdTrue(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+
+    @Override
+    public void createToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+        log.info("财务系统飞书,推送待办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+
+
+        List<Long> userIds = toDoInfo.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            log.info("财务系统飞书,人员为空,推送失败");
+            return;
+        }
+
+        QFilter qFilter = new QFilter("id", QCP.in, userIds);
+        DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+        String token = getToken();
+
+
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+        String status="PENDING";
+        String fixName= "";
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        if("willApproval".equals(taskInfo.getString("handlestate"))){//待审批
+
+        }else if("dismissed".equals(taskInfo.getString("handlestate"))){//驳回
+            status="REJECTED";
+            //驳回新增的待办。不新增。特殊处理
+            List allApprovalRecord = WorkflowServiceHelper.getAllApprovalRecord(messageContext.getBusinessKey());
+            if(allApprovalRecord.size()>2){
+                IApprovalRecordItem item = ((ApprovalRecordGroup) allApprovalRecord.get(allApprovalRecord.size()-2)).getChildren().get(0);
+
+                String taskid = item.getTaskId();
+                String isreject = item.getDecisionType();
+                String submitMsg = item.getMessage();
+
+                if("reject".equals(isreject)){
+                    fixName = "(被驳回)";
+                    String content =toDoInfo.getContent();
+                    JSONObject m = getBoHuiJson(messageContext,toDoInfo,load,token,true,status,content,taskid);
+                    try {
+
+                        log.info("财务系统飞书驳回待办:"+m.toJSONString());
+                        String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+                        log.info("财务系统飞书驳回待办结果:"+response);
+                        JSONObject instanceRes = JSONObject.parseObject(response);
+
+
+
+                        //发送消息
+                        for (int i = 0; i < load.length ; i++) {
+                            String userid =  getUserIdTrue(load[i].getString("phone"),token);
+                            String title = toDoInfo.getTitle();
+                            String node = " ";
+                            if(messageContext.getEntityName()!=null&&!"".equals(messageContext.getEntityName())){
+                                int firstindex = content.indexOf(messageContext.getEntityName());
+                                if(firstindex>=0){
+                                    int subIndex = firstindex+messageContext.getEntityName().length();
+//                                    content =content.substring(0,subIndex)+"\n"+content.substring(subIndex);
+                                    content =content.substring(subIndex);
+                                    if("".equals(content)){
+                                        content =toDoInfo.getContent();
+                                    }
+                                }
+                            }
+                            sendFeishuMessage("1012", submitMsg, toDoInfo.getTaskId(), userid,title,content,node,token,changeDanDianSrc(pcUrl+"&apptype=feishu"),changeDanDianSrc( mobUrl+"&apptype=feishu"));
+                        }
+
+                    } catch (ConnectException e) {
+                        log.info("财务系统飞书,推送失败");
+                        log.error(e.getMessage(), e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+//            return ;
+        }
+
+        JSONObject m = getToDaiBanJson(messageContext,toDoInfo,load,token,false,"PENDING",null, fixName);
+        try {
+
+            log.info("财务系统飞书推送待办:"+m.toJSONString());
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书推送待办结果:"+response);
+            JSONObject instanceRes = JSONObject.parseObject(response);
+
+            if("".equals(fixName)) {
+                //发送消息
+                for (int i = 0; i < load.length; i++) {
+                    String userid = getUserIdTrue(load[i].getString("phone"), token);
+//               String title = "邦财通消息:"+toDoInfo.getTitle();
+
+                    String content = toDoInfo.getContent();
+                    if (messageContext.getEntityName() != null && !"".equals(messageContext.getEntityName())) {
+                        int firstindex = content.indexOf(messageContext.getEntityName());
+                        if (firstindex >= 0) {
+                            int subIndex = firstindex + messageContext.getEntityName().length();
+//                       content =content.substring(0,subIndex)+"\n"+content.substring(subIndex);
+                            content = content.substring(subIndex);
+                            if ("".equals(content)) {
+                                content = toDoInfo.getContent();
+                            }
+                        }
+                    }
+                    String node = " ";
+                    String title = toDoInfo.getTitle();
+                    sendFeishuMessage("1008", "", toDoInfo.getTaskId(), userid, title, content, node, token, changeDanDianSrc(pcUrl + "&apptype=feishu"), changeDanDianSrc(mobUrl + "&apptype=feishu"));
+                }
+            }
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        }
+
+    }
+
+    public String sendDaiBanFeishuMessage(String userid,String title,String content,String node,String token,String pcurl,String phoneurl){
+
+        JSONObject m = new JSONObject();
+
+        m.put("template_id","1008");
+        m.put("user_id",userid);
+        m.put("approval_name","@i18n@1");
+        m.put("note","@i18n@3");
+
+        JSONArray summaries = new  JSONArray();
+        JSONObject summarie = new  JSONObject();
+        summarie.put("summary","@i18n@2");
+        summaries.add(summarie);
+        JSONObject contentjson = new  JSONObject();
+
+        contentjson.put("summaries",summaries);
+        m.put("content",contentjson);
+
+        JSONArray actions = new JSONArray();
+        JSONObject action = new JSONObject();
+        action.put("action_name","DETAIL");
+        action.put("url",pcSrcToFeishu(pcurl));
+        action.put("android_url",phoneurl);
+        action.put("ios_url",phoneurl);
+        action.put("pc_url",pcSrcToFeishu(pcurl));
+        actions.add(action);
+        m.put("actions",actions);
+
+        JSONArray i18n_resources = new  JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale","en-US");
+        res.put("is_default",true);
+        JSONObject textsjson = new JSONObject();
+
+        String realtitle ="";
+        if(title!=null){
+            int index = title.indexOf("请处理");
+            if(index==0){
+                realtitle = title.substring(3);
+            }
+        }
+
+
+        textsjson.put("@i18n@1",realtitle);
+        textsjson.put("@i18n@2",content);
+        textsjson.put("@i18n@3",node);
+        res.put("texts",textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+        String userIdUrl="https://www.feishu.cn/approval/openapi/v1/message/send";
+        try {
+            log.info("门户发送消息:"+m.toString());
+            //飞书推送数据日志
+            log.info("飞书推送数据日志");
+            String uuid = saveMsgLog(userid, title, content, m);
+            log.info("飞书推送数据日志保存成功");
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户发送消息结果:"+response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+
+    public String sendFeishuMessage(String templateid, String submitMsg, Long taskId, String userid,String title,String content,String node,String token,String pcurl,String phoneurl){
+
+        JSONObject m = new JSONObject();
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+        }
+
+        //发起人
+        String startid = "";
+        if(taskInfo != null){
+            Long faqirenid = taskInfo.getLong("starterid");
+            if(faqirenid!=null){
+                DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                startid = getUserIdTrue(faqiren.getString("phone"),token);
+            }
+            title = taskInfo.getString("entityname");
+        }
+
+
+        m.put("template_id", templateid);
+        m.put("user_id", userid);
+
+        m.put("note","@i18n@3");
+
+        if("1021".equals(templateid)){
+            m.put("custom_title","@i18n@1");
+            m.put("custom_content","@i18n@2");
+
+            JSONArray actions = new JSONArray();
+            JSONObject action = new JSONObject();
+            action.put("action_name","@i18n@4");
+            action.put("url",pcSrcToFeishu(pcurl));
+            action.put("android_url",phoneurl);
+            action.put("ios_url",phoneurl);
+            action.put("pc_url",pcSrcToFeishu(pcurl));
+            actions.add(action);
+            m.put("actions",actions);
+        } else {
+            String kduuid = UUID.randomUUID().toString().replace("-", "");
+            m.put("uuid", kduuid);
+
+            m.put("title_user_id_type", "user_id");
+            m.put("title_user_id", startid);
+            m.put("approval_name", "@i18n@1");
+
+            JSONObject contentobj = new JSONObject();
+            contentobj.put("user_id", startid);
+            contentobj.put("user_id_type", "user_id");
+
+            JSONArray summarys = new JSONArray();
+            JSONObject summary1 = new JSONObject();
+            summary1.put("summary", "@i18n@2");
+
+            summarys.add(summary1);
+//
+//        JSONObject summary2 = new JSONObject();
+//        summary2.put("summary", "测试内容2");
+//        summarys.add(summary2);
+
+            contentobj.put("summaries", summarys);
+            m.put("content", contentobj);
+
+            if("1012".equals(templateid) || "1016".equals(templateid)) {
+                m.put("comment", "@i18n@4");
+            }
+
+            JSONArray actions = new JSONArray();
+            JSONObject action = new JSONObject();
+            action.put("action_name","DETAIL");
+            action.put("url",pcSrcToFeishu(pcurl));
+            action.put("android_url",phoneurl);
+            action.put("ios_url",phoneurl);
+            action.put("pc_url",pcSrcToFeishu(pcurl));
+            actions.add(action);
+            m.put("actions",actions);
+        }
+
+
+
+        JSONArray i18n_resources = new  JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale","en-US");
+        res.put("is_default",true);
+        JSONObject textsjson = new JSONObject();
+        textsjson.put("@i18n@1",title);
+        textsjson.put("@i18n@2",content);
+        textsjson.put("@i18n@3", "来自财务系统");
+
+        if("1021".equals(templateid)){
+            textsjson.put("@i18n@4","查看详情");
+        } else if("1012".equals(templateid) || "1016".equals(templateid)) {
+            textsjson.put("@i18n@4", submitMsg);
+        }
+
+        res.put("texts",textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+
+
+//        String userIdUrl="https://www.feishu.cn/approval/openapi/v1/message/send";
+        String userIdUrl="https://open.feishu.cn/open-apis/approval/v1/message/send";
+        try {
+            log.info("门户发送消息:"+m.toString());
+            String uuid = saveMsgLog(userid, title, content, m);
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 发送带链接的消息,发送至审批应用
+     * @param ctx
+     * @param message
+     * @param userid
+     * @param token
+     */
+    public void sendLinkMessage(MessageContext ctx, MessageInfo message, String userid, String token, String templateid){
+        String node = " ";
+        String pcurl  = message.getContentUrl();
+        String moburl  = message.getMobContentUrl();
+        if("".equals(pcurl)||pcurl==null){
+            pcurl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+        }
+        if("".equals(moburl)||moburl==null){
+            moburl = System.getProperty("domain.contextUrl")+"/mobile.html?form=er_mainpage_daily";
+            moburl = changeDanDianSrc(moburl+"&apptype=feishu");
+        }else{
+            moburl = changeDanDianSrc(moburl+"&apptype=feishu");
+        }
+        pcurl =changeDanDianSrc(pcurl+"&apptype=feishu");
+
+        String content = message.getContent();
+        if("".equals(content)||content==null){
+            content = message.getTitle();
+        }
+
+        //替换PC端域名
+        pcurl = replacePcUrlDomain(pcurl);
+        //移动端替换域名
+        moburl = replaceMobUrlDomain(moburl);
+
+        /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+        Long taskId = 0L;
+
+        QFilter qFilter = new QFilter("channel", QCP.equals, "feishu");
+        qFilter.and(new QFilter("id", QCP.equals, Long.valueOf(message.getChannelMsgId())));
+        DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+        if(msgFail != null){
+            qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgFail.getLong("messageid")));
+
+            DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+            if(msg != null && StringUtils.isNotEmpty(msg.getString("config"))){
+                JSONObject messateContext = JSONObject.parseObject(msg.getString("config")).getJSONObject("messageContext");
+                if(messateContext != null && messateContext.get("taskId") != null) {
+                    taskId = messateContext.getLong("taskId");
+                }
+            }
+        }
+
+        log.info("pcurl:"+ pcurl);
+        log.info("mobileurl:" + moburl);
+        sendFeishuMessage(templateid, "", taskId, userid,message.getTitle(),content,node,token,pcurl,moburl);
+    }
+
+    /**
+     * 发送传阅消息,发送至抄送页签
+     * @param ctx
+     * @param message
+     * @param userid
+     * @param token
+     */
+    public void sendFlowMessage(MessageContext ctx, MessageInfo message, String userid, String token){
+        try {
+            JSONObject m = new JSONObject(true);
+            JSONObject c = new JSONObject(true);
+
+            String channelMsgId = message.getChannelMsgId() + "";
+
+            c.put("approval_code", SPDYID);
+            c.put("instance_id","cq"+ channelMsgId);
+            c.put("status", "HIDDEN");
+            c.put("display_method", "SIDEBAR");
+
+            //url连接
+            JSONObject links = new JSONObject();
+
+            String pcUrl = message.getContentUrl() == null ? "" : message.getContentUrl();
+            String mobUrl = message.getMobContentUrl() == null ? pcUrl : message.getMobContentUrl();
+
+            log.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+            if(StringUtils.isNotEmpty(pcUrl)){
+                pcUrl = pcUrl.replace(mobileUrl, curUrl);
+            }
+
+            if(StringUtils.isNotEmpty(mobUrl)){
+                mobUrl = mobUrl.replace(curUrl, mobileUrl);
+            }
+
+            if("".equals(pcUrl)||pcUrl==null){
+                pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+            }
+
+
+            links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+            c.put("links", links);
+
+            String content = message.getContent() == null ? message.getTitle() : message.getContent();
+            String title = message.getTitle();
+            //标题
+            c.put("title", title);
+
+            Long createTime = (new Date()).getTime();
+
+            c.put("start_time", createTime);
+            c.put("update_time", createTime);
+            c.put("update_mode", "UPDATE");
+
+
+            /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+            String workflowname = "财务系统审批流";
+            String nodename = "";
+            String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            QFilter qFilter = new QFilter("channel", QCP.equals, "feishu");
+            qFilter.and(new QFilter("id", QCP.equals, Long.valueOf(message.getChannelMsgId())));
+            DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+            if(msgFail != null){
+                qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgFail.getLong("messageid")));
+
+                DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+                if(msg != null && StringUtils.isNotEmpty(msg.getString("config"))){
+                    JSONObject messateContext = JSONObject.parseObject(msg.getString("config")).getJSONObject("messageContext");
+                    Long taskId = messateContext.getLong("taskId");
+                    Long faqirenid = messateContext.getLong("startUserId");
+                    /*************处理消息格式*****************/
+                    DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+                    //add by wnaghaiwu_kd 2024/04/28
+                    //如果任务中找不到,就找历史任务
+                    if(taskInfo == null){
+                        taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+                    }
+
+                    if(taskInfo != null){
+                        nodename = taskInfo.getString("name");
+
+                        if(taskInfo.getLong("processdefinitionid") > 0) {
+                            qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                            DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                            if (processdef != null) {
+                                workflowname = processdef.getString("name");
+                            }
+                        }
+
+                        if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                            workflowname = taskInfo.getString("entityname") + "审批流程";
+                        }
+                        if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                                && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                            qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                            DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                            if(bill != null){
+                                createDate = getCreateTime(bill);
+                            }
+
+                        }
+
+                        //发起人
+                        faqirenid = taskInfo.getLong("starterid");
+                    }
+
+                    if(faqirenid != null){
+                        DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                        c.put("open_id", getUserId(faqiren.getString("phone"),token));
+                    }
+                }
+            }
+
+            c.put("title", workflowname);
+
+            JSONArray formArr = new JSONArray();
+            JSONObject form1 = new JSONObject();
+            form1.put("name", "流程标题");
+            form1.put("value", StringUtils.isEmpty(msgFail.getString("title")) ? workflowname : msgFail.getString("title"));
+            formArr.add(form1);
+
+            JSONObject form2 = new JSONObject();
+            form2.put("name", "提单时间");
+            form2.put("value", createDate);
+            formArr.add(form2);
+
+            JSONObject form3 = new JSONObject();
+            form3.put("name", "当前节点");
+            form3.put("value", nodename);
+            formArr.add(form3);
+
+            c.put("form", formArr);
+
+            /*************处理消息格式*****************/
+
+            //cc_list	抄送人相关
+            JSONArray ccArr = new JSONArray();
+
+            List<Long> userIds = message.getUserIds();
+            if (CollectionUtils.isEmpty(userIds)) {
+                log.info("财务系统飞书,人员为空,抄送失败");
+                return;
+            }
+
+            qFilter = new QFilter("id", QCP.in, userIds);
+            DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+            for (int i = 0; i <load.length ; i++) {
+                JSONObject cc = new JSONObject();
+                cc.put("cc_id", load[i].getPkValue()+"_"+ channelMsgId);
+                cc.put("open_id", getUserId(load[i].getString("phone"),token));
+                cc.put("read_status", "UNREAD");
+                cc.put("title", title);
+                cc.put("create_time", createTime);
+                cc.put("update_time", createTime);
+
+                cc.put("title", workflowname);
+
+//                cc.put("display_method", "SIDEBAR");
+
+                JSONObject links1 = new JSONObject();
+                links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+                links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+                cc.put("links", links1);
+
+                ccArr.add(cc);
+            }
+            c.put("cc_list", ccArr);
+
+            m.put("content", c);
+
+            log.info("财务系统飞书推送传阅:"+m.toJSONString());
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书推送传阅结果:"+response);
+
+
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 发送普通消息,发送至自建应用
+     * @param ctx
+     * @param info
+     * @param userid
+     * @param token
+     * @return
+     */
+    public String sendCustomMessage(MessageContext ctx, MessageInfo info, String userid, String token){
+        JSONObject m = new JSONObject();
+
+        String uuid = "KD-" + UUIDUtil.generateUuid();
+        String title = info.getTitle() == null ? "" : info.getTitle();
+        String contentText = info.getContent() == null ? "" : info.getContent();
+
+        //飞书不支持星瀚的换行标签,替换成飞书兼容的换行标签
+        contentText = contentText.replace("<a></a>", "\r\n");
+        contentText = contentText.replace("</a><a>", "\r\n");
+        contentText = contentText.replace("<a>", "");
+        contentText = contentText.replace("</a>", "");
+
+        m.put("receive_id", userid);
+        m.put("msg_type","text");
+
+        JSONObject content = new JSONObject();
+        content.put("text", contentText);
+        m.put("content", content.toJSONString());
+        m.put("uuid", uuid);
+
+        String linkUrl = info.getContentUrl();
+        String linkMobileUrl = info.getMobContentUrl();
+
+        DynamicObject msgInfo = BusinessDataServiceHelper.loadSingleFromCache(info.getId(), "wf_msg_message");
+        if(msgInfo != null) {
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkUrl = msgInfo.getString("contenturl");
+            }
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkMobileUrl = msgInfo.getString("mobcontenturl");
+            }
+            if(StringUtils.isNotEmpty(linkMobileUrl)){
+                linkMobileUrl = linkUrl;
+            }
+        }
+
+        //替换PC端域名
+        linkUrl = replacePcUrlDomain(linkUrl);
+        //移动端替换域名
+        linkMobileUrl = replaceMobUrlDomain(linkMobileUrl);
+
+        String userIdUrl = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id";
+        try {
+            log.info("门户发送消息:"+m.toString());
+
+            uuid = saveMsgLog(userid, title, content.toJSONString(), m);
+
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+
+    /**
+     * 获取取消JSON
+     * @return
+     */
+    public JSONObject getdeleteJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content,String taskId){
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+taskId);
+        c.put("status", "CANCELED");
+        c.put("display_method", "SIDEBAR");
+
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        if(taskInfo != null){
+            String handlestate = taskInfo == null ? "" : taskInfo.getString("handlestate");
+
+            //驳回状态的任务做提交处理时不做待办更新推送。
+            if("willApproval".equals(handlestate)){
+                c.put("status", "APPROVED");
+            }
+        }
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+        c.put("links", links);
+
+        //标题
+        c.put("title", toDoInfo.getContent());
+        if(isUpdate){
+            c.put("title", content);
+        }
+
+        //发起人
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("end_time", d.getTime());
+        c.put("update_time", d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        /*************处理消息格式*****************/
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+
+        //发起人
+
+        Long faqirenid = taskInfo.getLong("starterid");
+        if(faqirenid!=null){
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+            c.put("open_id", getUserId(faqiren.getString("phone"),token));
+        }
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id",load[i].getPkValue()+"_"+taskId);
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc( changephoneurl(mobUrl,taskId)));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "DONE");
+            }
+
+
+            task.put("title",  toDoInfo.getContent());
+            if(isUpdate){
+                task.put("title", content);
+            }
+
+            task.put("title", workflowname);
+
+            Date d1 = new Date();
+            task.put("create_time", d1.getTime());
+            task.put("end_time", d1.getTime());
+            task.put("update_time", d1.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+
+
+        return m;
+    }
+
+
+    /**
+     * 驳回JSON
+     * @return
+     */
+    public JSONObject getBoHuiJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content,String taskId){
+
+        boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+        if(!isExist){
+            return getdeleteJson(messageContext,toDoInfo,load,token,isUpdate,passStatus,content,taskId);
+        }
+
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+taskId);
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link",changeDanDianSrc( changephoneurl(mobUrl,taskId)));
+        c.put("links", links);
+
+        //标题
+        if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+            c.put("title", toDoInfo.getContent());
+        }
+        if(isUpdate){
+            if(content!=null&&!"".equals(content)) {
+                c.put("title", content);
+            }
+        }
+        String oldcontent="";
+        if(toDoInfo.getContent()==null&&content==null){
+            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+            oldcontent =getTitle(messageContext,businessObject);
+            c.put("title", oldcontent);
+        }
+        //发起人
+
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("update_time",d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", "(被驳回)" + (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+taskId);
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(changephoneurl(mobUrl,taskId)) );
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+
+            if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+                task.put("title",  toDoInfo.getContent());
+            }
+            if(isUpdate){
+                if(content!=null&&!"".equals(content)) {
+                    task.put("title", content);
+                }
+            }
+            if(toDoInfo.getContent()==null&&content==null){
+                task.put("title", oldcontent);
+            }
+
+//            task.put("title", workflowname);
+
+            task.put("create_time", d.getTime());
+            task.put("end_time", d.getTime());
+            task.put("update_time", d.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+    public static String changephoneurl(String url,String taskId){
+        String returnurl="";
+        if(url==null){
+            return null;
+        }
+        int firstindex = url.indexOf("&tId=");
+        if(firstindex>0){
+            returnurl = url.substring(0,firstindex)+"&apptype=feishu";
+        }
+        return returnurl;
+    }
+
+    /**
+     * 待办变已办
+     * @return
+     */
+    public JSONObject getToYiBanJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content){
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+toDoInfo.getTaskId());
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(changephoneurl(mobUrl,toDoInfo.getTaskId()+"")));
+        c.put("links", links);
+
+        //标题
+        if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+            c.put("title", toDoInfo.getContent());
+        }
+        if(isUpdate){
+            if(content!=null&&!"".equals(content)) {
+                c.put("title", content);
+            }
+        }
+        String oldcontent="";
+        if(toDoInfo.getContent()==null&&content==null){
+            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+            oldcontent =getTitle(messageContext,businessObject);
+            c.put("title", oldcontent);
+        }
+//        Long faqirenid = messageContext.getStartUserId();
+//        if(faqirenid!=null){
+//            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+//            c.put("open_id", FeishuUtil.getUserId(faqiren.getString("phone"),token));
+//        }
+
+        //发起人
+
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+
+            //发起人
+
+            Long faqirenid = taskInfo.getLong("starterid");
+            if(faqirenid!=null){
+                DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                c.put("open_id", getUserId(faqiren.getString("phone"),token));
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle());
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("update_time",d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+toDoInfo.getTaskId());
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(changephoneurl(mobUrl,toDoInfo.getTaskId()+"")));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+            if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+                task.put("title",  toDoInfo.getContent());
+            }
+            if(isUpdate){
+                if(content!=null&&!"".equals(content)) {
+                    task.put("title", content);
+                }
+            }
+            if(toDoInfo.getContent()==null&&content==null){
+                task.put("title", oldcontent);
+            }
+
+            task.put("title", workflowname);
+
+            task.put("create_time", d.getTime());
+            task.put("end_time", d.getTime());
+            task.put("update_time", d.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+
+
+    /**
+     * 获取创建审批实例json
+     * @return
+     */
+    public JSONObject getToDaiBanJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content, String fixName){
+        boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+        if(!isExist){
+            return getdeleteJson(messageContext,toDoInfo,load,token,isUpdate,passStatus,content,toDoInfo.getTaskId()+"");
+        }
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingle(toDoInfo.getTaskId() ,"wf_task");
+
+
+        JSONObject m = new JSONObject(true);
+
+
+
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+toDoInfo.getTaskId());
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+        c.put("links", links);
+
+        //标题
+        c.put("title", toDoInfo.getContent());
+        if(isUpdate){
+            c.put("title", content);
+        }
+
+        /*************处理消息格式*****************/
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", fixName + (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+
+        //发起人
+
+        Long faqirenid = taskInfo.getLong("starterid");
+        if(faqirenid!=null){
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+            c.put("open_id", getUserId(faqiren.getString("phone"),token));
+
+        }
+
+
+        c.put("start_time", ((Timestamp)taskInfo.get("createdate")).getTime());
+        c.put("update_time", ((Timestamp)taskInfo.get("createdate")).getTime());
+        c.put("update_mode", "REPLACED");
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+toDoInfo.getTaskId());
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+
+            task.put("title",  toDoInfo.getContent());
+            if(isUpdate){
+                task.put("title", content);
+            }
+            task.put("title", workflowname);
+
+            task.put("create_time", taskInfo.get("createdate"));
+            task.put("end_time", taskInfo.get("createdate"));
+            task.put("update_time", taskInfo.get("createdate"));
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+    //pc链接特殊处理
+    public String pcSrcToFeishu(String src){
+        String dandianStr="";
+        if(dandianStr != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://applink.feishu.cn/client/web_url/open?mode=window&url=" + encodesrc;
+            } catch (UnsupportedEncodingException e) {
+                log.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    public static String changeDanDianSrc(String src){
+        String dandianStr="";
+        if(src != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=" + encodesrc + "&app_id=" + APP_ID;
+            } catch (UnsupportedEncodingException e) {
+                log.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    /**
+     * 工作流中将待办变成已办的时候,会调用此接口进行待办变已办的处理
+     * @param messageContext
+     * @param toDoInfo
+     */
+    @Override
+    public void dealToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+        log.info("财务系统飞书,更新待办为已办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+        try {
+            List<Long> userIds = toDoInfo.getUserIds();
+            if (CollectionUtils.isEmpty(userIds)) {
+                dynamicObject.set("nckd_msg_content", "财务系统飞书,人员为空,推送失败");
+                log.info("财务系统飞书,人员为空,推送失败");
+                return;
+            }
+            log.info("获取用户开始");
+            dynamicObject.set("nckd_to_userid", userIds.get(0));
+            QFilter qFilter = new QFilter("id", QCP.in, userIds);
+            DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+            log.info("获取用户结束:"+load[0].getPkValue());
+
+            String token = getToken();
+            String status="APPROVED";
+            log.info("获取任务:");
+            boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+            if(isExist){
+                DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+                log.info("获取任务结束:"+taskInfo.getPkValue());
+                String taskStatus=taskInfo.getString("handlestate");
+                if("dismissed".equals(taskStatus)){//驳回
+                    status="REJECTED";
+                }else if("willApproval".equals(taskStatus)){
+                    //            status="REJECTED";
+                }
+            }
+//            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+//            String content =getTitle(messageContext,businessObject);
+            String content =toDoInfo.getContent();
+            log.info("获取json开始:");
+            dynamicObject.set("nckd_msg_title", toDoInfo.getTitle());
+
+            JSONObject m = getToYiBanJson(messageContext,toDoInfo,load,token,true,status,content);
+            dynamicObject.set("nckd_msg_content", "财务系统飞书更新待办[DaJingDaiBanServiceHandler.dealToDo]");
+
+            log.info("财务系统飞书更新待办:"+m.toJSONString());
+            String msgContent = m.toJSONString();
+            if (msgContent.length() < 200) {
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            } else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+
+            String response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书更新待办结果:"+response);
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+                response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                log.info("财务系统飞书更新待办结果二次:" + response);
+            }
+            if (response.length() < 200) {
+                dynamicObject.set("nckd_send_result_l", response);
+            } else {
+                dynamicObject.set("nckd_send_result_l", response.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_send_result_l_tag", response);
+
+        } catch (Exception e) {
+            log.info("财务系统飞书,更新待办失败" + e.getMessage());
+            log.info(e.getMessage());
+            e.printStackTrace();
+        } finally {
+            // 记录日志
+            String uuid = messageContext.getProcessInstanceId().longValue() + "";
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("nckd_send_time", new Date());
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            log.info("记录新增飞书推送数据日志成功:{}", uuid);
+        }
+
+
+
+
+    }
+
+    /**
+     * 工作流中撤回,或者多人收到任务,其中一人审批,删除其他人的待办时,调用此接口
+     * @param messageContext
+     * @param toDoInfo
+     */
+    @Override
+    public void deleteToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+
+        log.info("财务系统飞书,取消待办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+        List<Long> userIds = toDoInfo.getUserIds();
+        QFilter qFilter = new QFilter("id", QCP.in, userIds);
+        DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+        String token = getToken();
+//        DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+//        String content =getTitle(messageContext,businessObject);
+        String content = toDoInfo.getContent();
+        JSONObject m = getToDaiBanJson(messageContext,toDoInfo,load,token,true,"DONE",content, "");
+        try {
+            log.info("财务系统飞书取消待办:"+m.toJSONString());
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书取消待办结果:"+response);
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,取消待办失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        }
+    }
+
+    public String getTitle(MessageContext messageContext,DynamicObject businessObject){
+        String content = "请处理:%s单据编号:%s";
+
+        DynamicObjectType dt = businessObject.getDynamicObjectType();
+        IDataEntityProperty pkProp = dt.getPrimaryKey();
+        DataEntityPropertyCollection properties = dt.getProperties();
+
+        //发现预算的单据虽然是基础资料,但编码字段是billno, 所以改成根据实体属性是否包含number或billno字段
+        if(properties.containsKey("number")){
+            content = String.format(content, messageContext.getEntityName(), businessObject.getString("number"));
+        } else if(properties.containsKey("billno")){
+            content = String.format(content, messageContext.getEntityName(), businessObject.getString("billno"));
+        } else {
+            content = String.format(content, messageContext.getEntityName(), dt.getName());
+        }
+
+//        if (dt instanceof BasedataEntityType) {
+//            content = String.format(content, messageContext.getEntityName(), businessObject.getString("number"));
+//        }else{
+//            content = String.format(content, messageContext.getEntityName(), businessObject.getString("billno"));
+//        }
+
+        return content;
+    }
+
+    /**
+     * 普通消息暂时不推送
+     * @param ctx
+     * @param message
+     */
+    @Override
+    public void sendMessage(MessageContext ctx, MessageInfo message) {
+
+        try {
+            String notifyType = message.getNotifyType();
+            if (ctx != null){
+                log.info("DaiBanServiceHandler.sendMessage 飞书待办消息url:" + message.getContentUrl() + ",mobileurl:" + message.getMobContentUrl()+ ", pk:" + ctx.getBusinessKey());
+            }else {
+                log.info("DaiBanServiceHandler.sendMessage 飞书待办消息url:" + message.getContentUrl() + ",mobileurl:" + message.getMobContentUrl());
+            }
+            if("feishu".equals(notifyType)){
+                log.info("财务系统移动审批,消息发 taskId:" + message.getId());
+                String token = getToken();
+                List<Long> userIds = message.getUserIds();
+
+                String linkUrl = message.getContentUrl();
+                String linkMobileUrl = message.getMobContentUrl();
+
+                //替换PC端域名
+                linkUrl = replacePcUrlDomain(linkUrl);
+                //移动端替换域名
+                linkMobileUrl = replaceMobUrlDomain(linkMobileUrl);
+
+                //发送消息
+                for (int i = 0; i < userIds.size() ; i++) {
+                    DynamicObject userInfo =  BusinessDataServiceHelper.loadSingle(userIds.get(i),"bos_user");
+                    String userid =  getUserIdTrue(userInfo.getString("phone"),token);
+
+
+                    if("circulation".equals(message.getTplScene())){
+                        //发送传阅消息
+                        sendFlowMessage(ctx, message, userid, token);
+                        //发送带链接的消息
+                        sendLinkMessage(ctx, message, userid, token, "1016");
+                    } else if(StringUtils.isNotEmpty(linkUrl)){
+                        //发送带链接的消息
+                        sendLinkMessage(ctx, message, userid, token, "1021");
+                    } else {
+                        //发送普通消息
+                        userid =  getUserId(userInfo.getString("phone"),token);
+                        sendCustomMessage(ctx, message, userid, token);
+                    }
+                }
+            }
+        }catch (Exception e){
+            log.error("DaiBanServiceHandler.sendMessage 发送审批消息异常:" + e.getMessage(), e);
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * 替换移动端域名
+     * @param mobUrl
+     * @return
+     */
+    public String replaceMobUrlDomain(String mobUrl){
+        log.info("替换前移动端地址: moburl:" + mobUrl);
+
+        if(StringUtils.isEmpty(mobUrl)){
+            return mobUrl;
+        }
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileDomain = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        mobUrl = mobUrl.replace(curUrl, mobileDomain);
+
+        log.info("替换后移动端地址: moburl:" + mobUrl);
+
+        return mobUrl;
+    }
+
+    /**
+     * 替换PC端域名
+     * @param pcUrl
+     * @return
+     */
+    public String replacePcUrlDomain(String pcUrl){
+        log.info("替换前PC端地址: pcUrl:" + pcUrl);
+
+        if(StringUtils.isEmpty(pcUrl)){
+            return pcUrl;
+        }
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileDomain = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        pcUrl = pcUrl.replace(mobileDomain, curUrl);
+
+        log.info("替换后地址: pcUrl:" + pcUrl);
+
+        return pcUrl;
+    }
+
+
+    private final String getAppWfName() {
+        return System.getProperty("app.wf.name", TRAGETNAME);
+    }
+
+    private String getCreateTime(DynamicObject bill){
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        if(bill.getDynamicObjectType().getProperties().contains("createtime")) {
+            createDate = DateFormatUtils.format(bill.getDate("createtime"), "yyyy-MM-dd HH:mm:ss");
+        } else if(bill.getDynamicObjectType().getProperties().contains("createdate")) {
+            createDate = DateFormatUtils.format(bill.getDate("createdate"), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        return createDate;
+    }
+
+    /**
+     * 保存飞书消息日志
+     *
+     * @param userid  用户id
+     * @param title   标题
+     * @param content 内容
+     * @param m       发送消息体
+     * @return uuid
+     */
+    private String saveMsgLog(String userid, String title, String content, JSONObject m) {
+        try {
+            log.info("记录新增飞书推送数据日志");
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("nckd_to_userid", userid);
+            dynamicObject.set("nckd_msg_title", title);
+            String msgContent = m.toString();
+            if (msgContent.length() < 200){
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            }else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+            if (content.length() < 200){
+                dynamicObject.set("nckd_msg_content", content);
+            }else {
+                dynamicObject.set("nckd_msg_content", content.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_tag", content);
+            dynamicObject.set("nckd_send_time", new Date());
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            log.info("记录新增飞书推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            log.info("记录新增飞书推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 更新消息响应到对应日志记录
+     * @param uuid uuid
+     * @param response 消息响应
+     */
+    private void updateMsgLog(String uuid, String response) {
+        try {
+            log.info("记录更新飞书推送结果数据日志");
+            QFilter qFilterCas = new QFilter("billno", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("nckd_log_feishumsg", "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, "nckd_log_feishumsg");
+                if (response.length() < 200){
+                    dynamicObject.set("nckd_send_result_l", response);
+                }else {
+                    dynamicObject.set("nckd_send_result_l", response.substring(0,200) + "...");
+                }
+                dynamicObject.set("nckd_send_result_l_tag", response);
+                SaveServiceHelper.update(dynamicObject);
+                log.info("记录更新飞书推送结果数据日志成功");
+            }
+        } catch (Exception e) {
+            log.info("记录更新飞书推送结果数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void deleteProcessInstance(MessageContext ctx, Long proceInstanceId) {
+        // 打印控制台日志
+        System.out.println(String.format("流程实例:%d 被删除了,messageContext:" + ctx, proceInstanceId));
+        log.info(String.format("流程实例:%d 被删除了,messageContext:" + ctx, proceInstanceId));
+    }
+
+    @Override
+    public void completeProcessInstance(MessageContext ctx, Long proceInstanceId) {
+        // 流程归档
+        System.out.println(String.format("流程实例:%d 结束了,messageContext:" + ctx, proceInstanceId));
+        log.info(String.format("流程实例:%d 结束了,messageContext:" + ctx, proceInstanceId));
+    }
+
+}

+ 187 - 0
main/java/kd/cosmic/jkjt/msg/feishu/FeiShuRetryUtil.java

@@ -0,0 +1,187 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.rmi.ConnectException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @description 飞书同步异常补偿重试工具类
+ * @author wanghaiwu_kd
+ * @date 2025/02/26
+ */
+public class FeiShuRetryUtil {
+    private static final Logger log = LoggerFactory.getLogger(FeiShuRetryUtil.class);
+    private static final String APP_ID = "cli_a786040738b5d00d";//个人测试
+    private static final String APP_SERCRET = "WCHnL9EIs654PxRtdMFLVeMB30EwUFOJ";
+
+    private static List<Integer> interErrCodeArr = Arrays.asList(new Integer[]{
+            65001,90203,90235,90242,91201,95001,95003,95005,95010,95011,
+            95203,95204,95205,95206,95207,95208,95209,
+            105001,190003,230020,1050002,1050008,1069301,1069399
+    });
+
+    /**
+     * 重新同步飞书失败的消息
+     * @param logId
+     */
+    public static String reSynchronizeMsg (Long logId) {
+        QFilter qFilter = new QFilter("id", QCP.equals, logId);
+        DynamicObject logInfo = BusinessDataServiceHelper.loadSingle("nckd_log_feishumsg", qFilter.toArray());
+//        if("success".equals(logInfo.getString("nckd_sysstatus"))){
+//            return "只有失败的记录才能重新同步!";
+//        }
+
+        String returnMsg = "";
+        if("dealToDo".equals(logInfo.getString("nckd_logtype"))){
+            returnMsg = synDealToDoMessage(logInfo);
+        }
+
+        return returnMsg;
+    }
+
+    /**
+     * 重新同步待办处理消息
+     * @param logInfo
+     */
+    private static String synDealToDoMessage(DynamicObject logInfo){
+        String msg = logInfo.getString("nckd_msg_content_l_tag");
+
+        if(msg == null){
+            return "日志中的【推送消息json】字段为空,重新执行飞书同步失败!";
+        }
+        try {
+            JSONObject jsonMsg = JSONObject.parseObject(msg);
+
+            JSONObject jsonContent = jsonMsg.getJSONObject("content");
+
+            Long currentTime = (new Date()).getTime();
+            //处理时间
+            jsonContent.put("start_time", currentTime);
+            jsonContent.put("update_time", currentTime);
+
+            String token = FeishuUtil.getToken(APP_ID, APP_SERCRET);
+
+            String openId = "";
+            //流程发起人飞书账号
+            if(StringUtils.isEmpty(jsonContent.getString("open_id"))){
+                if(StringUtils.isNotEmpty(logInfo.getString("nckd_creatoridfeishu"))){
+                    openId = logInfo.getString("nckd_creatoridfeishu");
+                } else {
+                    String creatorId = logInfo.getString("nckd_creatorid");
+                    DynamicObject creator = BusinessDataServiceHelper.loadSingleFromCache(Long.valueOf(creatorId), "bos_user");
+
+                    openId = FeishuUtil.getUserId(creator.getString("phone"),token);
+                }
+
+                jsonContent.put("open_id", openId);
+            }
+
+            JSONArray jsonTaskList = jsonContent.getJSONArray("task_list");
+
+            if(jsonTaskList == null){
+                return "日志中的【推送消息json】字段中task_list为空,重新执行飞书同步失败!";
+            }
+
+            openId = "";
+
+            for(Object task : jsonTaskList){
+                JSONObject jsonTask = (JSONObject)task;
+
+                jsonTask.put("create_time", currentTime);
+                jsonTask.put("update_time", currentTime);
+                jsonTask.put("end_time", currentTime);
+
+                //接收人飞书账号
+                if(StringUtils.isEmpty(jsonTask.getString("open_id"))){
+                    if(StringUtils.isNotEmpty(logInfo.getString("nckd_to_useridfeishu"))){
+                        openId = logInfo.getString("nckd_to_useridfeishu");
+                    } else {
+                        String receiverId = logInfo.getString("nckd_to_userid");
+                        DynamicObject receiver = BusinessDataServiceHelper.loadSingleFromCache(Long.valueOf(receiverId), "bos_user");
+
+                        openId = FeishuUtil.getUserId(receiver.getString("phone"),token);
+                    }
+
+                    jsonTask.put("open_id", openId);
+                }
+            }
+
+            msg = jsonMsg.toJSONString();
+
+            if (msg.length() < 200){
+                logInfo.set("nckd_msg_content_l2", msg);
+            } else {
+                logInfo.set("nckd_msg_content_l2", msg.substring(0,200) + "...");
+            }
+            logInfo.set("nckd_msg_content_l2_tag", msg);
+
+//            SaveServiceHelper.update(new DynamicObject[]{logInfo});
+
+            String url = logInfo.getString("nckd_apiurl");
+
+            String response = FeishuUtil.doPostByHttpClient(url, msg, true, token, 3);
+            log.info("重新执行飞书同步:"+response);
+
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+
+                response = FeishuUtil.doPostByHttpClient(url, msg, true, token, 3);
+                log.info("重新执行飞书同步重试第二次:" + response);
+            }
+
+            if (StringUtils.isNotEmpty(response)) {
+                if (response.length() < 200){
+                    logInfo.set("nckd_send_result_l2", response);
+                }else {
+                    logInfo.set("nckd_send_result_l2", response.substring(0,200) + "...");
+                }
+                logInfo.set("nckd_send_result_l2_tag", response);
+
+                if(response != null && isJson(response)) {
+                    res = JSONObject.parseObject(response);
+                    if ("0".equals(res.getString("code"))) {
+                        logInfo.set("nckd_sysstatus", "success");
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.info("重新执行飞书同步失败:" + e.getMessage());
+
+            return "重新执行飞书同步失败:" + e.getMessage();
+        } finally {
+            SaveServiceHelper.update(new DynamicObject[]{logInfo});
+        }
+        return "";
+    }
+
+    private static boolean isJson(String str){
+        try{
+            JSONObject jsonStr = JSONObject.parseObject(str);
+            return true;
+        } catch(Exception e){
+            return false;
+        }
+    }
+}

+ 545 - 0
main/java/kd/cosmic/jkjt/msg/feishu/FeishuUtil.java

@@ -0,0 +1,545 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.cosmic.jkjt.msg.ecology.SSLClient;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.SocketTimeoutException;
+import java.net.URLEncoder;
+import java.rmi.ConnectException;
+import java.util.Date;
+
+/**
+ * @author zhongxinjian
+ * @create 2022/10/8 17:02:11
+ */
+public class FeishuUtil {
+    private static final Logger log = LoggerFactory.getLogger(FeishuUtil.class);
+
+    /**
+     * 获取飞书token
+     *
+     * @return
+     */
+    public static String getToken(String appId, String secret) {
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        // 加载参数
+        // initParams();
+
+        tokenJson.put("app_id", appId);
+        tokenJson.put("app_secret", secret);
+        try {
+            String returnSt = FeishuUtil.doPostByHttpClient(url, tokenJson.toJSONString(), false, null, 0);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if ("0".equals(returnJson.getString("code"))) {//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (Exception e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 更改手机号url
+     * @param url
+     * @param taskId
+     * @return
+     */
+    public static String changephoneurl(String url, String taskId) {
+        String returnurl = "";
+        if (url == null) {
+            return null;
+        }
+        int firstindex = url.indexOf("&tId=");
+        if (firstindex > 0) {
+            returnurl = url.substring(0, firstindex) + "&apptype=feishu";
+        }
+        return returnurl;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell, String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+//        mobiles.add("18680370905");//梁帆
+//        mobiles.add("18679933085");//sss
+
+        m.put("mobiles", mobiles);
+        String userIdUrl = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+            String response = FeishuUtil.doPostByHttpClient(userIdUrl, m.toJSONString(), true, token, 0);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    /**
+     * 根据手机号(暂定)获取 userId/openId
+     * @param cell
+     * @param token
+     * @return
+     */
+    public static String getUserIdTrue(String cell, String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+//        mobiles.add("18680370905");//梁帆
+//        mobiles.add("18679933085");//sss
+
+        m.put("mobiles", mobiles);
+        String userIdUrl = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(), true, token, 0);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    /**
+     * post 飞书请求
+     * @param url
+     * @param data
+     * @param isBearer
+     * @param token
+     * @return
+     * @throws ConnectException
+     */
+    public static String doPostByHttpClient(String url, String data, boolean isBearer, String token, int times) throws ConnectException {
+        log.info("url = {}", url);
+        log.info("data = {}", data);
+        CloseableHttpClient httpClient = null;
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            if (url.toLowerCase().startsWith("https://")) {
+                log.info("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                log.info("use nonssl");
+                httpClient = HttpClients.createDefault();
+            }
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if (isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+            // 设置超时时间 超时直接返回失败
+            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(3000)
+                    .setConnectionRequestTimeout(60000).build();
+            httpPost.setConfig(requestConfig);
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+
+            String body = EntityUtils.toString(response.getEntity());
+            log.info("请求:{},结果:{}", url, body);
+            return body;
+        } catch (SocketTimeoutException | ConnectTimeoutException ex){
+            log.error("请求url " + url + " 超时", ex);
+            // 执行重试
+            if (times > 0){
+                times--;
+                if (times > 3){
+                    times = 3;
+                }
+                return doPostByHttpClient(url, data, isBearer, token, times);
+            }else {
+                throw new ConnectException("请求飞书接口超时");
+            }
+        }catch (Exception e) {
+            log.error("doPostByHttpClient() ERROR" + e.getMessage(), e);
+            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+            try {
+                httpClient.close();
+            } catch (IOException e) {
+                httpClient = null;
+                log.error("关闭http请求失败" + e.getMessage(), e);
+            }
+        }
+    }
+
+
+    public static String sendDaiBanFeishuMessage(String userid, String title, String content, String node, String token, String pcurl, String phoneurl) {
+
+        JSONObject m = new JSONObject();
+
+        m.put("template_id", "1008");
+        m.put("user_id", userid);
+        m.put("approval_name", "@i18n@1");
+        m.put("note", "@i18n@3");
+
+        JSONArray summaries = new JSONArray();
+        JSONObject summarie = new JSONObject();
+        summarie.put("summary", "@i18n@2");
+        summaries.add(summarie);
+        JSONObject contentjson = new JSONObject();
+
+        contentjson.put("summaries", summaries);
+        m.put("content", contentjson);
+
+        JSONArray actions = new JSONArray();
+        JSONObject action = new JSONObject();
+        action.put("action_name", "DETAIL");
+        action.put("url", pcSrcToFeishu(pcurl));
+        action.put("android_url", phoneurl);
+        action.put("ios_url", phoneurl);
+        action.put("pc_url", pcSrcToFeishu(pcurl));
+        actions.add(action);
+        m.put("actions", actions);
+
+        JSONArray i18n_resources = new JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale", "en-US");
+        res.put("is_default", true);
+        JSONObject textsjson = new JSONObject();
+
+        String realtitle = "";
+        if (title != null) {
+            int index = title.indexOf("请处理");
+            if (index == 0) {
+                realtitle = title.substring(3);
+            }
+        }
+
+
+        textsjson.put("@i18n@1", realtitle);
+        textsjson.put("@i18n@2", content);
+        textsjson.put("@i18n@3", node);
+        res.put("texts", textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+        String userIdUrl = "https://www.feishu.cn/approval/openapi/v1/message/send";
+        try {
+            log.info("财务系统发送消息:" + m.toString());
+            //飞书推送数据日志
+            log.info("飞书推送数据日志");
+
+            String uuid = saveMsgLog(userid, title, content, m);
+            log.info("飞书推送数据日志保存成功");
+            String response = FeishuUtil.doPostByHttpClient(userIdUrl, m.toJSONString(), true, token, 1);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            QFilter qFilterCas = new QFilter("billno", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("nckd_log_feishumsg", "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                log.info("更新发送bot消息的结果");
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject1 = BusinessDataServiceHelper.loadSingle(id, "nckd_log_feishumsg");
+                dynamicObject1.set("nckd_send_result", response);
+                SaveServiceHelper.update(dynamicObject1);
+                log.info("更新发送bot消息的结果完成");
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("财务系统发送消息结果:" + response);
+            return userRes.toJSONString();
+        } catch (Exception e) {
+            log.info(e.getMessage(), e);
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    public static String sendFeishuMessage(String userid, String title, String content, String node, String token, String pcurl, String phoneurl) {
+
+        JSONObject m = new JSONObject();
+
+        m.put("template_id", "1021");
+        m.put("user_id", userid);
+        m.put("custom_title", "@i18n@1");
+        m.put("custom_content", "@i18n@2");
+        m.put("note", "@i18n@3");
+
+
+        JSONArray actions = new JSONArray();
+        JSONObject action = new JSONObject();
+        action.put("action_name", "@i18n@4");
+        action.put("url", FeishuUtil.pcSrcToFeishu(pcurl));
+        action.put("android_url", phoneurl);
+        action.put("ios_url", phoneurl);
+        action.put("pc_url", FeishuUtil.pcSrcToFeishu(pcurl));
+        actions.add(action);
+        m.put("actions", actions);
+
+        JSONArray i18n_resources = new JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale", "en-US");
+        res.put("is_default", true);
+        JSONObject textsjson = new JSONObject();
+        textsjson.put("@i18n@1", title);
+        textsjson.put("@i18n@2", content);
+        textsjson.put("@i18n@3", node);
+        textsjson.put("@i18n@4", "查看详情");
+        res.put("texts", textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+        String userIdUrl = "https://www.feishu.cn/approval/openapi/v1/message/send";
+        try {
+            log.info("财务系统发送消息:" + m.toString());
+            String uuid = saveMsgLog(userid, title, content, m);
+            String response = FeishuUtil.doPostByHttpClient(userIdUrl, m.toString(), true, token, 1);
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("财务系统消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+            return userRes.toJSONString();
+        } catch (Exception e) {
+            log.info(e.getMessage(), e);
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 更新消息响应到对应日志记录
+     *
+     * @param uuid     uuid
+     * @param response 消息响应
+     */
+    private static void updateMsgLog(String uuid, String response) {
+        try {
+            log.info("记录更新飞书推送结果数据日志");
+            QFilter qFilterCas = new QFilter("billno", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("nckd_log_feishumsg", "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, "nckd_log_feishumsg");
+                if (response.length() < 200) {
+                    dynamicObject.set("nckd_send_result_l", response);
+                } else {
+                    dynamicObject.set("nckd_send_result_l", response.substring(0, 200) + "...");
+                }
+                dynamicObject.set("nckd_send_result_l_tag", response);
+                SaveServiceHelper.update(dynamicObject);
+                log.info("记录更新飞书推送结果数据日志成功");
+            }
+        } catch (Exception e) {
+            log.info("记录更新飞书推送结果数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * 保存飞书消息日志
+     *
+     * @param userid  用户id
+     * @param title   标题
+     * @param content 内容
+     * @param m       发送消息体
+     * @return uuid
+     */
+    private static String saveMsgLog(String userid, String title, String content, JSONObject m) {
+        try {
+            log.info("记录新增飞书推送数据日志");
+            String uuid = UUIDUtil.generateUuid();
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("nckd_to_userid", userid);
+            dynamicObject.set("nckd_msg_title", title);
+            String msgContent = m.toString();
+            if (msgContent.length() < 200) {
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            } else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+            dynamicObject.set("nckd_msg_content", content);
+            dynamicObject.set("nckd_send_time", new Date());
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            log.info("记录新增飞书推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            log.info("记录新增飞书推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    //pc链接特殊处理
+    public static String pcSrcToFeishu(String src) {
+        String dandianStr = "";
+        try {
+            String encodesrc = URLEncoder.encode(src, "utf-8");
+            dandianStr = "https://applink.feishu.cn/client/web_url/open?mode=window&url=" + encodesrc;
+        } catch (UnsupportedEncodingException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return dandianStr;
+    }
+
+
+    public static String changeDanDianSrc(String src, String appId) {
+
+        String dandianStr = "";
+        try {
+            // 初始化参数
+//            initParams();
+            String encodesrc = URLEncoder.encode(src, "utf-8");
+            dandianStr = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=" + encodesrc + "&app_id=" + appId;
+        } catch (UnsupportedEncodingException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return dandianStr;
+    }
+
+    /**
+     * 转换erp任务状态--》飞书任务状态
+     * erp 状态:// 已同意	approve
+     *         // 已驳回	reject
+     *         // 已终止	terminate
+     *         // 已处理	handled
+     *         // 被驳回	dismissed
+     *         // 待审批	willApproval
+     *         // 已冻结	freeze
+     *         // 待处理	willHandled
+     *         // 已挂起	manualSuspended
+     *         // 已转换	converted
+     *         // 转换中	converting
+     *         // 待转换	unConverted
+     * 飞书task状态:PENDING: 待审批
+     *              APPROVED: 任务同意
+     *              REJECTED: 任务拒绝
+     *              TRANSFERRED: 任务转交
+     *              DONE: 任务通过但审批人未操作
+     * @param handleState
+     * @return
+     */
+    public static String getFeishuTaskStateByTaskHandleState(String handleState){
+        if ("willHandled".equalsIgnoreCase(handleState) || "willApproval".equalsIgnoreCase(handleState)){
+            return "PENDING";
+        }else if ("reject".equalsIgnoreCase(handleState) || "dismissed".equalsIgnoreCase(handleState)){
+            return "REJECTED";
+        }else if ("converted".equalsIgnoreCase(handleState) || "converting".equalsIgnoreCase(handleState) || "unConverted".equalsIgnoreCase(handleState)){
+            return "TRANSFERRED";
+        }else if ("approve".equalsIgnoreCase(handleState) || "handled".equalsIgnoreCase(handleState)){
+            return "APPROVED";
+        }else {
+            return "DONE";
+        }
+    }
+    // 邦财通上线时间
+    private static final String TARGET_DATESTR = "2022-10-24 00:00:00";
+    /**
+     * 判断是否是旧版本
+     * @param messageContext
+     * @return
+     */
+    public static boolean isBehindVersion(MessageContext messageContext){
+        try {
+            Date targetDate = DateUtil.parse(TARGET_DATESTR, "yyyy-MM-dd HH:mm:ss");
+            if (targetDate.getTime() > messageContext.getCreateDate().getTime()){
+                // 小于目标时间
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        // 新版
+        return false;
+    }
+
+    public static boolean isBehindVersion(Date source){
+        try {
+            Date targetDate = DateUtil.parse(TARGET_DATESTR, "yyyy-MM-dd HH:mm:ss");
+            if (targetDate.getTime() > source.getTime()){
+                // 小于目标时间
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    // 大鲸上线时间
+    private static final String TARGET_DATESTR_DJ = "2022-11-02 09:00:00";
+
+    /**
+     * 判断是否是旧版本
+     * @param messageContext
+     * @return
+     */
+    public static boolean isBehindVersionDJ(MessageContext messageContext){
+        try {
+            Date targetDate = DateUtil.parse(TARGET_DATESTR_DJ, "yyyy-MM-dd HH:mm:ss");
+            if (targetDate.getTime() > messageContext.getCreateDate().getTime()){
+                // 小于目标时间
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        // 新版
+        return false;
+    }
+
+    public static boolean isBehindVersionDJ(Date source){
+        try {
+            Date targetDate = DateUtil.parse(TARGET_DATESTR_DJ, "yyyy-MM-dd HH:mm:ss");
+            if (targetDate.getTime() > source.getTime()){
+                // 小于目标时间
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+}

+ 64 - 0
main/java/kd/cosmic/jkjt/msg/feishu/SSLClient.java

@@ -0,0 +1,64 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+public class SSLClient extends DefaultHttpClient
+{
+    public SSLClient()
+            throws Exception
+    {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        X509TrustManager tm = new X509TrustManager()
+        {
+            @Override
+            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
+                    throws CertificateException
+            {
+            }
+            @Override
+            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
+            {
+            }
+            @Override
+            public X509Certificate[] getAcceptedIssuers()
+            {
+                return null;
+            }
+        };
+        sslContext.init(null, new TrustManager[] { tm }, null);
+        SSLSocketFactory ssf = new SSLSocketFactory(sslContext, new X509HostnameVerifier()
+        {   @Override
+            public boolean verify(String s, SSLSession sslSession) {
+                return true;
+            }
+            @Override
+            public void verify(String host, SSLSocket ssl)
+                    throws IOException
+            {
+            }
+            @Override
+            public void verify(String host, X509Certificate cert)
+                    throws SSLException
+            {
+            }
+            @Override
+            public void verify(String host, String[] cns, String[] subjectAlts)
+                    throws SSLException
+            {
+            }
+        });
+        ClientConnectionManager ccm = getConnectionManager();
+        SchemeRegistry sr = ccm.getSchemeRegistry();
+        sr.register(new Scheme("https", 443, ssf));
+    }
+}

+ 420 - 0
main/java/kd/cosmic/jkjt/msg/feishu/TestChangeMessageStatePlugin.java

@@ -0,0 +1,420 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.bec.api.IEventServicePlugin;
+import kd.bos.bec.model.EntityEvent;
+import kd.bos.bec.model.JsonEvent;
+import kd.bos.bec.model.KDBizEvent;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.engine.WfUtils;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import org.apache.commons.lang.time.DateFormatUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.rmi.ConnectException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2024/04/22
+ * 插件说明:业务事件中心插件,修改飞书待办状态,测试类
+ * 表单标识:消息阅读状态变更事件.执行插件(wf.AfterChangeMessageStateEvent.executePlugin)
+ */
+public class TestChangeMessageStatePlugin implements IEventServicePlugin {
+    private static Log logger = LogFactory.getLog(TestChangeMessageStatePlugin.class);
+    private static final String USER_FORM_ID = "bos_user";
+    private static final String USERNAME = "username";
+    private List<Integer> interErrCodeArr = Arrays.asList(new Integer[]{
+            65001,90203,90235,90242,91201,95001,95003,95005,95010,95011,
+            95203,95204,95205,95206,95207,95208,95209,
+            105001,190003,230020,1050002,1050008,1069301,1069399
+    });
+
+    public static final String createInstanceUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+    private static final String TRAGETNAME = "云苍穹移动审批";
+    private static final String SPDYNUMBER = "jxjkxh";//审批定义number
+    private static final String SPDYID = "946F4A46-2D64-4EBF-9C83-5CDBC5FEB9BD";//审批定义id-测试
+    private static final String APP_ID = "cli_a6f0c94f273ed00b";//个人测试
+    private static final String APP_SERCRET = "WmQUMiDFFSjN60kKnsayvdaEd0wvqidh";
+
+    @Override
+    public Object handleEvent(KDBizEvent evt) {
+        logger.info("飞书消息阅读状态变更事件" + evt.toString());
+
+        if (evt instanceof EntityEvent) {//苍穹事件
+            EntityEvent entityEvent = (EntityEvent) evt;//类型转换
+            String businesskey = entityEvent.getBusinesskeys().get(0);
+            String entityNumber = entityEvent.getEntityNumber();
+            DynamicObject obj = BusinessDataServiceHelper.loadSingle(businesskey, entityNumber);
+            Long evtID = evt.getEventId();
+            String source = evt.getSource();//传递的事件参数
+        } else {//自定义事件
+            JsonEvent jsonEvent = (JsonEvent) evt;//类型转换
+            String source = jsonEvent.getSource();//传递的事件参数
+            if (WfUtils.isNotEmpty(source)) {
+                JSONArray arr = (JSONArray) JSON.parse(source);
+                for (int i = 0; i < arr.size(); i++) {
+                    if(arr.getJSONObject(i) == null) {
+                        continue;
+                    }
+                    //消息id
+                    String msgIds = arr.getJSONObject(i).getString("msgIds");
+
+                    logger.info("飞书消息阅读状态变更事件:msgIds" + msgIds);
+
+                    //用户id
+                    String userId = arr.getJSONObject(i).getString("userId");
+
+                    if(StringUtils.isEmpty(msgIds)){
+                        continue;
+                    }
+
+                    updateFeiShuMsgState(userId, msgIds);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void updateFeiShuMsgState(String userId, String msgIds){
+        QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(userId));
+        //接收人员
+        DynamicObject user = BusinessDataServiceHelper.loadSingle("bos_user", qFilter.toArray());
+        String personNo = user.getString("number");
+
+        msgIds = msgIds.replace("[", "").replace("]", "");
+        String[] msgIdList = msgIds.split(",");
+        if(msgIdList.length == 0){
+            return;
+        }
+
+        for(String msgId : msgIdList){
+            logger.info("飞书消息阅读状态变更事件:替换前 msgId:" + msgId);
+            msgId = msgId.replace("\"", "");
+            logger.info("飞书消息阅读状态变更事件:替换后msgId:" + msgId);
+
+            qFilter = new QFilter("channel", QCP.equals, "feishu");
+            qFilter.and(new QFilter("messageid", QCP.equals, Long.valueOf(msgId)));
+            DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+            if(msgFail == null){
+                continue;
+            }
+            qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgId));
+            DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+            if(msg == null || StringUtils.isEmpty(msg.getString("contenturl"))){
+                continue;
+            }
+
+            Long channelMsgId = msgFail.getLong("id");
+            String tplscene = msgFail.getString("tplscene");
+
+            logger.info("飞书消息阅读状态变更事件:tplscene:" + tplscene);
+
+            if("circulation".equals(tplscene)){
+                String token = getToken();
+
+                sendFlowMessage(channelMsgId.toString(), msg, user, token);
+            }
+
+
+        }
+    }
+
+    public void sendFlowMessage(String channelMsgId, DynamicObject message, DynamicObject userInfo, String token){
+        try {
+            JSONObject m = new JSONObject(true);
+            JSONObject c = new JSONObject(true);
+
+            c.put("approval_code", SPDYID);
+            c.put("instance_id","cq"+ channelMsgId);
+            c.put("status", "HIDDEN");
+
+            //url连接
+            JSONObject links = new JSONObject();
+
+            String pcUrl = StringUtils.isEmpty(message.getString("contenturl")) ? "" : message.getString("contenturl");
+            String mobUrl = StringUtils.isEmpty(message.getString("mobcontenturl")) ? pcUrl : message.getString("mobcontenturl");
+
+            logger.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+            if(StringUtils.isNotEmpty(pcUrl)){
+                pcUrl = pcUrl.replace(mobileUrl, curUrl);
+            }
+
+            if(StringUtils.isNotEmpty(mobUrl)){
+                mobUrl = mobUrl.replace(curUrl, mobileUrl);
+            }
+
+            if("".equals(pcUrl)||pcUrl==null){
+                pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+            }
+
+
+            links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+            c.put("links", links);
+
+            String content = message.getString("content");
+            String title = message.getString("title");
+            //标题
+            c.put("title", title);
+
+            Long createTime = (new Date()).getTime();
+
+            c.put("start_time", createTime);
+            c.put("update_time", createTime);
+            c.put("update_mode", "UPDATE");
+
+
+
+            /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+            String workflowname = "财务系统审批流";
+            String nodename = "";
+            String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            if(message != null && StringUtils.isNotEmpty(message.getString("config"))){
+                JSONObject messateContext = JSONObject.parseObject(message.getString("config")).getJSONObject("messageContext");
+                Long taskId = messateContext.getLong("taskId");
+                /*************处理消息格式*****************/
+                DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+                //add by wnaghaiwu_kd 2024/04/28
+                //如果任务中找不到,就找历史任务
+                if(taskInfo == null){
+                    taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+                }
+
+                if(taskInfo != null){
+                    QFilter qFilter;
+                    nodename = taskInfo.getString("name");
+
+                    if(taskInfo.getLong("processdefinitionid") > 0) {
+                        qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                        DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                        if (processdef != null) {
+                            workflowname = processdef.getString("name");
+                        }
+                    }
+
+                    if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                        workflowname = taskInfo.getString("entityname") + "审批流程";
+                    }
+                    if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                            && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                        qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                        DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                        if(bill != null){
+                            if(bill.getDynamicObjectType().getProperties().contains("createtime")) {
+                                createDate = DateFormatUtils.format(bill.getDate("createtime"), "yyyy-MM-dd HH:mm:ss");
+                            } else if(bill.getDynamicObjectType().getProperties().contains("createdate")) {
+                                createDate = DateFormatUtils.format(bill.getDate("createdate"), "yyyy-MM-dd HH:mm:ss");
+                            }
+                        }
+
+                    }
+                }
+            }
+
+            c.put("title", workflowname);
+
+            JSONArray formArr = new JSONArray();
+            JSONObject form1 = new JSONObject();
+            form1.put("name", "流程标题");
+            form1.put("value", StringUtils.isEmpty(message.getString("title")) ? workflowname : message.getString("title"));
+            formArr.add(form1);
+
+            JSONObject form2 = new JSONObject();
+            form2.put("name", "提单时间");
+            form2.put("value", createDate);
+            formArr.add(form2);
+
+            JSONObject form3 = new JSONObject();
+            form3.put("name", "当前节点");
+            form3.put("value", nodename);
+            formArr.add(form3);
+
+            c.put("form", formArr);
+
+            /*************处理消息格式*****************/
+
+            //cc_list	抄送人相关
+            JSONArray ccArr = new JSONArray();
+
+            JSONObject cc = new JSONObject();
+            cc.put("cc_id", userInfo.getPkValue()+"_"+ channelMsgId);
+            cc.put("open_id", getUserId(userInfo.getString("phone"),token));
+            cc.put("read_status", "READ");
+            cc.put("title", title);
+            cc.put("create_time", createTime);
+            cc.put("update_time", createTime);
+
+            cc.put("title", workflowname);
+
+            JSONObject links1 = new JSONObject();
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+            cc.put("links", links1);
+
+            ccArr.add(cc);
+
+            c.put("cc_list", ccArr);
+
+            m.put("content", c);
+
+            logger.info("财务系统飞书推送传阅状态更新:"+m.toJSONString());
+
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token);
+
+            logger.info("财务系统飞书推送传阅状态更新:"+response);
+        } catch (ConnectException e) {
+            logger.info("财务系统飞书,推送失败");
+            logger.error(e.getMessage(), e);
+            e.printStackTrace();
+        }
+    }
+
+    //pc链接特殊处理
+    public String pcSrcToFeishu(String src){
+        String dandianStr="";
+        if(dandianStr != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://applink.feishu.cn/client/web_url/open?mode=window&url=" + encodesrc;
+            } catch (UnsupportedEncodingException e) {
+                logger.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    public static String changeDanDianSrc(String src){
+        String dandianStr="";
+        if(src != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=" + encodesrc + "&app_id=" + APP_ID;
+            } catch (UnsupportedEncodingException e) {
+                logger.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+    /**
+     * 获取飞书token
+     * @return
+     */
+    public static String getToken(){
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        tokenJson.put("app_id",APP_ID);
+        tokenJson.put("app_secret",APP_SERCRET);
+        try {
+            String returnSt =  doPostByHttpClient(url,tokenJson.toJSONString(),false,null);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if("0".equals(returnJson.getString("code"))){//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (ConnectException e) {
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            logger.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+
+            logger.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    public static String doPostByHttpClient(String url, String data, boolean isBearer,String token) throws ConnectException {
+        System.out.println("url =" + url);
+        System.out.println("data =" + data);
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            DefaultHttpClient httpClient = null;
+            if (url.toLowerCase().startsWith("https://")) {
+                System.out.println("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                System.out.println("use nonssl");
+                httpClient = new DefaultHttpClient();
+            }
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if(isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+            String body = EntityUtils.toString(response.getEntity());
+            System.out.println(body);
+            return body;
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println(" ===== doPostByHttpClient() ERROR ===== ");
+            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+}

+ 2172 - 0
main/java/kd/cosmic/jkjt/msg/feishu/TestFeiShuDaiBanServiceHandler.java

@@ -0,0 +1,2172 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.dataentity.metadata.clr.DataEntityPropertyCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.BasedataEntityType;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.workflow.WorkflowServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.bos.workflow.component.approvalrecord.IApprovalRecordItem;
+import kd.bos.workflow.engine.WfUtils;
+import kd.bos.workflow.engine.impl.persistence.entity.task.component.ApprovalRecordGroup;
+import kd.bos.workflow.engine.msg.AbstractMessageServiceHandler;
+import kd.bos.workflow.engine.msg.ctx.MessageContext;
+import kd.bos.workflow.engine.msg.info.LinkMessageInfo;
+import kd.bos.workflow.engine.msg.info.MessageInfo;
+import kd.bos.workflow.engine.msg.info.ToDoInfo;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.rmi.ConnectException;
+import java.sql.Timestamp;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 测试环境类
+ * 插件说明:飞书待办类,
+ */
+public class TestFeiShuDaiBanServiceHandler extends AbstractMessageServiceHandler {
+
+    public TestFeiShuDaiBanServiceHandler() {
+
+    }
+
+    private static final Logger log = LoggerFactory.getLogger(TestFeiShuDaiBanServiceHandler.class);
+
+    /**
+     * 人员表标识
+     */
+    private static final String USER_FORM_ID = "bos_user";
+    /**
+     * 用户名
+     */
+    private static final String USERNAME = "username";
+
+    private List<Integer> interErrCodeArr = Arrays.asList(new Integer[]{
+            65001,90203,90235,90242,91201,95001,95003,95005,95010,95011,
+            95203,95204,95205,95206,95207,95208,95209,
+            105001,190003,230020,1050002,1050008,1069301,1069399
+    });
+
+    public static final String createInstanceUrl = "https://www.feishu.cn/approval/openapi/v2/external/instance/create";
+    private static final String TRAGETNAME = "云苍穹移动审批";
+    private static final String SPDYNUMBER = "jxjkxh";//审批定义number
+    private static final String SPDYID = "946F4A46-2D64-4EBF-9C83-5CDBC5FEB9BD";//审批定义id-测试
+    private static final String APP_ID = "cli_a786040738b5d00d";//个人测试
+    private static final String APP_SERCRET = "WCHnL9EIs654PxRtdMFLVeMB30EwUFOJ";
+
+
+    public static String doPostByHttpClient(String url, String data, boolean isBearer,String token, int times) throws ConnectException {
+        System.out.println("url =" + url);
+        System.out.println("data =" + data);
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            DefaultHttpClient httpClient = null;
+            if (url.toLowerCase().startsWith("https://")) {
+                System.out.println("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                System.out.println("use nonssl");
+                httpClient = new DefaultHttpClient();
+            }
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if(isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+            String body = EntityUtils.toString(response.getEntity());
+            System.out.println(body);
+            return body;
+        } catch (Exception e) {
+            String errMsg = e.getMessage() == null ? "未知异常" : e.getMessage();
+            log.error("请求url " + url + " 超时", errMsg);
+            // 执行重试
+            if (times > 0){
+                times--;
+                if (times > 3){
+                    times = 3;
+                }
+                return doPostByHttpClient(url, data, isBearer, token, times);
+            }else {
+                throw new ConnectException("请求飞书接口失败:" + errMsg);
+            }
+
+//            e.printStackTrace();
+//            System.out.println(" ===== doPostByHttpClient() ERROR ===== ");
+//            throw new ConnectException(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+
+    /**
+     * 获取飞书token
+     * @return
+     */
+    public static String getToken(){
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        tokenJson.put("app_id",APP_ID);
+        tokenJson.put("app_secret",APP_SERCRET);
+        try {
+            String returnSt =  doPostByHttpClient(url,tokenJson.toJSONString(),false,null, 3);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if("0".equals(returnJson.getString("code"))){//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserIdTrue(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
+        try {
+            log.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            log.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+
+    @Override
+    public void createToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+        log.info("财务系统飞书,推送待办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+
+
+        List<Long> userIds = toDoInfo.getUserIds();
+        if (CollectionUtils.isEmpty(userIds)) {
+            log.info("财务系统飞书,人员为空,推送失败");
+            return;
+        }
+
+        QFilter qFilter = new QFilter("id", QCP.in, userIds);
+        DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+        String token = getToken();
+
+
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+        String status="PENDING";
+        String fixName= "";
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        if("willApproval".equals(taskInfo.getString("handlestate"))){//待审批
+
+        }else if("dismissed".equals(taskInfo.getString("handlestate"))){//驳回
+            status="REJECTED";
+            //驳回新增的待办。不新增。特殊处理
+            List allApprovalRecord = WorkflowServiceHelper.getAllApprovalRecord(messageContext.getBusinessKey());
+            if(allApprovalRecord.size()>2){
+                IApprovalRecordItem item = ((ApprovalRecordGroup) allApprovalRecord.get(allApprovalRecord.size()-2)).getChildren().get(0);
+
+                String taskid = item.getTaskId();
+                String isreject = item.getDecisionType();
+                String submitMsg = item.getMessage();
+
+                if("reject".equals(isreject)){
+                    fixName = "(被驳回)";
+                    String content =toDoInfo.getContent();
+
+                    Map<String, String> logMaps = new HashMap<>();
+
+                    logMaps.put("nckd_logtype", "reject");
+                    logMaps.put("nckd_apiurl", createInstanceUrl);
+
+                    String reveiceUserIds = userIds.stream().map(Object::toString) // 将Long转换为String
+                            .collect(Collectors.joining(",")); // 用逗号连接所有String元素
+
+                    logMaps.put("nckd_receiveuserids", reveiceUserIds);
+
+                    JSONObject m = getBoHuiJson(messageContext,toDoInfo,load,token,true,status,content,taskid, logMaps);
+                    try {
+
+                        log.info("财务系统飞书驳回待办:"+m.toJSONString());
+
+
+
+                        String uuid = saveMsgLogNew(userIds.get(0).toString(), content, "财务系统飞书更新待办[DaJingDaiBanServiceHandler.createToDoreject]", m, logMaps);
+
+                        String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+                        log.info("财务系统飞书驳回待办结果:"+response);
+
+                        JSONObject res = JSONObject.parseObject(response);
+                        if (interErrCodeArr.contains(res.getIntValue("code"))){
+                            // 内部错误,可重试
+                            Thread.sleep(500L);
+                            response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                            log.info("财务系统飞书更新待办结果二次:" + response);
+                        }
+
+                        if (!ObjectUtils.isEmpty(uuid)) {
+                            // 将发送消息的结果回填到数据库日志记录
+                            updateMsgLog(uuid, response);
+                        }
+
+
+                        //发送消息
+                        for (int i = 0; i < load.length ; i++) {
+                            String userid =  getUserIdTrue(load[i].getString("phone"),token);
+                            String title = toDoInfo.getTitle();
+                            String node = " ";
+                            if(messageContext.getEntityName()!=null&&!"".equals(messageContext.getEntityName())){
+                                int firstindex = content.indexOf(messageContext.getEntityName());
+                                if(firstindex>=0){
+                                    int subIndex = firstindex+messageContext.getEntityName().length();
+//                                    content =content.substring(0,subIndex)+"\n"+content.substring(subIndex);
+                                    content =content.substring(subIndex);
+                                    if("".equals(content)){
+                                        content =toDoInfo.getContent();
+                                    }
+                                }
+                            }
+                            sendFeishuMessage("1012", submitMsg, toDoInfo.getTaskId(), userid, load[i].getPkValue().toString() ,title,content,node,token,changeDanDianSrc(pcUrl+"&apptype=feishu"),changeDanDianSrc( mobUrl+"&apptype=feishu"));
+                        }
+
+                    } catch (ConnectException e) {
+                        log.info("财务系统飞书,推送失败");
+                        log.error(e.getMessage(), e);
+                        e.printStackTrace();
+                    } catch (InterruptedException e1) {
+                        log.info("财务系统飞书,推送失败");
+                        log.error(e1.getMessage(), e1);
+                        throw new RuntimeException(e1);
+                    }
+                }
+            }
+
+//            return ;
+        }
+
+        Map<String, String> logMaps = new HashMap<>();
+
+        logMaps.put("nckd_logtype", "createToDo");
+        logMaps.put("nckd_apiurl", createInstanceUrl);
+
+        String reveiceUserIds = userIds.stream().map(Object::toString) // 将Long转换为String
+                .collect(Collectors.joining(",")); // 用逗号连接所有String元素
+
+        logMaps.put("nckd_receiveuserids", reveiceUserIds);
+
+        JSONObject m = getToDaiBanJson(messageContext,toDoInfo,load,token,false,"PENDING",null, fixName, logMaps);
+        try {
+
+            log.info("财务系统飞书推送待办:"+m.toJSONString());
+
+
+
+            String title = toDoInfo.getTitle();
+
+            String uuid = saveMsgLogNew(userIds.get(0).toString(), title, "财务系统飞书更新待办[DaJingDaiBanServiceHandler.createToDo]", m, logMaps);
+
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书推送待办结果:"+response);
+
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+                response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                log.info("财务系统飞书推送待办结果:" + response);
+            }
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+
+            if("".equals(fixName)) {
+                //发送消息
+                for (int i = 0; i < load.length; i++) {
+                    String userid = getUserIdTrue(load[i].getString("phone"), token);
+//               String title = "邦财通消息:"+toDoInfo.getTitle();
+
+                    String content = toDoInfo.getContent();
+                    if (messageContext.getEntityName() != null && !"".equals(messageContext.getEntityName())) {
+                        int firstindex = content.indexOf(messageContext.getEntityName());
+                        if (firstindex >= 0) {
+                            int subIndex = firstindex + messageContext.getEntityName().length();
+//                       content =content.substring(0,subIndex)+"\n"+content.substring(subIndex);
+                            content = content.substring(subIndex);
+                            if ("".equals(content)) {
+                                content = toDoInfo.getContent();
+                            }
+                        }
+                    }
+                    String node = " ";
+                    title = toDoInfo.getTitle();
+                    sendFeishuMessage("1008", "", toDoInfo.getTaskId(), userid, load[i].getPkValue().toString(), title, content, node, token, changeDanDianSrc(pcUrl + "&apptype=feishu"), changeDanDianSrc(mobUrl + "&apptype=feishu"));
+                }
+            }
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        } catch (InterruptedException e1) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e1.getMessage(), e1);
+            throw new RuntimeException(e1);
+        }
+
+    }
+
+    public String sendDaiBanFeishuMessage(String userid,String title,String content,String node,String token,String pcurl,String phoneurl){
+
+        JSONObject m = new JSONObject();
+
+        m.put("template_id","1008");
+        m.put("user_id",userid);
+        m.put("approval_name","@i18n@1");
+        m.put("note","@i18n@3");
+
+        JSONArray summaries = new  JSONArray();
+        JSONObject summarie = new  JSONObject();
+        summarie.put("summary","@i18n@2");
+        summaries.add(summarie);
+        JSONObject contentjson = new  JSONObject();
+
+        contentjson.put("summaries",summaries);
+        m.put("content",contentjson);
+
+        JSONArray actions = new JSONArray();
+        JSONObject action = new JSONObject();
+        action.put("action_name","DETAIL");
+        action.put("url",pcSrcToFeishu(pcurl));
+        action.put("android_url",phoneurl);
+        action.put("ios_url",phoneurl);
+        action.put("pc_url",pcSrcToFeishu(pcurl));
+        actions.add(action);
+        m.put("actions",actions);
+
+        JSONArray i18n_resources = new  JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale","en-US");
+        res.put("is_default",true);
+        JSONObject textsjson = new JSONObject();
+
+        String realtitle ="";
+        if(title!=null){
+            int index = title.indexOf("请处理");
+            if(index==0){
+                realtitle = title.substring(3);
+            }
+        }
+
+
+        textsjson.put("@i18n@1",realtitle);
+        textsjson.put("@i18n@2",content);
+        textsjson.put("@i18n@3",node);
+        res.put("texts",textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+        String userIdUrl="https://www.feishu.cn/approval/openapi/v1/message/send";
+        try {
+            log.info("门户发送消息:"+m.toString());
+            //飞书推送数据日志
+            log.info("飞书推送数据日志");
+
+            Map<String, String> logMaps = new HashMap<>();
+
+            logMaps.put("nckd_logtype", "other");
+            logMaps.put("nckd_apiurl", userIdUrl);
+
+            logMaps.put("nckd_receiveuserids", userid);
+
+//            String uuid = saveMsgLog(userid, title, content, m);
+
+            String uuid = saveMsgLogNew(userid, title, "财务系统飞书其他消息", m, logMaps);
+
+            log.info("飞书推送数据日志保存成功");
+
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户发送消息结果:"+response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+
+    public String sendFeishuMessage(String templateid, String submitMsg, Long taskId, String userid, String userpk,String title,String content,String node,String token,String pcurl,String phoneurl){
+
+        JSONObject m = new JSONObject();
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+        }
+
+        //发起人
+        String startid = "";
+        if(taskInfo != null){
+            Long faqirenid = taskInfo.getLong("starterid");
+            if(faqirenid!=null){
+                DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                startid = getUserIdTrue(faqiren.getString("phone"),token);
+            }
+            title = taskInfo.getString("entityname");
+        }
+
+
+        m.put("template_id", templateid);
+        m.put("user_id", userid);
+
+        m.put("note","@i18n@3");
+
+        if("1021".equals(templateid)){
+            m.put("custom_title","@i18n@1");
+            m.put("custom_content","@i18n@2");
+
+            JSONArray actions = new JSONArray();
+            JSONObject action = new JSONObject();
+            action.put("action_name","@i18n@4");
+            action.put("url",pcSrcToFeishu(pcurl));
+            action.put("android_url",phoneurl);
+            action.put("ios_url",phoneurl);
+            action.put("pc_url",pcSrcToFeishu(pcurl));
+            actions.add(action);
+            m.put("actions",actions);
+        } else {
+            String kduuid = UUID.randomUUID().toString().replace("-", "");
+            m.put("uuid", kduuid);
+
+            m.put("title_user_id_type", "user_id");
+            m.put("title_user_id", startid);
+            m.put("approval_name", "@i18n@1");
+
+            JSONObject contentobj = new JSONObject();
+            contentobj.put("user_id", startid);
+            contentobj.put("user_id_type", "user_id");
+
+            JSONArray summarys = new JSONArray();
+            JSONObject summary1 = new JSONObject();
+            summary1.put("summary", "@i18n@2");
+
+            summarys.add(summary1);
+//
+//        JSONObject summary2 = new JSONObject();
+//        summary2.put("summary", "测试内容2");
+//        summarys.add(summary2);
+
+            contentobj.put("summaries", summarys);
+            m.put("content", contentobj);
+
+            if("1012".equals(templateid) || "1016".equals(templateid)) {
+                m.put("comment", "@i18n@4");
+            }
+
+            JSONArray actions = new JSONArray();
+            JSONObject action = new JSONObject();
+            action.put("action_name","DETAIL");
+            action.put("url",pcSrcToFeishu(pcurl));
+            action.put("android_url",phoneurl);
+            action.put("ios_url",phoneurl);
+            action.put("pc_url",pcSrcToFeishu(pcurl));
+            actions.add(action);
+            m.put("actions",actions);
+        }
+
+
+
+        JSONArray i18n_resources = new  JSONArray();
+        JSONObject res = new JSONObject();
+        res.put("locale","en-US");
+        res.put("is_default",true);
+        JSONObject textsjson = new JSONObject();
+        textsjson.put("@i18n@1",title);
+        textsjson.put("@i18n@2",content);
+        textsjson.put("@i18n@3", "来自财务系统");
+
+        if("1021".equals(templateid)){
+            textsjson.put("@i18n@4","查看详情");
+        } else if("1012".equals(templateid) || "1016".equals(templateid)) {
+            textsjson.put("@i18n@4", submitMsg);
+        }
+
+        res.put("texts",textsjson);
+        i18n_resources.add(res);
+        m.put("i18n_resources", i18n_resources);
+
+
+//        String userIdUrl="https://www.feishu.cn/approval/openapi/v1/message/send";
+        String userIdUrl="https://open.feishu.cn/open-apis/approval/v1/message/send";
+        try {
+            log.info("门户发送消息:"+m.toString());
+
+            Map<String, String> logMaps = new HashMap<>();
+
+            logMaps.put("nckd_logtype", "bot");
+            logMaps.put("nckd_apiurl", userIdUrl);
+            logMaps.put("nckd_receiveuserids", userpk);
+            logMaps.put("nckd_to_useridfeishu", userid);
+
+//            String uuid = saveMsgLog(userid, title, content, m);
+            String uuid = saveMsgLogNew(userpk, title, "财务系统飞书发送审批Bot消息", m, logMaps);
+
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 发送带链接的消息,发送至审批应用
+     * @param ctx
+     * @param message
+     * @param userid
+     * @param token
+     */
+    public void sendLinkMessage(MessageContext ctx, MessageInfo message, String userid, String userpk, String token, String templateid){
+        String node = " ";
+        String pcurl  = message.getContentUrl();
+        String moburl  = message.getMobContentUrl();
+        if("".equals(pcurl)||pcurl==null){
+            pcurl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+        }
+        if("".equals(moburl)||moburl==null){
+            moburl = System.getProperty("domain.contextUrl")+"/mobile.html?form=er_mainpage_daily";
+            moburl = changeDanDianSrc(moburl+"&apptype=feishu");
+        }else{
+            moburl = changeDanDianSrc(moburl+"&apptype=feishu");
+        }
+        pcurl =changeDanDianSrc(pcurl+"&apptype=feishu");
+
+        String content = message.getContent();
+        if("".equals(content)||content==null){
+            content = message.getTitle();
+        }
+
+        //替换PC端域名
+        pcurl = replacePcUrlDomain(pcurl);
+        //移动端替换域名
+        moburl = replaceMobUrlDomain(moburl);
+
+        /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+        Long taskId = 0L;
+
+        QFilter qFilter = new QFilter("channel", QCP.equals, "feishu");
+        qFilter.and(new QFilter("id", QCP.equals, Long.valueOf(message.getChannelMsgId())));
+        DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+        if(msgFail != null){
+            qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgFail.getLong("messageid")));
+
+            DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+            if(msg != null && StringUtils.isNotEmpty(msg.getString("config"))){
+                JSONObject messateContext = JSONObject.parseObject(msg.getString("config")).getJSONObject("messageContext");
+                if(messateContext != null && messateContext.get("taskId") != null) {
+                    taskId = messateContext.getLong("taskId");
+                }
+            }
+        }
+
+        log.info("pcurl:"+ pcurl);
+        log.info("mobileurl:" + moburl);
+        sendFeishuMessage(templateid, "", taskId, userid, userpk,message.getTitle(),content,node,token,pcurl,moburl);
+    }
+
+    @Override
+    public void sendLinkMessage(MessageContext ctx, LinkMessageInfo linkMessage) {
+        super.sendLinkMessage(ctx, linkMessage);
+    }
+
+    /**
+     * 发送传阅消息,发送至抄送页签
+     * @param ctx
+     * @param message
+     * @param userid
+     * @param token
+     */
+    public void sendFlowMessage(MessageContext ctx, MessageInfo message, String userid, String token){
+        try {
+            JSONObject m = new JSONObject(true);
+            JSONObject c = new JSONObject(true);
+
+            String channelMsgId = message.getChannelMsgId() + "";
+
+            c.put("approval_code", SPDYID);
+            c.put("instance_id","cq"+ channelMsgId);
+            c.put("status", "HIDDEN");
+            c.put("display_method", "SIDEBAR");
+            List<Long> userIds = message.getUserIds();
+
+
+            //url连接
+            JSONObject links = new JSONObject();
+
+            String pcUrl = message.getContentUrl() == null ? "" : message.getContentUrl();
+            String mobUrl = message.getMobContentUrl() == null ? pcUrl : message.getMobContentUrl();
+
+            log.info("消息pcurl:" + pcUrl + ", moburl:" + mobUrl);
+
+            //移动端需要走外网,将地址替换成外网地址
+            String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+            String mobileUrl = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+            if(StringUtils.isNotEmpty(pcUrl)){
+                pcUrl = pcUrl.replace(mobileUrl, curUrl);
+            }
+
+            if(StringUtils.isNotEmpty(mobUrl)){
+                mobUrl = mobUrl.replace(curUrl, mobileUrl);
+            }
+
+            if("".equals(pcUrl)||pcUrl==null){
+                pcUrl = System.getProperty("domain.contextUrl")+"/index.html?formId=wf_msg_center";
+            }
+
+
+            links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+            c.put("links", links);
+
+            String content = message.getContent() == null ? message.getTitle() : message.getContent();
+            String title = message.getTitle();
+            //标题
+            c.put("title", title);
+
+            Long createTime = (new Date()).getTime();
+
+            c.put("start_time", createTime);
+            c.put("update_time", createTime);
+            c.put("update_mode", "UPDATE");
+
+
+            /*************处理消息格式*****************/
+//            String nodename = toDoInfo.getCategory();
+            String workflowname = "财务系统审批流";
+            String nodename = "";
+            String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+            QFilter qFilter = new QFilter("channel", QCP.equals, "feishu");
+            qFilter.and(new QFilter("id", QCP.equals, Long.valueOf(message.getChannelMsgId())));
+            DynamicObject msgFail = BusinessDataServiceHelper.loadSingle("wf_msg_failmessage", qFilter.toArray());
+
+            Map<String, String> logMaps = new HashMap<>();
+
+            logMaps.put("nckd_logtype", "flow");
+            logMaps.put("nckd_apiurl", createInstanceUrl);
+
+            String reveiceUserIds = userIds.stream().map(Object::toString) // 将Long转换为String
+                    .collect(Collectors.joining(",")); // 用逗号连接所有String元素
+
+            logMaps.put("nckd_receiveuserids", reveiceUserIds);
+
+            if(msgFail != null){
+                qFilter = new QFilter("id", QCP.equals, Long.valueOf(msgFail.getLong("messageid")));
+
+                DynamicObject msg = BusinessDataServiceHelper.loadSingle("wf_msg_message", qFilter.toArray());
+                if(msg != null && StringUtils.isNotEmpty(msg.getString("config"))){
+                    JSONObject messateContext = JSONObject.parseObject(msg.getString("config")).getJSONObject("messageContext");
+                    Long taskId = messateContext.getLong("taskId");
+                    Long faqirenid = messateContext.getLong("startUserId");
+                    /*************处理消息格式*****************/
+                    DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_task");
+
+                    //add by wnaghaiwu_kd 2024/04/28
+                    //如果任务中找不到,就找历史任务
+                    if(taskInfo == null){
+                        taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(taskId ,"wf_hitaskinst");
+                    }
+
+                    if(taskInfo != null){
+                        nodename = taskInfo.getString("name");
+
+                        if(taskInfo.getLong("processdefinitionid") > 0) {
+                            qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                            DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                            if (processdef != null) {
+                                workflowname = processdef.getString("name");
+                            }
+                        }
+
+                        if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                            workflowname = taskInfo.getString("entityname") + "审批流程";
+                        }
+                        if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                                && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                            qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                            DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                            if(bill != null){
+                                createDate = getCreateTime(bill);
+                            }
+
+                        }
+
+                        //发起人
+                        faqirenid = taskInfo.getLong("starterid");
+                    }
+
+                    if(faqirenid != null){
+                        DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                        c.put("open_id", getUserId(faqiren.getString("phone"),token));
+
+                        //发起人id、发起人飞书id
+                        logMaps.put("nckd_creatorid", faqirenid.toString());
+                        logMaps.put("nckd_creatoridfeishu", getUserId(faqiren.getString("phone"),token));
+                    }
+                }
+            }
+
+            c.put("title", workflowname);
+
+            JSONArray formArr = new JSONArray();
+            JSONObject form1 = new JSONObject();
+            form1.put("name", "流程标题");
+            form1.put("value", StringUtils.isEmpty(msgFail.getString("title")) ? workflowname : msgFail.getString("title"));
+            formArr.add(form1);
+
+            JSONObject form2 = new JSONObject();
+            form2.put("name", "提单时间");
+            form2.put("value", createDate);
+            formArr.add(form2);
+
+            JSONObject form3 = new JSONObject();
+            form3.put("name", "当前节点");
+            form3.put("value", nodename);
+            formArr.add(form3);
+
+            c.put("form", formArr);
+
+            /*************处理消息格式*****************/
+
+            //cc_list	抄送人相关
+            JSONArray ccArr = new JSONArray();
+
+
+            if (CollectionUtils.isEmpty(userIds)) {
+                log.info("财务系统飞书,人员为空,抄送失败");
+                return;
+            }
+
+            qFilter = new QFilter("id", QCP.in, userIds);
+            DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+            for (int i = 0; i <load.length ; i++) {
+                JSONObject cc = new JSONObject();
+                cc.put("cc_id", load[i].getPkValue()+"_"+ channelMsgId);
+                cc.put("open_id", getUserId(load[i].getString("phone"),token));
+                cc.put("read_status", "UNREAD");
+                cc.put("title", title);
+                cc.put("create_time", createTime);
+                cc.put("update_time", createTime);
+
+                cc.put("title", workflowname);
+
+//                cc.put("display_method", "SIDEBAR");
+
+                JSONObject links1 = new JSONObject();
+                links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+                links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+                cc.put("links", links1);
+
+                ccArr.add(cc);
+            }
+            c.put("cc_list", ccArr);
+
+            m.put("content", c);
+
+            log.info("财务系统飞书推送传阅:"+m.toJSONString());
+            if(load.length > 0) {
+                logMaps.put("nckd_to_useridfeishu", getUserId(load[0].getString("phone"), token));
+            }
+
+            String uuid = saveMsgLogNew(userIds.get(0).toString(), title, "财务系统飞书传同步阅消息[DaJingDaiBanServiceHandler.sendMessage]", m, logMaps);
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+
+            log.info("财务系统飞书推送传阅结果:"+response);
+
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+                response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                log.info("财务系统飞书更新待办结果二次:" + response);
+            }
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        } catch (InterruptedException e1) {
+            log.info("财务系统飞书,推送失败");
+            log.error(e1.getMessage(), e1);
+            throw new RuntimeException(e1);
+        }
+    }
+
+    /**
+     * 发送普通消息,发送至自建应用
+     * @param ctx
+     * @param info
+     * @param userid
+     * @param token
+     * @return
+     */
+    public String sendCustomMessage(MessageContext ctx, MessageInfo info, String userid, String token){
+        JSONObject m = new JSONObject();
+
+        String uuid = "KD-" + UUIDUtil.generateUuid();
+        String title = info.getTitle() == null ? "" : info.getTitle();
+        String contentText = info.getContent() == null ? "" : info.getContent();
+
+        //飞书不支持星瀚的换行标签,替换成飞书兼容的换行标签
+        contentText = contentText.replace("<a></a>", "\r\n");
+        contentText = contentText.replace("</a><a>", "\r\n");
+        contentText = contentText.replace("<a>", "");
+        contentText = contentText.replace("</a>", "");
+
+        m.put("receive_id", userid);
+        m.put("msg_type","text");
+
+        JSONObject content = new JSONObject();
+        content.put("text", contentText);
+        m.put("content", content.toJSONString());
+        m.put("uuid", uuid);
+
+        String linkUrl = info.getContentUrl();
+        String linkMobileUrl = info.getMobContentUrl();
+
+        DynamicObject msgInfo = BusinessDataServiceHelper.loadSingleFromCache(info.getId(), "wf_msg_message");
+        if(msgInfo != null) {
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkUrl = msgInfo.getString("contenturl");
+            }
+            if(StringUtils.isNotEmpty(msgInfo.getString("contenturl"))){
+                linkMobileUrl = msgInfo.getString("mobcontenturl");
+            }
+            if(StringUtils.isNotEmpty(linkMobileUrl)){
+                linkMobileUrl = linkUrl;
+            }
+        }
+
+        //替换PC端域名
+        linkUrl = replacePcUrlDomain(linkUrl);
+        //移动端替换域名
+        linkMobileUrl = replaceMobUrlDomain(linkMobileUrl);
+
+        String userIdUrl = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id";
+        try {
+            log.info("门户发送消息:"+m.toString());
+
+//            uuid = saveMsgLog(userid, title, content.toJSONString(), m);
+
+            Map<String, String> logMaps = new HashMap<>();
+
+            logMaps.put("nckd_logtype", "message");
+            logMaps.put("nckd_apiurl", userIdUrl);
+
+            logMaps.put("nckd_receiveuserids", userid);
+
+            uuid = saveMsgLogNew(userid, title, "财务系统飞书发送普通消息", m, logMaps);
+
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true,token, 3);
+            log.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+
+            JSONObject userRes = JSONObject.parseObject(response);
+            log.info("门户消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+            return userRes.toJSONString();
+        } catch (ConnectException e) {
+            log.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+
+    /**
+     * 获取取消JSON
+     * @return
+     */
+    public JSONObject getdeleteJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content,String taskId, Map<String, String> logMaps){
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+taskId);
+        c.put("status", "CANCELED");
+        c.put("display_method", "SIDEBAR");
+
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        if(taskInfo != null){
+            String handlestate = taskInfo == null ? "" : taskInfo.getString("handlestate");
+
+            //驳回状态的任务做提交处理时不做待办更新推送。
+            if("willApproval".equals(handlestate)){
+                c.put("status", "APPROVED");
+            }
+        }
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+        c.put("links", links);
+
+        //标题
+        c.put("title", toDoInfo.getContent());
+        if(isUpdate){
+            c.put("title", content);
+        }
+
+        //发起人
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("end_time", d.getTime());
+        c.put("update_time", d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        /*************处理消息格式*****************/
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+
+        //发起人
+
+        Long faqirenid = taskInfo.getLong("starterid");
+        if(faqirenid!=null){
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+            c.put("open_id", getUserId(faqiren.getString("phone"),token));
+
+            //发起人id、发起人飞书id
+            logMaps.put("nckd_creatorid", faqirenid.toString());
+            logMaps.put("nckd_creatoridfeishu", getUserId(faqiren.getString("phone"),token));
+        }
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id",load[i].getPkValue()+"_"+taskId);
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc( changephoneurl(mobUrl,taskId)));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "DONE");
+            }
+
+
+            task.put("title",  toDoInfo.getContent());
+            if(isUpdate){
+                task.put("title", content);
+            }
+
+            task.put("title", workflowname);
+
+            Date d1 = new Date();
+            task.put("create_time", d1.getTime());
+            task.put("end_time", d1.getTime());
+            task.put("update_time", d1.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+
+
+        return m;
+    }
+
+
+    /**
+     * 驳回JSON
+     * @return
+     */
+    public JSONObject getBoHuiJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content,String taskId, Map<String, String> logMaps){
+
+        boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+        if(!isExist){
+            return getdeleteJson(messageContext,toDoInfo,load,token,isUpdate,passStatus,content,taskId, logMaps);
+        }
+
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+taskId);
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link",changeDanDianSrc( changephoneurl(mobUrl,taskId)));
+        c.put("links", links);
+
+        //标题
+        if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+            c.put("title", toDoInfo.getContent());
+        }
+        if(isUpdate){
+            if(content!=null&&!"".equals(content)) {
+                c.put("title", content);
+            }
+        }
+        String oldcontent="";
+        if(toDoInfo.getContent()==null&&content==null){
+            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+            oldcontent =getTitle(messageContext,businessObject);
+            c.put("title", oldcontent);
+        }
+        //发起人
+
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("update_time",d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", "(被驳回)" + (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+taskId);
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(changephoneurl(mobUrl,taskId)) );
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+
+            if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+                task.put("title",  toDoInfo.getContent());
+            }
+            if(isUpdate){
+                if(content!=null&&!"".equals(content)) {
+                    task.put("title", content);
+                }
+            }
+            if(toDoInfo.getContent()==null&&content==null){
+                task.put("title", oldcontent);
+            }
+
+//            task.put("title", workflowname);
+
+            task.put("create_time", d.getTime());
+            task.put("end_time", d.getTime());
+            task.put("update_time", d.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+    public static String changephoneurl(String url,String taskId){
+        String returnurl="";
+        if(url==null){
+            return null;
+        }
+        int firstindex = url.indexOf("&tId=");
+        if(firstindex>0){
+            returnurl = url.substring(0,firstindex)+"&apptype=feishu";
+        }
+        return returnurl;
+    }
+
+    /**
+     * 待办变已办
+     * @return
+     */
+    public JSONObject getToYiBanJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,
+                                     boolean isUpdate,String passStatus,String content, Map<String, String> logMaps){
+
+        JSONObject m = new JSONObject(true);
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+toDoInfo.getTaskId());
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(changephoneurl(mobUrl,toDoInfo.getTaskId()+"")));
+        c.put("links", links);
+
+        //标题
+        if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+            c.put("title", toDoInfo.getContent());
+        }
+        if(isUpdate){
+            if(content!=null&&!"".equals(content)) {
+                c.put("title", content);
+            }
+        }
+        String oldcontent="";
+        if(toDoInfo.getContent()==null&&content==null){
+            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+            oldcontent =getTitle(messageContext,businessObject);
+            c.put("title", oldcontent);
+        }
+//        Long faqirenid = messageContext.getStartUserId();
+//        if(faqirenid!=null){
+//            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+//            c.put("open_id", FeishuUtil.getUserId(faqiren.getString("phone"),token));
+//        }
+
+        //发起人
+
+
+        /*************处理消息格式*****************/
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+
+        //add by wnaghaiwu_kd 2024/04/28
+        //如果任务中找不到,就找历史任务
+        if(taskInfo == null){
+            taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_hitaskinst");
+        }
+
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+
+            //发起人
+
+            Long faqirenid = taskInfo.getLong("starterid");
+            if(faqirenid!=null){
+                DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+                c.put("open_id", getUserId(faqiren.getString("phone"),token));
+
+                //发起人id、发起人飞书id
+                logMaps.put("nckd_creatorid", faqirenid.toString());
+                logMaps.put("nckd_creatoridfeishu", getUserId(faqiren.getString("phone"),token));
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle());
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+        Date d = new Date();
+
+        c.put("start_time", d.getTime());
+        c.put("update_time",d.getTime());
+        c.put("update_mode", "UPDATE");
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+toDoInfo.getTaskId());
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            //接收人飞书id
+            logMaps.put("nckd_to_useridfeishu", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(changephoneurl(mobUrl,toDoInfo.getTaskId()+"")));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+            if(toDoInfo.getContent()!=null&&!"".equals(toDoInfo.getContent())){
+                task.put("title",  toDoInfo.getContent());
+            }
+            if(isUpdate){
+                if(content!=null&&!"".equals(content)) {
+                    task.put("title", content);
+                }
+            }
+            if(toDoInfo.getContent()==null&&content==null){
+                task.put("title", oldcontent);
+            }
+
+            task.put("title", workflowname);
+
+            task.put("create_time", d.getTime());
+            task.put("end_time", d.getTime());
+            task.put("update_time", d.getTime());
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+
+
+    /**
+     * 获取创建审批实例json
+     * @return
+     */
+    public JSONObject getToDaiBanJson(MessageContext messageContext,ToDoInfo toDoInfo,DynamicObject[] load,String token,boolean isUpdate,String passStatus,String content, String fixName, Map<String, String> logMaps){
+        boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+        if(!isExist){
+            return getdeleteJson(messageContext,toDoInfo,load,token,isUpdate,passStatus,content,toDoInfo.getTaskId()+"", logMaps);
+        }
+        DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingle(toDoInfo.getTaskId() ,"wf_task");
+
+
+        JSONObject m = new JSONObject(true);
+
+
+
+        JSONObject c = new JSONObject(true);
+        c.put("approval_code", SPDYID);
+        c.put("instance_id","cq"+toDoInfo.getTaskId());
+
+        if(isUpdate){
+            if ("DONE".equalsIgnoreCase(passStatus)){
+                passStatus = "APPROVED";
+            }
+            c.put("status", passStatus);
+        }else{
+            c.put("status", "PENDING");
+        }
+
+        c.put("display_method", "SIDEBAR");
+
+        String pcUrl = toDoInfo.getUrl();
+        String mobUrl = toDoInfo.getUrl();
+
+        //替换PC端域名
+        pcUrl = replacePcUrlDomain(pcUrl);
+        //移动端替换域名
+        mobUrl = replaceMobUrlDomain(mobUrl);
+
+        //url连接
+        JSONObject links = new JSONObject();
+        links.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+        links.put("mobile_link", changeDanDianSrc(mobUrl+"&apptype=feishu"));
+        c.put("links", links);
+
+        //标题
+        c.put("title", toDoInfo.getContent());
+        if(isUpdate){
+            c.put("title", content);
+        }
+
+        /*************处理消息格式*****************/
+        String nodename = toDoInfo.getCategory();
+        String workflowname = "财务系统审批流";
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+
+        if(taskInfo != null){
+            nodename = taskInfo.getString("name");
+
+            if(taskInfo.getLong("processdefinitionid") > 0) {
+                QFilter qFilter = new QFilter("id", QCP.equals, taskInfo.getLong("processdefinitionid"));
+                DynamicObject processdef = BusinessDataServiceHelper.loadSingle("wf_processdefinition", qFilter.toArray());
+                if (processdef != null) {
+                    workflowname = processdef.getString("name");
+                }
+            }
+
+            if("财务系统审批流".equals(workflowname) && taskInfo.getString("entityname") != null){
+                workflowname = taskInfo.getString("entityname") + "审批流程";
+            }
+            if(StringUtils.isNotEmpty(taskInfo.getString("entitynumber"))
+                    && StringUtils.isNotEmpty(taskInfo.getString("businesskey"))){
+                QFilter qFilter = new QFilter("id", QCP.equals, Long.valueOf(taskInfo.getString("businesskey")));
+                DynamicObject bill = BusinessDataServiceHelper.loadSingle(taskInfo.getString("entitynumber"), qFilter.toArray());
+                if(bill != null){
+                    createDate = getCreateTime(bill);
+                }
+
+            }
+        }
+
+
+        c.put("title", workflowname);
+
+        JSONArray formArr = new JSONArray();
+        JSONObject form1 = new JSONObject();
+        form1.put("name", "流程标题");
+        form1.put("value", fixName + (StringUtils.isEmpty(toDoInfo.getTitle()) ? workflowname : toDoInfo.getTitle()));
+        formArr.add(form1);
+
+        JSONObject form2 = new JSONObject();
+        form2.put("name", "提单时间");
+        form2.put("value", createDate);
+        formArr.add(form2);
+
+        JSONObject form3 = new JSONObject();
+        form3.put("name", "当前节点");
+        form3.put("value", nodename);
+        formArr.add(form3);
+
+        c.put("form", formArr);
+
+        /*************处理消息格式*****************/
+
+
+        //发起人
+
+        Long faqirenid = taskInfo.getLong("starterid");
+        if(faqirenid!=null){
+            DynamicObject faqiren = BusinessDataServiceHelper.loadSingleFromCache(faqirenid,"bos_user");
+            c.put("open_id", getUserId(faqiren.getString("phone"),token));
+
+            //发起人id、发起人飞书id
+            logMaps.put("nckd_creatorid", faqirenid.toString());
+            logMaps.put("nckd_creatoridfeishu", getUserId(faqiren.getString("phone"),token));
+        }
+
+
+        c.put("start_time", ((Timestamp)taskInfo.get("createdate")).getTime());
+        c.put("update_time", ((Timestamp)taskInfo.get("createdate")).getTime());
+        c.put("update_mode", "REPLACED");
+
+        //task_list	审批人相关
+        JSONArray taskArr = new JSONArray();
+
+        for (int i = 0; i <load.length ; i++) {
+            JSONObject task = new JSONObject();
+            task.put("task_id", load[i].getPkValue()+"_"+toDoInfo.getTaskId());
+            task.put("open_id", getUserId(load[i].getString("phone"),token));
+
+            JSONObject links1 = new JSONObject();
+
+            links1.put("pc_link",pcSrcToFeishu(changeDanDianSrc(pcUrl+"&apptype=feishu")));
+            links1.put("mobile_link",changeDanDianSrc(mobUrl+"&apptype=feishu"));
+
+            task.put("links", links1);
+
+            if(isUpdate){
+                task.put("status", passStatus);
+            }else{
+                task.put("status", "PENDING");
+            }
+
+
+            task.put("title",  toDoInfo.getContent());
+            if(isUpdate){
+                task.put("title", content);
+            }
+            task.put("title", workflowname);
+
+            task.put("create_time", taskInfo.get("createdate"));
+            task.put("end_time", taskInfo.get("createdate"));
+            task.put("update_time", taskInfo.get("createdate"));
+            taskArr.add(task);
+        }
+        c.put("task_list", taskArr);
+
+        m.put("content", c);
+        return m;
+    }
+
+    //pc链接特殊处理
+    public String pcSrcToFeishu(String src){
+        String dandianStr="";
+        if(dandianStr != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://applink.feishu.cn/client/web_url/open?mode=window&url=" + encodesrc;
+            } catch (UnsupportedEncodingException e) {
+                log.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    public static String changeDanDianSrc(String src){
+        String dandianStr="";
+        if(src != null) {
+            try {
+                String encodesrc = URLEncoder.encode(src, "utf-8");
+                dandianStr = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=" + encodesrc + "&app_id=" + APP_ID;
+            } catch (UnsupportedEncodingException e) {
+                log.info(e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return dandianStr;
+    }
+
+
+    /**
+     * 工作流中将待办变成已办的时候,会调用此接口进行待办变已办的处理
+     * @param messageContext
+     * @param toDoInfo
+     */
+    @Override
+    public void dealToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+        log.info("财务系统飞书,更新待办为已办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+
+        try {
+            List<Long> userIds = toDoInfo.getUserIds();
+            if (CollectionUtils.isEmpty(userIds)) {
+                log.info("财务系统飞书,人员为空,推送失败");
+                return;
+            }
+            log.info("获取用户开始");
+
+            QFilter qFilter = new QFilter("id", QCP.in, userIds);
+            DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+            log.info("获取用户结束:"+load[0].getPkValue());
+
+            String token = getToken();
+            String status="APPROVED";
+            log.info("获取任务:");
+            boolean isExist = QueryServiceHelper.exists("wf_task",toDoInfo.getTaskId());
+            if(isExist){
+                DynamicObject taskInfo =  BusinessDataServiceHelper.loadSingleFromCache(toDoInfo.getTaskId() ,"wf_task");
+                log.info("获取任务结束:"+taskInfo.getPkValue());
+                String taskStatus=taskInfo.getString("handlestate");
+                if("dismissed".equals(taskStatus)){//驳回
+                    status="REJECTED";
+                }else if("willApproval".equals(taskStatus)){
+                    //            status="REJECTED";
+                }
+            }
+//            DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+//            String content =getTitle(messageContext,businessObject);
+            String content = toDoInfo.getContent();
+            String title = toDoInfo.getTitle();
+
+            log.info("获取json开始:");
+
+            Map<String, String> logMaps = new HashMap<>();
+
+            logMaps.put("nckd_logtype", "dealToDo");
+            logMaps.put("nckd_apiurl", createInstanceUrl);
+
+            String reveiceUserIds = userIds.stream().map(Object::toString) // 将Long转换为String
+                    .collect(Collectors.joining(",")); // 用逗号连接所有String元素
+
+            logMaps.put("nckd_receiveuserids", reveiceUserIds);
+
+            JSONObject m = getToYiBanJson(messageContext, toDoInfo, load, token,true, status, content, logMaps);
+
+            log.info("财务系统飞书更新待办:"+m.toJSONString());
+
+
+
+            String uuid = saveMsgLogNew(userIds.get(0).toString(), title, "财务系统飞书更新待办[DaJingDaiBanServiceHandler.dealToDo]", m, logMaps);
+
+            String response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+            log.info("财务系统飞书更新待办结果:"+response);
+
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+                response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                log.info("财务系统飞书更新待办结果二次:" + response);
+            }
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+        } catch (Exception e) {
+            log.info("财务系统飞书,更新待办失败" + e.getMessage());
+            log.info(e.getMessage());
+//            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 工作流中撤回,或者多人收到任务,其中一人审批,删除其他人的待办时,调用此接口
+     * @param messageContext
+     * @param toDoInfo
+     */
+    @Override
+    public void deleteToDo(MessageContext messageContext, ToDoInfo toDoInfo) {
+
+        log.info("财务系统飞书,取消待办开始 ctx: " + messageContext.toString() + ",taskId:" + messageContext.getTaskId());
+        List<Long> userIds = toDoInfo.getUserIds();
+        QFilter qFilter = new QFilter("id", QCP.in, userIds);
+        DynamicObject[] load = BusinessDataServiceHelper.load(USER_FORM_ID, USERNAME+",phone", qFilter.toArray());
+        String token = getToken();
+//        DynamicObject businessObject = WfUtils.findBusinessObject(messageContext.getBusinessKey(), messageContext.getEntityNumber());
+//        String content =getTitle(messageContext,businessObject);
+
+        Map<String, String> logMaps = new HashMap<>();
+
+        logMaps.put("nckd_logtype", "deleteToDo");
+        logMaps.put("nckd_apiurl", createInstanceUrl);
+
+        String reveiceUserIds = userIds.stream().map(Object::toString) // 将Long转换为String
+                .collect(Collectors.joining(",")); // 用逗号连接所有String元素
+
+        logMaps.put("nckd_receiveuserids", reveiceUserIds);
+
+        String content = toDoInfo.getContent();
+        JSONObject m = getToDaiBanJson(messageContext,toDoInfo,load,token,true,"DONE",content, "", logMaps);
+        try {
+            log.info("财务系统飞书取消待办:"+m.toJSONString());
+
+
+
+            String uuid = saveMsgLogNew(userIds.get(0).toString(), content, "财务系统飞书更新待办[DaJingDaiBanServiceHandler.deleteToDo]", m, logMaps);
+            String response = doPostByHttpClient(createInstanceUrl, m.toJSONString(), true,token, 3);
+
+            log.info("财务系统飞书取消待办结果:"+response);
+
+            JSONObject res = JSONObject.parseObject(response);
+            if (interErrCodeArr.contains(res.getIntValue("code"))){
+                // 内部错误,可重试
+                Thread.sleep(500L);
+                response = FeishuUtil.doPostByHttpClient(createInstanceUrl, m.toJSONString(), true, token, 3);
+                log.info("财务系统飞书更新待办结果二次:" + response);
+            }
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                updateMsgLog(uuid, response);
+            }
+        } catch (ConnectException e) {
+            log.info("财务系统飞书,取消待办失败");
+            log.error(e.getMessage(), e);
+            e.printStackTrace();
+        } catch (InterruptedException e1) {
+            log.info("财务系统飞书,取消待办失败");
+            log.error(e1.getMessage(), e1);
+            throw new RuntimeException(e1);
+        }
+    }
+
+    public String getTitle(MessageContext messageContext,DynamicObject businessObject){
+        String content = "请处理:%s单据编号:%s";
+
+        DynamicObjectType dt = businessObject.getDynamicObjectType();
+        IDataEntityProperty pkProp = dt.getPrimaryKey();
+        DataEntityPropertyCollection properties = dt.getProperties();
+
+        //发现预算的单据虽然是基础资料,但编码字段是billno, 所以改成根据实体属性是否包含number或billno字段
+        if(properties.containsKey("number")){
+            content = String.format(content, messageContext.getEntityName(), businessObject.getString("number"));
+        } else if(properties.containsKey("billno")){
+            content = String.format(content, messageContext.getEntityName(), businessObject.getString("billno"));
+        } else {
+            content = String.format(content, messageContext.getEntityName(), dt.getName());
+        }
+
+//        if (dt instanceof BasedataEntityType) {
+//            content = String.format(content, messageContext.getEntityName(), businessObject.getString("number"));
+//        }else{
+//            content = String.format(content, messageContext.getEntityName(), businessObject.getString("billno"));
+//        }
+
+        return content;
+    }
+
+    /**
+     * 普通消息暂时不推送
+     * @param ctx
+     * @param message
+     */
+    @Override
+    public void sendMessage(MessageContext ctx, MessageInfo message) {
+
+        try {
+            String notifyType = message.getNotifyType();
+            if (ctx != null){
+                log.info("DaiBanServiceHandler.sendMessage 飞书待办消息url:" + message.getContentUrl() + ",mobileurl:" + message.getMobContentUrl()+ ", pk:" + ctx.getBusinessKey());
+            }else {
+                log.info("DaiBanServiceHandler.sendMessage 飞书待办消息url:" + message.getContentUrl() + ",mobileurl:" + message.getMobContentUrl());
+            }
+            if("feishu".equals(notifyType)){
+                log.info("财务系统移动审批,消息发 taskId:" + message.getId());
+                String token = getToken();
+                List<Long> userIds = message.getUserIds();
+
+                String linkUrl = message.getContentUrl();
+                String linkMobileUrl = message.getMobContentUrl();
+
+                //替换PC端域名
+                linkUrl = replacePcUrlDomain(linkUrl);
+                //移动端替换域名
+                linkMobileUrl = replaceMobUrlDomain(linkMobileUrl);
+
+                //发送消息
+                for (int i = 0; i < userIds.size() ; i++) {
+                    DynamicObject userInfo =  BusinessDataServiceHelper.loadSingle(userIds.get(i),"bos_user");
+                    String userid =  getUserIdTrue(userInfo.getString("phone"),token);
+
+
+                    if("circulation".equals(message.getTplScene())){
+                        if(i == 0) {
+                            //发送传阅消息,多接收人只需要发一次
+                            sendFlowMessage(ctx, message, userid, token);
+                        }
+                        //发送带链接的消息
+                        sendLinkMessage(ctx, message, userid, userIds.get(i).toString(), token, "1016");
+                    } else if(StringUtils.isNotEmpty(linkUrl)){
+                        //发送带链接的消息
+                        sendLinkMessage(ctx, message, userid, userIds.get(i).toString(), token, "1021");
+                    } else {
+                        //发送普通消息
+                        userid =  getUserId(userInfo.getString("phone"),token);
+                        sendCustomMessage(ctx, message, userid, token);
+                    }
+                }
+            }
+        }catch (Exception e){
+            log.error("DaiBanServiceHandler.sendMessage 发送审批消息异常:" + e.getMessage(), e);
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * 替换移动端域名
+     * @param mobUrl
+     * @return
+     */
+    public String replaceMobUrlDomain(String mobUrl){
+        log.info("替换前移动端地址: moburl:" + mobUrl);
+
+        if(StringUtils.isEmpty(mobUrl)){
+            return mobUrl;
+        }
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileDomain = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        mobUrl = mobUrl.replace(curUrl, mobileDomain);
+
+        log.info("替换后移动端地址: moburl:" + mobUrl);
+
+        return mobUrl;
+    }
+
+    /**
+     * 替换PC端域名
+     * @param pcUrl
+     * @return
+     */
+    public String replacePcUrlDomain(String pcUrl){
+        log.info("替换前PC端地址: pcUrl:" + pcUrl);
+
+        if(StringUtils.isEmpty(pcUrl)){
+            return pcUrl;
+        }
+
+        //移动端需要走外网,将地址替换成外网地址
+        String curUrl = ParamsUtil.getCommonParamsField("nckd_url");
+        String mobileDomain = ParamsUtil.getCommonParamsField("nckd_mobileurl");
+
+        pcUrl = pcUrl.replace(mobileDomain, curUrl);
+
+        log.info("替换后地址: pcUrl:" + pcUrl);
+
+        return pcUrl;
+    }
+
+
+    private final String getAppWfName() {
+        return System.getProperty("app.wf.name", TRAGETNAME);
+    }
+
+    private String getCreateTime(DynamicObject bill){
+        String createDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        if(bill.getDynamicObjectType().getProperties().contains("createtime")) {
+            createDate = DateFormatUtils.format(bill.getDate("createtime"), "yyyy-MM-dd HH:mm:ss");
+        } else if(bill.getDynamicObjectType().getProperties().contains("createdate")) {
+            createDate = DateFormatUtils.format(bill.getDate("createdate"), "yyyy-MM-dd HH:mm:ss");
+        }
+
+        return createDate;
+    }
+
+    /**
+     * 保存飞书消息日志
+     *
+     * @param userid  用户id
+     * @param title   标题
+     * @param content 内容
+     * @param m       发送消息体
+     * @return uuid
+     */
+    private String saveMsgLog(String userid, String title, String content, JSONObject m) {
+        try {
+            log.info("记录新增飞书推送数据日志");
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("billstatus", "C");
+            dynamicObject.set("nckd_to_userid", userid);
+            dynamicObject.set("nckd_msg_title", title);
+            String msgContent = m.toString();
+            if (msgContent.length() < 200){
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            }else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+            if (content.length() < 200){
+                dynamicObject.set("nckd_msg_content", content);
+            }else {
+                dynamicObject.set("nckd_msg_content", content.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_tag", content);
+            dynamicObject.set("nckd_send_time", new Date());
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            log.info("记录新增飞书推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            log.info("记录新增飞书推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 保存飞书消息日志
+     *
+     * @param userid  用户id
+     * @param title   标题
+     * @param content 内容
+     * @param m       发送消息体
+     * @return uuid
+     */
+    private String saveMsgLogNew(String userid, String title, String content, JSONObject m, Map<String, String> logMaps) {
+        try {
+            log.info("记录新增飞书推送数据日志");
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("billstatus", "C");
+            dynamicObject.set("nckd_to_userid", userid);
+            dynamicObject.set("nckd_msg_title", title);
+            String msgContent = m.toString();
+            if (msgContent.length() < 200){
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            }else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+            if (content.length() < 200){
+                dynamicObject.set("nckd_msg_content", content);
+            }else {
+                dynamicObject.set("nckd_msg_content", content.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_tag", content);
+            dynamicObject.set("nckd_send_time", new Date());
+            dynamicObject.set("nckd_sysstatus", "fail");
+
+            if(logMaps != null){
+                dynamicObject.set("nckd_logtype", logMaps.get("nckd_logtype"));
+                dynamicObject.set("nckd_apiurl", logMaps.get("nckd_apiurl"));
+                dynamicObject.set("nckd_to_useridfeishu", logMaps.get("nckd_to_useridfeishu"));
+                dynamicObject.set("nckd_creatorid", logMaps.get("nckd_creatorid"));
+                dynamicObject.set("nckd_creatoridfeishu", logMaps.get("nckd_creatoridfeishu"));
+                dynamicObject.set("nckd_receiveuserids", logMaps.get("nckd_receiveuserids"));
+            }
+
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            log.info("记录新增飞书推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            log.info("记录新增飞书推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 更新消息响应到对应日志记录
+     * @param uuid uuid
+     * @param response 消息响应
+     */
+    private void updateMsgLog(String uuid, String response) {
+        try {
+            log.info("记录更新飞书推送结果数据日志");
+            QFilter qFilterCas = new QFilter("billno", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("nckd_log_feishumsg", "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, "nckd_log_feishumsg");
+                if (response.length() < 200){
+                    dynamicObject.set("nckd_send_result_l", response);
+                }else {
+                    dynamicObject.set("nckd_send_result_l", response.substring(0,200) + "...");
+                }
+                dynamicObject.set("nckd_send_result_l_tag", response);
+
+                if(response != null && isJson(response)) {
+                    JSONObject res = JSONObject.parseObject(response);
+                    if ("0".equals(res.getString("code"))) {
+                        dynamicObject.set("nckd_sysstatus", "success");
+                    }
+                }
+
+                SaveServiceHelper.update(dynamicObject);
+                log.info("记录更新飞书推送结果数据日志成功");
+            }
+        } catch (Exception e) {
+            log.info("记录更新飞书推送结果数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void deleteProcessInstance(MessageContext ctx, Long proceInstanceId) {
+        // 打印控制台日志
+        System.out.println(String.format("流程实例:%d 被删除了,messageContext:" + ctx, proceInstanceId));
+        log.info(String.format("流程实例:%d 被删除了,messageContext:" + ctx, proceInstanceId));
+    }
+
+    @Override
+    public void completeProcessInstance(MessageContext ctx, Long proceInstanceId) {
+        // 流程归档
+        System.out.println(String.format("流程实例:%d 结束了,messageContext:" + ctx, proceInstanceId));
+        log.info(String.format("流程实例:%d 结束了,messageContext:" + ctx, proceInstanceId));
+    }
+
+    private boolean isJson(String str){
+        try{
+            JSONObject jsonStr = JSONObject.parseObject(str);
+            return true;
+        } catch(Exception e){
+            return false;
+        }
+    }
+}

+ 11 - 0
main/java/kd/cosmic/jkjt/msg/feishu/UUIDUtil.java

@@ -0,0 +1,11 @@
+package kd.cosmic.jkjt.msg.feishu;
+
+import java.util.UUID;
+
+public class UUIDUtil {
+    public static String generateUuid() {
+        return UUID.randomUUID().toString().replace("-", "");
+    }
+
+
+}

+ 56 - 0
main/java/kd/cosmic/jkjt/msg/feishu/formplugin/FeiShuMessageEditPlugin.java

@@ -0,0 +1,56 @@
+package kd.cosmic.jkjt.msg.feishu.formplugin;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.msg.feishu.FeiShuRetryUtil;
+
+import java.util.EventObject;
+
+/**
+ * @description飞书待办同步日志(nckd_log_feishumsg)
+ * @author wanghaiwu_kd
+ * @date 2025/02/26
+ */
+public class FeiShuMessageEditPlugin extends AbstractBillPlugIn {
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+    }
+
+    @Override
+    public void click(EventObject evt) {
+        super.click(evt);
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+
+        String itemKey = evt.getItemKey();
+        if ("nckd_resyn".equals(itemKey)) {
+            Object id = this.getModel().getValue("id");
+            if(Long.valueOf(String.valueOf(id)) <= 0L){
+                this.getView().showErrorNotification("请先保存日志!");
+                return;
+            }
+
+            String msg = FeiShuRetryUtil.reSynchronizeMsg(Long.valueOf(String.valueOf(id)));
+
+            if(StringUtils.isNotEmpty(msg)){
+                this.getView().showErrorNotification(msg);
+            } else {
+                this.getView().showSuccessNotification("重新同步成功!");
+            }
+
+            this.getView().invokeOperation("refresh");
+        }
+    }
+}

+ 213 - 0
main/java/kd/cosmic/jkjt/siit/HttpUtilAction.java

@@ -0,0 +1,213 @@
+package kd.cosmic.jkjt.siit;
+
+import kd.cosmic.jkjt.wps.HttpClientFactory;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ParseException;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.TrustStrategy;
+import org.apache.http.util.EntityUtils;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+public class HttpUtilAction {
+
+
+
+    /**
+     * 模拟发送 post 请求
+     */
+    public static String doGet(String url, String data, Map<String,String> headerMap) {
+        // 构建POST请求 请求地址请更换为自己的。
+        HttpGet get = new HttpGet(url);
+        for( String key : headerMap.keySet()){
+            get.addHeader(key,headerMap.get(key));
+        }
+        InputStream inputStream = null;
+        String result = "";
+        try {
+            // 使用之前写的方法创建httpClient实例
+            CloseableHttpClient httpClient = createSSLClientDefault();
+            HttpResponse response = httpClient.execute(get);
+            result=getResponseMessage(response);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (ClientProtocolException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return result;
+
+    }
+
+
+
+    /**
+     * 模拟发送 post 请求
+     */
+    public static String doPost(String url, String data,Map<String,String> headerMap) {
+        // 构建POST请求 请求地址请更换为自己的。
+        HttpPost post = new HttpPost(url);
+        InputStream inputStream = null;
+        String result = "";
+        try {
+            // 使用之前写的方法创建httpClient实例
+            // CloseableHttpClient httpClient = createSSLClient();
+            CloseableHttpClient httpClient = createSSLClientDefault();
+            // 构造消息头
+            for( String key : headerMap.keySet()){
+                post.addHeader(key,headerMap.get(key));
+            }
+//            post.setHeader("Content-type", "application/json; charset=utf-8");
+//            post.setHeader("Connection", "Close");
+            // 构建消息实体
+            StringEntity entity = new StringEntity(data, Charset.forName("UTF-8"));
+            entity.setContentEncoding("UTF-8");
+            // 发送Json格式的数据请求
+            entity.setContentType("application/json");
+            post.setEntity(entity);
+            // 发送请求
+            HttpResponse response = httpClient.execute(post);
+            result=getResponseMessage(response);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (ClientProtocolException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return result;
+
+    }
+
+    private static String getResponseMessage(HttpResponse response) throws ParseException, IOException {
+        HttpEntity entity = response.getEntity();
+        String resp = EntityUtils.toString(entity, "UTF-8");
+        EntityUtils.consume(entity);
+        return resp;
+
+    }
+    /**
+     * POST方式发送请求
+     * @param url
+     * @param data
+     * @return
+     * @throws Exception
+     */
+    public static String doPostByHttpClient(String url, String data,Map<String,String> headerMap) throws Exception {
+
+        StringEntity se = new StringEntity(data, "UTF-8");
+        se.setContentType("text/json");
+        se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.addHeader("api", "true");
+
+        for( String key : headerMap.keySet()){
+            httpPost.addHeader(key,headerMap.get(key));
+        }
+        //设置boundary,文件解析用
+//		httpPost.addHeader("Content-Type",  "multipart/form-data; boundary="+BOUNDARY);
+        httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+        httpPost.setEntity(se);
+        return doExecuteByHttpClient(httpPost,new ResponseHandler<String>() {
+            @Override
+            public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
+                int statusCode = response.getStatusLine().getStatusCode();
+                if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_CREATED) {
+                    try {
+                        throw new Exception("连接服务器发生错误!");
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+                return EntityUtils.toString(response.getEntity());
+            }
+        });
+    }
+
+    public static <T> T doExecuteByHttpClient(HttpUriRequest httpPost, ResponseHandler<? extends T> responseHandler) throws Exception {
+        try {
+            CloseableHttpClient httpClient = HttpClientFactory.getHttpClient();
+            T rtn = httpClient.execute(httpPost, responseHandler);
+            return rtn;
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println(" ===== doPostByHttpClient() ERROR ===== ");
+            throw new Exception(e.getMessage());
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+
+
+    /**
+     * 信任所有的证书
+     * 有风险,域名、ip方式皆可,不建议使用
+     * @return
+     */
+    public static CloseableHttpClient createSSLClientDefault() {
+        try {
+            // 使用 loadTrustMaterial() 方法实现一个信任策略,信任所有证书
+            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
+                // 信任所有
+                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                    return true;
+                }
+            }).build();
+            // NoopHostnameVerifier类: 作为主机名验证工具,实质上关闭了主机名验证,它接受任何
+            // 有效的SSL会话并匹配到目标主机。
+            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
+            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
+            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
+        } catch (KeyManagementException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (KeyStoreException e) {
+            e.printStackTrace();
+        }
+        return HttpClients.createDefault();
+    }
+
+
+}

+ 297 - 0
main/java/kd/cosmic/jkjt/siit/SiitServerHandler.java

@@ -0,0 +1,297 @@
+package kd.cosmic.jkjt.siit;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import org.apache.commons.codec.digest.DigestUtils;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * @author turbo
+ *
+ * 国信影像对接
+ */
+public class SiitServerHandler {
+
+    private static String siitClientcode = "";
+
+    private static String siitPassword = "";
+
+    public static String url = "";
+
+    private static String useraccount = "";
+
+    static {
+        DynamicObject commonParams = ParamsUtil.getCommonParamsAllField();
+        if(commonParams != null ){
+            siitClientcode = commonParams.getString("nckd_siitclientcode");
+            siitPassword = commonParams.getString("nckd_siitpassword");
+            url = commonParams.getString("nckd_siiturl");
+            //useraccount = commonParams.getString("nckd_siituser");
+        }
+    }
+
+    /**
+     * 国信接口测试用
+     * @param args
+     */
+    public static void main(String[] args)  {
+
+        String barcode = "Bill20230101001";
+        JSONObject jsonSafety = getSafetyParams(barcode,useraccount);
+        JSONObject jsonFlow = getFlowParams(barcode,"", useraccount, "");
+        JSONObject jsonPageUrl = getPageUrl(barcode,useraccount);
+        JSONObject jsonCreateImagePath = getCreateImagePath(barcode,useraccount);
+
+        JSONObject json = new JSONObject();
+        json.put("safety",jsonSafety);
+        //json.put("params",jsonFlow);
+        json.put("params",jsonPageUrl);
+        //json.put("params",jsonCreateImagePath);
+
+        Map<String, String> headers = new ConcurrentHashMap<>(1);
+        headers.put("Content-Type", "application/json");
+
+        //String uri = url + "/siitservice/startFlow/addFlow";
+        String uri = url + "/siitservice/ticketpage/getPageUrl";
+        //String uri = url + "/siitservice/createImagePathApi/createImagePath";
+        try {
+            System.out.println(json.toJSONString());
+
+            String appReturnStr = HttpUtilAction.doPost(uri, json.toJSONString(), headers);
+
+            JSONObject appTokenJO = JSONObject.parseObject(appReturnStr);
+
+            System.out.println(appTokenJO.toString());
+
+            if(appTokenJO.getString("status").equals("true")){
+                System.out.println(appTokenJO.getString("data"));
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    /**
+     * 安全参数接入
+     * @param barcode
+     * @return
+     */
+    public static JSONObject getSafetyParams(String barcode,String useraccount){
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        String sTime = sdf.format(new Date());
+
+        //安全校验位=Time的值+clientcode的值+barcode的值+useraccount的值+密码, 整体生成MD5
+        String ticket= DigestUtils.md5Hex(sTime+siitClientcode+barcode+useraccount+siitPassword);
+
+
+        JSONObject jsonSafety = new JSONObject();
+        jsonSafety.put("time", sTime);
+        jsonSafety.put("useraccount", useraccount);
+        jsonSafety.put("clientcode", siitClientcode);
+        jsonSafety.put("barcode", barcode);
+        jsonSafety.put("ticket", ticket);
+
+        return jsonSafety;
+    }
+
+    /**
+     * 触发扫描接口(待办)
+     * @param barcode
+     * @return
+     */
+    public static JSONObject getFlowParams(String barcode,String companyNumber,String useraccount, String voucherId){
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
+
+        JSONObject jsonFlow = new JSONObject();
+        jsonFlow.put("alleis", "0");
+        jsonFlow.put("formtype", "BARCODE");
+        jsonFlow.put("datasource", "Kingdee");
+        jsonFlow.put("issend", "");
+        jsonFlow.put("billcode", barcode);
+        jsonFlow.put("barcode", barcode);
+        jsonFlow.put("operatoruser", useraccount);
+        jsonFlow.put("operatorname", useraccount);
+        jsonFlow.put("busstype", "kdvoucher");
+        jsonFlow.put("busscode", "kdvoucher");
+        jsonFlow.put("companycode", companyNumber);
+        jsonFlow.put("billamount", 0);
+        jsonFlow.put("subbilltime", sdf1.format(new Date()));
+        jsonFlow.put("remark", "ticket");
+        jsonFlow.put("issubmit", 1);
+        jsonFlow.put("smallbuss", "0");
+        jsonFlow.put("createdate", sdf2.format(new Date()));
+        jsonFlow.put("scantime", sdf1.format(new Date()));
+
+        if(StringUtils.isNotEmpty(voucherId)){
+            List<String> serialList = getSerialList(voucherId);
+
+            jsonFlow.put("anattr1", serialList);
+        }
+
+        return jsonFlow;
+    }
+
+    /**
+     * 获取凭证的上游付款单或收款单分录的交易流水号
+     * @param voucherId
+     * @return
+     */
+    public static List<String> getSerialList(String voucherId){
+        List<String> serialList = new ArrayList(10);
+
+        QFilter qFilter = new QFilter("voucherid", QCP.equals, Long.valueOf(voucherId));
+
+        DataSet daptracks = QueryServiceHelper.queryDataSet("SiitServiceHandler.queryDaptracker"
+                                        , "ai_daptracker", "sourcebillid, billtype.id as billtype", qFilter.toArray(), (String)null);
+        for (Row row : daptracks) {
+            Long sourceBillId = row.getLong("sourcebillid");
+            String billType = row.getString("billtype");
+
+            qFilter = new QFilter("id", QCP.equals, sourceBillId);
+            DynamicObject sourceDO = BusinessDataServiceHelper.loadSingle(billType, qFilter.toArray());
+
+            if(sourceDO != null){
+                DynamicObjectCollection entrys = sourceDO.getDynamicObjectCollection("entry");
+
+                for(DynamicObject entry : entrys){
+                    if(StringUtils.isNotEmpty(entry.getString("nckd_bankserialno"))){
+                        serialList.add(entry.getString("nckd_bankserialno"));
+                    }
+                }
+            }
+        }
+
+
+        return serialList;
+    }
+
+    /**
+     * 触发归档动作
+     * @param barcode
+     * @param useraccount
+     * @return
+     */
+    public static JSONObject getAotuArchive(String barcode, String companyNumber, String useraccount){
+        JSONObject jsonArchive = new JSONObject();
+
+        jsonArchive.put("operatoruser", useraccount);
+        jsonArchive.put("operatorname", useraccount);
+        jsonArchive.put("batchnum", barcode);
+        jsonArchive.put("barcode", barcode);
+
+        return jsonArchive;
+    }
+
+    /**
+     * 获取票夹页面地址接口
+     * @param barcode
+     * @return
+     */
+    public static JSONObject getPageUrl(String barcode,String useraccount){
+
+        JSONObject json = new JSONObject();
+        /**
+         * AutoOCR: 只能上载发票,上传如果是非发票类附件,要求用户必须录入发票,如果不录入,上传对应附件。选择票夹中的数据时,也只能选择发票,非发票类不允许选择。
+         * SelTicket: 发票附件都可以上载,都可以选择。
+         * UploadElectron:只能上载电子附件和电子类发票,也只能选择附件类型为电子的附件。
+         */
+        json.put("pagetype", "SelTicket");
+        json.put("useraccount", useraccount);
+        json.put("datasource", "Kingdee");
+        /**
+         * APP/PC, 默认:APP
+         */
+        json.put("apporpc", "PC");
+        json.put("barcode", barcode);
+        json.put("issubmit", 1);
+
+        return json;
+    }
+
+    public static JSONObject getUploadPage(String barcode,String useraccount){
+        JSONObject json = new JSONObject();
+
+        json.put("barcode", barcode);
+        json.put("isadd", "1");
+        json.put("isdelete", "0");
+        json.put("submitbutton", "1");
+        json.put("useraccount", useraccount);
+        json.put("language", "zh_CN");
+
+//        JSONObject bussinfo = new JSONObject();
+//        bussinfo.put("barcode", barcode);
+//
+//        json.put("bussinfo", bussinfo);
+
+        return json;
+    }
+
+    /**
+     * 获取 查阅影像接口 PC端
+     * @param barcode
+     * @return
+     */
+    public static JSONObject getCreateImagePath(String barcode,String useraccount){
+
+        JSONObject json = new JSONObject();
+        json.put("barcode", barcode);
+        json.put("drawpen", "0");
+        json.put("watermark", "0");
+        json.put("useraccount", useraccount);
+        json.put("evaluation", "0");
+        json.put("commenttype", "0");
+        json.put("candownprint", "1");
+        json.put("uploaddoc", "0");
+        json.put("setdoclevel", "0");
+        json.put("editinvoice", "0");
+        json.put("input", "1");   //这个查看影像的接口要加一个参数,控制进入调阅页面可以编辑发票信息,手动录入电子凭证必填项信息,input参数传1是可以编辑
+
+        return json;
+    }
+
+    public static JSONObject getCreateAppImagePath(String barcode,String useraccount){
+        JSONObject json = new JSONObject();
+        json.put("barcode", barcode);
+        json.put("rescanbtn", "0");
+        json.put("datasource", "Kingdee");
+        json.put("useraccount", useraccount);
+
+        return json;
+    }
+
+    /**
+     * 作废接口
+     * @param barcode
+     * @return
+     */
+    public static JSONObject deleteBill(String barcode,String useraccount){
+
+        JSONObject json = new JSONObject();
+        json.put("reasoncode", "");
+        json.put("operatoruser", useraccount);
+        json.put("deletetype", 1);
+        json.put("barcode", barcode);
+        json.put("reason", "");
+
+        return json;
+    }
+
+
+}

+ 79 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/AccountSumCardPlugin.java

@@ -0,0 +1,79 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.form.control.Control;
+import kd.bos.form.control.Label;
+import kd.bos.form.control.Vector;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.portal.pluginnew.GridCardPlugin;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/07/11
+ * @description 账户管理主页小部件卡片:账户统计
+ */
+public class AccountSumCardPlugin extends GridCardPlugin {
+    public static final String ENTITY_ACCOUNTBANK = "am_accountbank";
+    public static final String KEY_LABELAP = "nckd_labelap";
+    public static final String KEY_REFRESHCOUNT = "nckd_refreshcount";
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        Label numLabel = (Label)this.getControl(KEY_LABELAP);
+        String count = getAccountCount();
+        numLabel.setText(count);
+    }
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+
+        Vector freshControl = (Vector)this.getControl(KEY_REFRESHCOUNT);
+        if (freshControl != null) {
+            freshControl.addClickListener(this);
+        }
+    }
+
+    @Override
+    public void click(EventObject evt) {
+        super.click(evt);
+        String key = "";
+        Object source = evt.getSource();
+        if (source instanceof Control) {
+            key = ((Control)source).getKey();
+        }
+        if (KEY_REFRESHCOUNT.equals(key)) {
+            Label numLabel = (Label)this.getControl(KEY_LABELAP);
+            String count = getAccountCount();
+            numLabel.setText(count);
+        }
+    }
+
+    private String getAccountCount(){
+        Long curOrgId = RequestContext.get().getOrgId();
+        DynamicObject curOrg = BusinessDataServiceHelper.loadSingle(curOrgId, "bos_org");
+        if(curOrg == null){
+            return "0";
+        }
+        List<Long> ids = new ArrayList<>();
+        ids.add(curOrg.getLong("id"));
+        List<Long> parentIds = OrgUnitServiceHelper.getAllSubordinateOrgs("10", ids, true);
+
+        String selectProperties = "id";
+        QFilter filter = new QFilter("openorg", QCP.in, parentIds);
+        filter.and(new QFilter("acctstatus", QCP.equals, "normal"));
+
+        DynamicObject[] objCols = BusinessDataServiceHelper.load(ENTITY_ACCOUNTBANK, selectProperties, new QFilter[]{filter});
+
+        return String.valueOf(objCols.length);
+    }
+}

+ 230 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/AccountbankDistributeExPlugin.java

@@ -0,0 +1,230 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.chart.*;
+import kd.bos.form.container.Tab;
+import kd.bos.form.control.events.TabSelectEvent;
+import kd.bos.form.control.events.TabSelectListener;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.orm.util.CollectionUtils;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.tmc.fbp.common.helper.TmcOrgDataHelper;
+import kd.tmc.fbp.common.util.EmptyUtil;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/06/16
+ * @description 银行账户管理主页插件,增加一级子公司分类统计
+ */
+public class AccountbankDistributeExPlugin extends AbstractFormPlugin {
+    private HistogramChart customChart = null;
+    private List<String> names = new ArrayList();
+    private List<Integer> values = new ArrayList();
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        Tab tab = (Tab)this.getControl("tabap");
+
+        tab.addTabSelectListener(new TabSelectListener() {
+            public void tabSelected(TabSelectEvent event) {
+                if("nckd_firstorg".equals(event.getTabKey())) {
+                    AccountbankDistributeExPlugin.this.setDimension(event.getTabKey());
+                    AccountbankDistributeExPlugin.this.fetchData();
+                    AccountbankDistributeExPlugin.this.paintChart(AccountbankDistributeExPlugin.this.names, AccountbankDistributeExPlugin.this.values);
+                }
+            }
+        });
+    }
+
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        String key = e.getProperty().getName();
+        String dimension = this.getDimension();
+        if("nckd_firstorg".equals(dimension)){
+            if ("showmode".equals(key)) {
+                String paymode = (String) this.getModel().getValue("showmode");
+                paymode = EmptyUtil.isEmpty(paymode) ? "10" : paymode;
+                this.setShowmode(paymode);
+                this.fetchData();
+                this.paintChart(this.names, this.values);
+            }
+        }
+    }
+
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+        String dimension = this.getDimension();
+        if("nckd_firstorg".equals(dimension)) {
+            this.fetchData();
+            this.paintChart(this.names, this.values);
+        }
+    }
+
+    private void fetchData() {
+        String appId = this.getView().getFormShowParameter().getAppId();
+        RequestContext context = RequestContext.get();
+        List<Long> comIdList = TmcOrgDataHelper.getAuthorizedBankOrgId(Long.valueOf(context.getUserId()), appId, "am_accountbank", "47150e89000000ac");
+        List<QFilter> accountbankQFilters = new ArrayList();
+        accountbankQFilters.add(new QFilter("company.id", "in", comIdList));
+        List<Object> statusList = new ArrayList();
+        statusList.add("normal");
+        statusList.add("closing");
+        accountbankQFilters.add(new QFilter("acctstatus", "in", statusList));
+        accountbankQFilters.add(new QFilter("nckd_firstlevelorg.id", QCP.is_notnull, null));
+        accountbankQFilters.add(new QFilter("nckd_firstlevelorg.id", QCP.not_equals, 0L));
+        DataSet bizDs = null;
+        String dimension = this.getDimension();
+        String groupByFieldStr = "bank.bank_cate.name";
+        switch (dimension) {
+            case "nckd_firstorg":
+                groupByFieldStr = "firstorg";
+                bizDs = this.queryData_ByFirstOrg((QFilter[])accountbankQFilters.toArray(new QFilter[0]));
+                break;
+        }
+
+        String paymode = this.getShowmode();
+        if (bizDs != null) {
+            bizDs = bizDs.orderBy(new String[]{"count desc"}).top(Integer.parseInt(paymode));
+            Iterator<Row> it = bizDs.iterator();
+            if (!it.hasNext()) {
+                this.getView().setVisible(true, new String[]{"flex_quesheng"});
+                this.getView().setVisible(false, new String[]{"flexpanelap1"});
+                return;
+            }
+
+            this.getView().setVisible(false, new String[]{"flex_quesheng"});
+            ArrayList xName = new ArrayList();
+            List<Integer> yValue = new ArrayList();
+
+            while(it.hasNext()) {
+                Row row = (Row)it.next();
+                xName.add(row.getString(groupByFieldStr));
+                yValue.add(row.getInteger("count") == null ? Integer.getInteger("0") : row.getInteger("count"));
+            }
+
+            this.names.clear();
+            this.values.clear();
+            this.names.addAll(xName);
+            this.values.addAll(yValue);
+        }
+    }
+
+    private String getDimension() {
+        String dimension = this.getPageCache().get("dimension");
+        if (EmptyUtil.isEmpty(dimension)) {
+            dimension = "btn_finorg";
+        }
+
+        return dimension;
+    }
+
+    private void setDimension(String key) {
+        this.getPageCache().put("dimension", key);
+    }
+
+    private String getShowmode() {
+        String showmode = this.getPageCache().get("showmode");
+        if (EmptyUtil.isEmpty(showmode)) {
+            showmode = "10";
+        }
+
+        return showmode;
+    }
+
+    private void setShowmode(String value) {
+        this.getPageCache().put("showmode", value);
+    }
+
+    protected DataSet queryData_ByFirstOrg(QFilter[] filter) {
+        String bankAcctSic = "nckd_firstlevelorg.name as firstorg";
+        DataSet bankAcctSet = QueryServiceHelper.queryDataSet("BankAcctSum", "am_accountbank", bankAcctSic, filter, (String)null);
+        return bankAcctSet.groupBy(new String[]{"firstorg"}).count().finish();
+    }
+
+    private void paintChart(List<String> xName, List<Integer> yValue) {
+        this.customChart = (HistogramChart)this.getControl("expchart");
+        if (null != this.customChart && !CollectionUtils.isEmpty(this.values)) {
+            this.getView().setVisible(true, new String[]{"expchart"});
+            this.customChart.clearData();
+            this.customChart.setDraggable(true);
+            this.customChart.setShowTooltip(true);
+            this.customChart.setTitleAlign(XAlign.left, YAlign.top);
+            this.customChart.setMargin(Position.left, "30px");
+            this.customChart.setMargin(Position.right, "10px");
+            BarSeries barSeries = this.customChart.createBarSeries(ResManager.loadKDString("账户数", "AccountbankDistributePlugin_0", "tmc-am-formplugin", new Object[0]));
+            Collections.replaceAll(xName, null, ResManager.loadKDString("未命名", "AccountbankDistributePlugin_7", "tmc-am-formplugin", new Object[0]));
+            Collections.replaceAll(xName, "", ResManager.loadKDString("未命名", "AccountbankDistributePlugin_7", "tmc-am-formplugin", new Object[0]));
+            if (xName.size() < 5) {
+                barSeries.setBarWidth("100");
+            } else if (xName.size() < 9) {
+                barSeries.setBarWidth("50");
+            } else if (xName.size() < 11) {
+                barSeries.setBarWidth("45");
+            } else {
+                barSeries.setBarWidth("4");
+            }
+
+            Axis xAxis = this.customChart.createXAxis(ResManager.loadKDString("银行账户", "AccountbankDistributePlugin_1", "tmc-am-formplugin", new Object[0]), AxisType.category);
+            Map<String, String> axisLabel = new HashMap();
+            xAxis.setPropValue("axisLabel", axisLabel);
+            xAxis.setCategorys(xName);
+            Axis yAxis = this.customChart.createYAxis(ResManager.loadKDString("账户数 ", "AccountbankDistributePlugin_2", "tmc-am-formplugin", new Object[0]), AxisType.value);
+            barSeries.setColor("#5F8AFF");
+            Iterator var7 = yValue.iterator();
+
+            while(var7.hasNext()) {
+                Integer amount = (Integer)var7.next();
+                barSeries.addData(amount);
+            }
+
+            this.setLableStyle(barSeries);
+            this.setLinearGradient(barSeries, "#5E80EB", "#83bff6");
+            this.setLineColor(xAxis, "#666666");
+            this.setLineColor(yAxis, "#666666");
+            this.customChart.refresh();
+        } else {
+            this.getView().setVisible(false, new String[]{"expchart"});
+        }
+    }
+
+    private void setLableStyle(BarSeries barSeries) {
+        Label label = new Label();
+        label.setShow(true);
+        label.setPosition(Position.top);
+        label.setColor("#5F8AFF");
+        label.setFormatter("function(itemValue,index){var itemData = itemValue.value;return itemData;}");
+        barSeries.setLabel(label);
+        List<Object> funPath = new ArrayList();
+        funPath.add("label");
+        funPath.add("normal");
+        funPath.add("formatter");
+        barSeries.addFuncPath(funPath);
+    }
+
+    private void setLinearGradient(BarSeries barSeries, String color1, String color2) {
+        List<Object> funPath = new ArrayList();
+        funPath.add("itemStyle");
+        funPath.add("normal");
+        funPath.add("color");
+        barSeries.addFuncPath(funPath);
+        HashMap<String, Object> map = new HashMap();
+        HashMap<String, Object> normap = new HashMap();
+        map.put("color", "new echarts.graphic.LinearGradient(0, 1, 0, 0, [{\"offset\": 0,\"color\": '" + color1 + "'}, {\"offset\": 1, \"color\": '" + color2 + "'}])");
+        normap.put("normal", map);
+        barSeries.setPropValue("itemStyle", normap);
+    }
+    private void setLineColor(Axis axix, String color) {
+        Map<String, Object> axisLineMap = new HashMap();
+        Map<String, Object> lineStyleMap = new HashMap();
+        lineStyleMap.put("color", color);
+        axisLineMap.put("lineStyle", lineStyleMap);
+        axix.setPropValue("axisLine", axisLineMap);
+    }
+}

+ 145 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/BankAccountEdit.java

@@ -0,0 +1,145 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.field.events.BeforeF7SelectEvent;
+import kd.bos.form.field.events.BeforeF7SelectListener;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.servicehelper.org.OrgViewType;
+import kd.bos.util.StringUtils;
+
+import java.util.EventObject;
+import java.util.List;
+
+/**
+ * 单据界面
+ */
+public class BankAccountEdit extends AbstractBillPlugIn implements BeforeF7SelectListener {
+    private static final Log logger = LogFactory.getLog(BankAccountEdit.class);
+
+    private final static String KEY_FIELD_OPENORG = "openorg";
+    private final static String KEY_FIELD_FIRSTORG = "nckd_firstlevelorg";
+    private final static String KEY_ENTITY_STRUCTURE = "bos_org_structure";
+    private final static String KEY_ENTITY_ORG = "bos_org";
+    private final static String KEY_FIRSTORG = "OF-01";
+
+    @Override
+    public void beforeF7Select(BeforeF7SelectEvent beforeF7SelectEvent) {
+
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+
+        IDataModel model = this.getModel();
+
+        DynamicObject openOrg = (DynamicObject) model.getValue(KEY_FIELD_OPENORG);
+        if(openOrg != null){
+            setDefaultValueByOpenOrgChanage(openOrg);
+        }
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        //加载后,显示是否
+        IDataModel model = this.getModel();
+        String entityName = model.getDataEntityType().getName();
+
+        if(model.getDataEntityType().getProperties().containsKey("nckd_iscapitalpool")) {
+            DynamicObject bank = model.getDataEntity().getDynamicObject("bank");
+            if (bank != null) {
+                bank = BusinessDataServiceHelper.loadSingle(bank.getPkValue(), "bd_finorginfo");
+                if (bank.getDynamicObject("bank_cate") != null) {
+
+                    DynamicObject bankCate = bank.getDynamicObject("bank_cate");
+                    bankCate = BusinessDataServiceHelper.loadSingle(bankCate.getPkValue(), "bd_bankcgsetting");
+                    model.setValue("nckd_iscapitalpool", bankCate.getBoolean("nckd_iscapitalpoolbank"));
+                    model.setDataChanged(false);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs args) {
+        super.afterDoOperation(args);
+
+        FormOperate formOperate = (FormOperate) args.getSource();
+        //保存
+        if ("save".equals(formOperate.getOperateKey())) {
+            getView().invokeOperation("refresh");
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+
+        String fieldKey = e.getProperty().getName();
+        if(KEY_FIELD_OPENORG.equals(fieldKey)){
+            // 获取修改数据
+            ChangeData[] changeData = e.getChangeSet();
+            DynamicObject newValue = (DynamicObject)changeData[0].getNewValue();
+            Boolean haveFirstOrg = this.getModel().getDataEntityType().getProperties().containsKey(KEY_FIELD_FIRSTORG);
+            if(haveFirstOrg) {
+                setDefaultValueByOpenOrgChanage(newValue);
+            }
+        } else if("nckd_issetcbs".equals(fieldKey)){
+            // 获取修改数据
+            ChangeData[] changeData = e.getChangeSet();
+            Boolean newValue = (Boolean)changeData[0].getNewValue();
+            IDataModel model = this.getModel();
+            if(newValue) {
+                model.setValue("nckd_manualupr", null);
+            }
+        }
+
+
+    }
+
+    private void setDefaultValueByOpenOrgChanage(DynamicObject openOrg){
+        IDataModel model = this.getModel();
+
+        model.setValue(KEY_FIELD_FIRSTORG, null);
+
+        if(openOrg != null){
+            List<Long> parentIds = OrgUnitServiceHelper.getAllSuperiorOrgs(OrgViewType.Accounting, openOrg.getLong("id"));
+            if(!parentIds.contains(openOrg.getLong("id"))){
+                parentIds.add(openOrg.getLong("id"));
+            }
+            QFilter filterView = new QFilter("view.id", QCP.equals, 10L);
+            QFilter filteraOrg = new QFilter("org.id", QCP.in, parentIds);
+            QFilter[] filters = new QFilter[]{filterView, filteraOrg};
+            String selectProperties = "org, number";
+            String orderBy = "longnumber desc";
+//            DynamicObjectCollection orgCols = QueryServiceHelper.query(KEY_ENTITY_STRUCTURE, selectProperties, filters, orderBy);
+            DynamicObject[] orgList = BusinessDataServiceHelper.load(KEY_ENTITY_STRUCTURE, selectProperties, filters, orderBy);
+            for(int i = 0; i < orgList.length; i++){
+                DynamicObject org = BusinessDataServiceHelper.loadSingle(orgList[i].getLong("org.id"), KEY_ENTITY_ORG);
+
+                if(org.getDynamicObject("orgpattern") != null){
+                    String pattern = org.getDynamicObject("orgpattern").getString("name");
+                    if(StringUtils.isNotEmpty(pattern) && pattern.contains("一级子公司")){
+                        model.setValue(KEY_FIELD_FIRSTORG, org);
+                        break;
+                    }
+                }
+            }
+
+            this.getView().updateView(KEY_FIELD_FIRSTORG);
+        }
+    }
+}

+ 111 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/BankAcctOpenCusEditPlugin.java

@@ -0,0 +1,111 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.field.events.BeforeF7SelectEvent;
+import kd.bos.form.field.events.BeforeF7SelectListener;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.util.StringUtils;
+
+/**
+ * @description:开户申请,处理账户名称、账户简称问题(nckd_am_accountbank_ext)。
+ * @author wanghaiwu_kd
+ * @date 2024/12/30
+ */
+public class BankAcctOpenCusEditPlugin extends AbstractBillPlugIn implements BeforeF7SelectListener {
+    private static final Log logger = LogFactory.getLog(BankAcctOpenCusEditPlugin.class);
+
+    @Override
+    public void beforeF7Select(BeforeF7SelectEvent beforeF7SelectEvent) {
+
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        ChangeData[] changeData = e.getChangeSet();
+
+        String fieldKey = e.getProperty().getName();
+        if("bank".equals(fieldKey) || "bankaccountnumber".equals(fieldKey)){
+            setName();
+        } else if("nckd_accountid".equals(fieldKey) || "company".equals(fieldKey) || "acctproperty".equals(fieldKey)){
+            setAcctName();
+        }
+    }
+
+    /**
+     * 设置账户名称
+     */
+    public void setAcctName(){
+        if(this.getModel().getValue("company") == null || StringUtils.isEmpty((String)this.getModel().getValue("nckd_accountid"))){
+            return;
+        }
+
+        DynamicObject company = (DynamicObject) this.getModel().getValue("company");
+        String companyName = company.getString("name").replace("本部", "");
+        String accountid = (String)this.getModel().getValue("nckd_accountid");
+        String acctName = "";
+
+        //实体户,账户名称 = 申请公司名称(带本部的要去掉)
+        if("entity".equals(accountid)){
+            acctName = companyName;
+        }
+        //虚拟户
+        else if("virtual".equals(accountid)){
+            if(companyName.contains("工会")){
+                acctName = companyName + "工会委员会";
+            } else if(companyName.contains("党费")){
+                acctName = "中共江西" + companyName + "支部委员会";
+            } else if( companyName.contains("团费")){
+                acctName = "中国共产主义青年团" + companyName + "委员会";
+            } else {
+                acctName = companyName;
+                DynamicObject acctproperty = (DynamicObject) this.getModel().getValue("acctproperty");
+                if(acctproperty != null){
+                    acctName = acctName + acctproperty.getString("name");
+                }
+            }
+        }
+
+        LocaleString nameLocale = new LocaleString(acctName);
+
+        this.getModel().setValue("name", nameLocale);
+
+        this.getView().updateView("name");
+    }
+
+    /**
+     * 设置账户简称
+     */
+    public void setName(){
+        if(this.getModel().getValue("bank") == null || StringUtils.isEmpty((String)this.getModel().getValue("bankaccountnumber"))){
+            return;
+        }
+
+        DynamicObject bank = (DynamicObject) this.getModel().getValue("bank");
+        String bankAccountNumber = (String) this.getModel().getValue("bankaccountnumber");
+        bank = BusinessDataServiceHelper.loadSingle(bank.getPkValue(), bank.getDynamicObjectType().getName());
+        DynamicObject bankCate = bank.getDynamicObject("bank_cate");
+        String name = "";
+        if(bankCate == null){
+            name = bankAccountNumber.substring(bankAccountNumber.length() - 4);
+        } else {
+            name = bankCate.getString("name") + bankAccountNumber.substring(bankAccountNumber.length() - 4);
+        }
+
+        LocaleString nameLocale = new LocaleString(name);
+//        nameLocale.setLocaleValue(name);
+//        nameLocale.setLocaleValue_zh_CN(name);
+//        nameLocale.setLocaleValue_zh_TW(name);
+//        nameLocale.setLocaleValue_en(name);
+
+        this.getModel().setValue("acctname", nameLocale);
+
+        this.getView().updateView("acctname");
+    }
+}

+ 107 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportAccountListPlugin.java

@@ -0,0 +1,107 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.bill.BillOperationStatus;
+import kd.bos.bill.BillShowParameter;
+import kd.bos.bill.OperationStatus;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.form.ShowType;
+import kd.bos.form.control.Control;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.events.BillListHyperLinkClickEvent;
+import kd.bos.form.events.HyperLinkClickEvent;
+import kd.bos.form.events.HyperLinkClickListener;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.list.BillList;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import org.apache.commons.lang.time.DateFormatUtils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * description:报表查询列表(nckd_accountcbslist),动态表单插件
+ * @author wanghaiwu_kd
+ * @date 2024/04/12
+ */
+public class ReportAccountListPlugin extends AbstractFormPlugin implements HyperLinkClickListener {
+    public ReportAccountListPlugin() {
+    }
+
+    public void initialize() {
+        super.initialize();
+        Control btnok = this.getView().getControl("btnok");
+        this.getView().setVisible(false, new String[]{"btnok", "btncancel"});
+        BillList list = (BillList)this.getView().getControl("nckd_billlistap");
+        Map<String, Object> customParams = this.getView().getFormShowParameter().getCustomParams();
+        Object accountIds = customParams.get("accountIds");
+
+        list.addSetFilterListener((l) -> {
+            List<Long> ids = new ArrayList();
+            if (accountIds != null) {
+                ids = (List<Long>)accountIds;
+            }
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            Date requestDate = null;
+            try {
+                requestDate = sdf.parse(customParams.get("requestDate").toString());
+            } catch (ParseException e) {
+                throw new RuntimeException(e);
+            }
+            QFilter qFilter = (new QFilter("nckd_datefield", "<", requestDate))
+                    .and(new QFilter("billstatus", "=", "C"))
+                    .and(new QFilter("entryentity.nckd_accountbank", "in", ids));
+            l.getQFilters().add(qFilter);
+        });
+    }
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        BillList list = (BillList)this.getView().getControl("nckd_billlistap");
+        Map<String, Object> customParams = this.getView().getFormShowParameter().getCustomParams();
+        Object accountIds = customParams.get("accountIds");
+
+        list.addSetFilterListener((l) -> {
+            List<Long> ids = new ArrayList();
+            if (accountIds != null) {
+                ids = (List<Long>)accountIds;
+            }
+
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            Date requestDate = null;
+            try {
+                requestDate = sdf.parse(customParams.get("requestDate").toString());
+            } catch (ParseException e1) {
+                throw new RuntimeException(e1);
+            }
+
+            QFilter qFilter = (new QFilter("nckd_datefield", "<", requestDate))
+                    .and(new QFilter("billstatus", "=", "C"))
+                    .and(new QFilter("entryentity.nckd_accountbank", "in", ids));
+            l.getQFilters().add(qFilter);
+        });
+
+        list.addHyperClickListener(this);
+    }
+
+    @Override
+    public void hyperLinkClick(HyperLinkClickEvent event) {
+        if ("billno".equals(event.getFieldName())) {
+            Object billId = ((BillListHyperLinkClickEvent) event).getCurrentRow().getPrimaryKeyValue();
+
+            BillShowParameter parameter = new BillShowParameter();
+
+            parameter.setFormId("nckd_am_cbs");
+            parameter.setPkId(billId);
+            parameter.setBillStatus(BillOperationStatus.VIEW);
+            parameter.setStatus(OperationStatus.EDIT);
+            parameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+
+            this.getView().showForm(parameter);
+        }
+    }
+}

+ 101 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportEditPlugin.java

@@ -0,0 +1,101 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.bill.OperationStatus;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.ShowType;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.org.OrgUnitServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.tmc.fbp.common.helper.TmcOrgDataHelper;
+import kd.tmc.fbp.common.util.EmptyUtil;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.EventObject;
+import java.util.List;
+
+public class ReportEditPlugin extends AbstractBillPlugIn  {
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+
+        DynamicObject openOrg = (DynamicObject)this.getModel().getValue("nckd_orgfield");
+        if (openOrg == null) {
+            DynamicObject dynamicObject = UserServiceHelper.getCurrentUser("id,name");
+            Long userId = dynamicObject.getLong("id");
+            //获取当前登录切换的组织
+            Long buOrg = RequestContext.get().getOrgId();//UserServiceHelper.get(userId);
+
+            // 查询业务单元
+            DynamicObject buOrgDObj = BusinessDataServiceHelper.loadSingle(buOrg, "bos_org");
+
+            DynamicObject accountOrg = TmcOrgDataHelper.getAccountOrgByFundsOrg(buOrgDObj);
+            if (!EmptyUtil.isEmpty(accountOrg)) {
+                this.getModel().setValue("nckd_orgfield", accountOrg.getPkValue());
+            } else {
+                this.getModel().setValue("nckd_orgfield", (Object)null);
+            }
+        }
+    }
+
+    @Override
+    public void afterLoadData(EventObject e) {
+        super.afterLoadData(e);
+
+        if(this.getModel().getValue("nckd_datefield") != null) {
+            this.showDynamicBill();
+        }
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+
+        if(this.getModel().getValue("nckd_datefield") != null) {
+            this.showDynamicBill();
+        }
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        super.afterDoOperation(afterDoOperationEventArgs);
+
+        if(this.getModel().getValue("nckd_datefield") != null) {
+            this.showDynamicBill();
+        }
+    }
+
+    private void showDynamicBill() {
+        Date requestDate = (Date)this.getModel().getValue("nckd_datefield");
+        DynamicObjectCollection entrys = this.getModel().getEntryEntity("entryentity");
+
+        if(entrys != null && entrys.size() > 0 && requestDate != null){
+            List<Long> accountIds = new ArrayList();
+            for(DynamicObject entry : entrys){
+                if(entry.getDynamicObject("nckd_accountbank") != null){
+                    accountIds.add(entry.getDynamicObject("nckd_accountbank").getLong("id"));
+                }
+            }
+
+            if(accountIds.size() > 0){
+                FormShowParameter formShowParameter = new FormShowParameter();
+                formShowParameter.getOpenStyle().setShowType(ShowType.InContainer);
+                formShowParameter.getOpenStyle().setTargetKey("nckd_flexpanelap");
+                formShowParameter.setStatus(OperationStatus.VIEW);
+                formShowParameter.setFormId("nckd_accountcbslist");
+                formShowParameter.setSendToClient(true);
+                formShowParameter.setCustomParam("accountIds", accountIds);
+                formShowParameter.setCustomParam("requestDate", requestDate);
+                this.getView().showForm(formShowParameter);
+            }
+        }
+    }
+}

+ 54 - 0
main/java/kd/cosmic/jkjt/tmc/am/formplugin/bankacct/ReportListPlugin.java

@@ -0,0 +1,54 @@
+package kd.cosmic.jkjt.tmc.am.formplugin.bankacct;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.filter.CommonFilterColumn;
+import kd.bos.filter.FilterColumn;
+import kd.bos.form.events.FilterContainerInitArgs;
+import kd.bos.form.events.SetFilterEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.tmc.fbp.common.helper.TmcOrgDataHelper;
+import kd.tmc.fbp.common.util.EmptyUtil;
+
+import java.util.List;
+
+/**
+ * 更新报备单插件
+ */
+public class ReportListPlugin extends AbstractListPlugin {
+
+    @Override
+    public void setFilter(SetFilterEvent e) {
+        super.setFilter(e);
+    }
+
+    @Override
+    public void filterColumnSetFilter(SetFilterEvent args) {
+        super.filterColumnSetFilter(args);
+    }
+
+    @Override
+    public void filterContainerInit(FilterContainerInitArgs args) {
+        super.filterContainerInit(args);
+
+        Long buOrg = RequestContext.get().getOrgId();//UserServiceHelper.get(userId);
+
+        // 查询业务单元
+        DynamicObject buOrgDObj = BusinessDataServiceHelper.loadSingle(buOrg, "bos_org");
+
+        DynamicObject accountOrg = TmcOrgDataHelper.getAccountOrgByFundsOrg(buOrgDObj);
+        if (!EmptyUtil.isEmpty(accountOrg)) {
+            Long orgid = accountOrg.getLong("id");
+            List<FilterColumn> filterColumnList = args.getFilterContainerInitEvent().getCommonFilterColumns();
+            for (int i = 0; i < filterColumnList.size(); i++) {
+                String fieldName = filterColumnList.get(i).getFieldName();
+                if (fieldName.equals("nckd_orgfield.name")) {
+                    CommonFilterColumn defa = (CommonFilterColumn) filterColumnList.get(i);
+                    defa.setDefaultValue(orgid.toString());
+                    filterColumnList.set(i, defa);
+                }
+            }
+        }
+    }
+}

+ 199 - 0
main/java/kd/cosmic/jkjt/tmc/am/mservice/UpdateAccountCBSServiceImpl.java

@@ -0,0 +1,199 @@
+package kd.cosmic.jkjt.tmc.am.mservice;
+
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.db.tx.TX;
+import kd.bos.db.tx.TXHandle;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class UpdateAccountCBSServiceImpl {
+    private static final Log logger = LogFactory.getLog(UpdateAccountCBSServiceImpl.class);
+    public Map<String, Object> updateWareBatch(DynamicObject accountInfo){
+        Map<String, Object> result = new HashMap();
+        boolean success = true;
+        String message = "";
+        TXHandle h = TX.requiresNew();
+        Throwable localThrowable4 = null;
+        try
+        {
+            try
+            {
+//                DynamicObject accountInfo = BusinessDataServiceHelper.loadSingleFromCache(id, "am_accountbank");
+                accountInfo.set("nckd_ispushcbs", true);
+                SaveServiceHelper.save(new DynamicObject[]{accountInfo});
+            }
+            catch (Throwable e)
+            {
+                h.markRollback();
+                success = false;
+                message = "更新CBS同步标记失败," + e.getMessage();
+                logger.error("更新CBS同步标记失败", e);
+                throw e;
+            }
+        }
+        catch (Throwable localThrowable2)
+        {
+            localThrowable4 = localThrowable2;throw localThrowable2;
+        }
+        finally
+        {
+            if (h != null) {
+                if (localThrowable4 != null) {
+                    try
+                    {
+                        h.close();
+                    }
+                    catch (Throwable localThrowable3)
+                    {
+                        localThrowable4.addSuppressed(localThrowable3);
+                    }
+                } else {
+                    h.close();
+                }
+            }
+        }
+        result.put("message", message);
+        result.put("success", Boolean.valueOf(success));
+        return result;
+    }
+
+    /**
+     * 保存日志
+     * @param logInfo
+     * @return
+     */
+    public Map<String, Object> saveCBSLogData(DynamicObject logInfo){
+        Map<String, Object> result = new HashMap();
+        boolean success = true;
+        String message = "";
+        TXHandle h = TX.requiresNew();
+        Throwable localThrowable4 = null;
+        try
+        {
+            try
+            {
+                SaveServiceHelper.save(new DynamicObject[]{logInfo});
+//                OperationResult resultSave = OperationServiceHelper.executeOperate("save", "nckd_cbslog", new DynamicObject[]{logInfo}, OperateOption.create());
+            }
+            catch (Throwable e)
+            {
+                h.markRollback();
+                success = false;
+                message = "更新CBS同步标记失败," + e.getMessage();
+                logger.error("更新CBS同步标记失败", e);
+                throw e;
+            }
+        }
+        catch (Throwable localThrowable2)
+        {
+            localThrowable4 = localThrowable2;throw localThrowable2;
+        }
+        finally
+        {
+            if (h != null) {
+                if (localThrowable4 != null) {
+                    try
+                    {
+                        h.close();
+                    }
+                    catch (Throwable localThrowable3)
+                    {
+                        localThrowable4.addSuppressed(localThrowable3);
+                    }
+                } else {
+                    h.close();
+                }
+            }
+        }
+        result.put("message", message);
+        result.put("success", Boolean.valueOf(success));
+        return result;
+    }
+
+    /**
+     * 更新日志
+     * @param uuid
+     * @param outData
+     * @return
+     */
+    public Map<String, Object> updateCBSLogData(String uuid, String outData){
+        Map<String, Object> result = new HashMap();
+        boolean success = true;
+        String message = "";
+        TXHandle h = TX.requiresNew();
+        Throwable localThrowable4 = null;
+        try
+        {
+            try
+            {
+                logger.info("记录更新CBS调用结果数据日志");
+                outData = outData == null ? "" : outData;
+
+                QFilter qFilterCas = new QFilter("number", QCP.equals, uuid);
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle("nckd_cbslog", new QFilter[]{qFilterCas});
+                if (dynamicObject != null ) {
+//                    String id = dynamicObject.getPkValue().toString();
+//                    DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, "nckd_cbslog");
+                    if (outData.length() < 200) {
+                        dynamicObject.set("nckd_outdata", outData);
+                    } else {
+                        dynamicObject.set("nckd_outdata", outData.substring(0, 200) + "...");
+                    }
+//                    if (outData.length() < 1000) {
+//                        dynamicObject.set("nckd_outdata_tag", outData);
+//                    } else {
+//                        dynamicObject.set("nckd_outdata_tag", outData.substring(0, 1000));
+//                    }
+
+                    dynamicObject.set("nckd_outdata_tag", outData);
+
+                    SaveServiceHelper.update(dynamicObject);
+
+                    logger.info("记录更新CBD调用数据日志成功");
+                }
+            }
+            catch (Throwable e)
+            {
+                h.markRollback();
+                success = false;
+                message = "记录更新CBS调用结果数据日志异常," + e.getMessage();
+                logger.error("记录更新CBS调用结果数据日志异常", e);
+                throw e;
+            }
+        }
+        catch (Throwable localThrowable2)
+        {
+            localThrowable4 = localThrowable2;throw localThrowable2;
+        }
+        finally
+        {
+            if (h != null) {
+                if (localThrowable4 != null) {
+                    try
+                    {
+                        h.close();
+                    }
+                    catch (Throwable localThrowable3)
+                    {
+                        localThrowable4.addSuppressed(localThrowable3);
+                    }
+                } else {
+                    h.close();
+                }
+            }
+        }
+        result.put("message", message);
+        result.put("success", Boolean.valueOf(success));
+        return result;
+    }
+}

+ 396 - 0
main/java/kd/cosmic/jkjt/tmc/am/opplugin/bankacct/BankAccountOpPlugin.java

@@ -0,0 +1,396 @@
+package kd.cosmic.jkjt.tmc.am.opplugin.bankacct;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.DispatchServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import kd.sdk.plugin.Plugin;
+import org.apache.commons.lang.time.DateFormatUtils;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date add by 2023/04/27
+ * @description 银行账户操作插件(nckd_am_accountbank_ext),保存时,同步银行账户至CBS系统。
+ */
+public class BankAccountOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+    private static final Log logger = LogFactory.getLog(BankAccountOpPlugin.class);
+
+    private static final String KEY_OP_SAVE = "save";
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+
+
+        String opKey = e.getOperationKey();
+        if (KEY_OP_SAVE.equals(opKey)){
+            synBankAccountToCBS(e, opKey);
+        }
+    }
+
+    /**
+     * 同步银行账户信息到CBS系统
+     * @param e
+     * @param type
+     */
+    private void synBankAccountToCBS(AfterOperationArgs e, String type){
+        DynamicObject[] dataEntities = e.getDataEntities();
+
+        if(dataEntities.length == 0){
+            return;
+        }
+
+        for(DynamicObject accountInfo : dataEntities){
+            String bankOrgType = accountInfo.getString("finorgtype");
+            if(!"0".equals(bankOrgType)){
+                continue;
+            }
+            Boolean isPushCBS = accountInfo.getBoolean("nckd_ispushcbs");
+            if(isPushCBS){
+                continue;
+            }
+
+            Long openOrgId = accountInfo.getDynamicObject("openorg").getLong("id");
+            String orgName = accountInfo.getDynamicObject("openorg").getString("name");
+
+            //获取cbs地址
+            String cbsUrl = ParamsUtil.getCBSURLByMaster(openOrgId);
+
+            if(StringUtils.isEmpty(cbsUrl)){
+                logger.info("同步银行账户失败:组织【" + orgName + "】(" + openOrgId.toString() + ")未配置相应的CBS地址");
+                continue;
+            }
+
+            //获取银行账号新增ACOPNACT报文内容
+            String body = getBankAccountBodyString(cbsUrl, accountInfo);
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            logger.info("调用CBS接口ERQRYTRN开始时间:"+ sTime);
+
+            String result = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            logger.info("调用CBS接口ERQRYTRN结束时间:"+ eTime);
+
+            //保存CBS调用日志
+            DynamicObject logInfo = getLogInfo(cbsUrl, "account", paramBody, result);
+            String path = "kd.cosmic.jkjt.tmc.am.servicehelper";
+            DispatchServiceHelper.invokeService(path,"am","UpdateAccountCBSService","saveCBSLogData", logInfo);
+
+            //更新CBS调用日志
+//            if(logInfo != null && StringUtils.isNotEmpty(logInfo.getString("number"))){
+//                String uuid = logInfo.getString("number");
+//                ParamsUtil.updateCBSLogData(uuid, result);
+//                DispatchServiceHelper.invokeService(path,"am","UpdateAccountCBSService","updateCBSLogData", uuid, result);
+//            }
+
+            String cbssr = CBSToolUtil.parseXMLcbs(result);
+            JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+            if(cbsObj != null && cbsObj.get("INFO") != null){
+                JSONArray array = (JSONArray)cbsObj.get("INFO");
+                for(int i = 0; i < array.size(); i++){
+                    JSONObject obj = array.getJSONObject(i);
+                    String retcode = obj.getString("RETCOD");
+                    if("0000000".equals(retcode)){
+//                        accountInfo = BusinessDataServiceHelper.loadSingle(accountInfo.getLong("id"), "am_accountbank");
+                        accountInfo.set("nckd_ispushcbs", true);
+                        SaveServiceHelper.update(new DynamicObject[]{accountInfo});
+//                        DispatchServiceHelper.invokeService(path,"am","UpdateAccountCBSService","updateWareBatch", accountInfo);
+                    }
+                }
+            }
+        }
+    }
+
+    public DynamicObject getLogInfo(String cbsUrl, String logType, String inData, String outData) {
+        try {
+            inData = inData == null ? "" : inData;
+
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+
+            uuid = uuid == null ? "uuid" + String.valueOf(System.currentTimeMillis()) : uuid;
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_cbslog");
+
+            dynamicObject.set("enable", "1");
+            dynamicObject.set("status", "C");
+            dynamicObject.set("number", uuid);
+            dynamicObject.set("nckd_date", new Date());
+            dynamicObject.set("nckd_logtype", logType);
+            dynamicObject.set("nckd_cbsurl", cbsUrl);
+
+            if (inData.length() < 200) {
+                dynamicObject.set("nckd_indata", inData);
+            } else {
+                dynamicObject.set("nckd_indata", inData.substring(0, 200) + "...");
+            }
+            dynamicObject.set("nckd_indata_tag", inData);
+
+            if (outData.length() < 200) {
+                dynamicObject.set("nckd_outdata", outData);
+            } else {
+                dynamicObject.set("nckd_outdata", outData.substring(0, 200) + "...");
+            }
+
+            dynamicObject.set("nckd_outdata_tag", outData);
+//            if (outData.length() < 1000) {
+//                dynamicObject.set("nckd_outdata_tag", outData);
+//            } else {
+//                dynamicObject.set("nckd_outdata_tag", outData.substring(0, 1000));
+//            }
+
+            return dynamicObject;
+        } catch (Exception e) {
+            logger.info("记录CBS调用日志:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取后台4位数的企业号
+     * @param cbsUrl
+     * @param cltseq
+     * @return
+     */
+    private String getCBScltnbr(String cbsUrl, String cltseq){
+        String cltnbr = "";
+        //交易明细ERQRYTRS报文内容
+        StringBuffer body = new StringBuffer();
+
+        body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>CICLTLST</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<CICLTLSTX>\r\n");
+        body.append("<CLTSEQ>").append(cltseq).append("</CLTSEQ>\r\n");
+        body.append("<CLTTYP>CL</CLTTYP>\r\n");
+        body.append("</CICLTLSTX>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        String paramBody = CBSToolUtil.getBodyString(body.toString());
+
+        String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        logger.info("调用CBS接口ERQRYTRN开始时间:"+ sTime);
+
+        String result = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+        String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        logger.info("调用CBS接口ERQRYTRN结束时间:"+ eTime);
+
+        String cbssr = CBSToolUtil.parseXMLcbs(result);
+        JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(cbsObj != null && cbsObj.get("CICLTLSTZ") != null){
+            JSONArray array = (JSONArray)cbsObj.get("CICLTLSTZ");
+            for(int i = 0; i < array.size(); i++) {
+                JSONObject obj = array.getJSONObject(i);
+                cltnbr = obj.getString("CLTNBR");
+            }
+        }
+
+        return cltnbr;
+    }
+
+    /**
+     *
+     * @param accountInfo
+     * @return
+     */
+    private String getBankAccountBodyString(String cbsUrl, DynamicObject accountInfo){
+        Map<String, Map<String, String>> materialMsg = new HashMap<>();
+
+        StringBuffer body = new StringBuffer();
+
+        body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ACOPNACT</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+
+        body.append("<ACACTINFX>\r\n");
+        body.append("<ACTBAL>0</ACTBAL>\r\n");//余额
+        body.append("<AVLBAL>0</AVLBAL>");//可用余额
+
+        //备注
+        String remark = accountInfo.getString("comment");
+        if(remark == null){
+            remark = "";
+        }
+        body.append("<ACTRMK>").append(remark).append("</ACTRMK>\r\n");
+
+        String accountNumber = accountInfo.getString("bankaccountnumber");
+        body.append("<BACNBR>").append(accountNumber).append("</BACNBR>\r\n");//外部账号
+        body.append("<BACRNK>R</BACRNK>\r\n");//账户级别,R:总户,C:子户
+
+        String isOpenCBS = accountInfo.getString("nckd_issetcbs");
+        if("1".equals(isOpenCBS)){
+            isOpenCBS = "9";
+        } else {
+            isOpenCBS = "0";
+        }
+
+        isOpenCBS = "0";
+        body.append("<BCPFLG>").append(isOpenCBS).append("</BCPFLG>\r\n");//是否开启银企直连平台
+
+        //合作金融机构
+        DynamicObject bank = accountInfo.getDynamicObject("bank");
+        bank = BusinessDataServiceHelper.loadSingle(bank.getLong("id"), "bd_finorginfo");
+
+        //行名行号
+        if(bank.getDynamicObject("bebank") != null) {
+            DynamicObject bebank = BusinessDataServiceHelper.loadSingle(bank.getDynamicObject("bebank").getLong("id"), "bd_bebank");
+            String unionnumber = bebank.getString("union_number");
+            body.append("<BRDNBR>").append(unionnumber).append("</BRDNBR>\r\n");//银行联行号
+        }
+
+        if(bank.getDynamicObject("bank_cate") != null) {
+            DynamicObject bankCate = BusinessDataServiceHelper.loadSingle(bank.getDynamicObject("bank_cate").getLong("id"), "bd_bankcgsetting");
+            String bankType = bankCate.getString("nckd_cbsbanktype");
+            if (StringUtils.isNotEmpty(bankType)) {
+                body.append("<BLGBNK>").append(bankType).append("</BLGBNK>\r\n");//所属银行
+            }
+        }
+
+        DynamicObjectCollection collection = accountInfo.getDynamicObjectCollection("currency");
+        Long currencyId = 0L;
+        for (DynamicObject item : collection) {
+            //引用该基础资料的单据id
+            long pkid = item.getLong("pkid");
+
+            // 获取该基础资料的动态对象
+            DynamicObject fbasedataid = item.getDynamicObject("fbasedataid");
+
+            //获取该基础资料的id
+            currencyId = item.getLong("fbasedataid_id");
+        }
+
+        DynamicObject currency = BusinessDataServiceHelper.loadSingleFromCache(currencyId, "bd_currency");
+        String currencyNumber = currency.getString("nckd_cbsvalue");
+        if(StringUtils.isEmpty(currencyNumber)){
+            currencyNumber = "10";
+        }
+        body.append("<CCYNBR>").append(currencyNumber).append("</CCYNBR>\r\n");//币种
+
+        String openOrgNumber = accountInfo.getDynamicObject("openorg").getString("number");
+
+        QFilter filterType = new QFilter("nckd_type", QCP.equals, "org");
+        QFilter filteraNumber = new QFilter("nckd_number", QCP.equals, openOrgNumber);
+        QFilter[] filters = new QFilter[]{filterType, filteraNumber};
+        String selectProperties = "nckd_cbsnumber";
+        DynamicObject orgCompare = BusinessDataServiceHelper.loadSingle("nckd_datacompare", selectProperties, filters);
+        String cbsNumber = "";
+        if(orgCompare != null){
+            cbsNumber = orgCompare.getString("nckd_cbsnumber");
+        }
+
+        String cltnbr = getCBScltnbr(cbsUrl, cbsNumber);
+
+        //nckd_datacompare
+        body.append("<CLTNBR>").append(cltnbr).append("</CLTNBR>\r\n");//企业号
+        body.append("<CLTSEQ>").append(cbsNumber).append("</CLTSEQ>\r\n");//企业号
+
+        //账户种类:A 活期	C 协定	F 定期	N 通知	Y 活期保证金	Z 定期保证金
+        body.append("<BACKND>A</BACKND>\r\n");
+
+        String acctproperty = accountInfo.getDynamicObject("acctproperty").getString("name");
+        body.append("<EXTTX9>").append(acctproperty).append("</EXTTX9>\r\n");
+
+        //取银行账户的【账户类型】对应CBS的外部账号性质,根据基础资料映射表做对应
+        String acctstyle = accountInfo.getString("acctstyle");
+        filterType = new QFilter("nckd_type", QCP.equals, "acctstyle");
+        filteraNumber = new QFilter("nckd_number", QCP.equals, acctstyle);
+        filters = new QFilter[]{filterType, filteraNumber};
+        selectProperties = "nckd_cbsnumber";
+
+        DynamicObject acctstyleCompare = BusinessDataServiceHelper.loadSingle("nckd_datacompare", selectProperties, filters);
+        String bactype = "";
+        if(acctstyleCompare != null){
+            bactype = acctstyleCompare.getString("nckd_cbsnumber");
+        }
+        body.append("<BACTYP>").append(bactype).append("</BACTYP>\r\n");//外部账号性质
+
+        DynamicObject province = bank.getDynamicObject("province");
+        if(province != null){
+            province =  BusinessDataServiceHelper.loadSingle(province.getLong("id"),"bd_admindivision");
+            String areaCode = province.getString("areacode");
+            if(StringUtils.isNotEmpty(areaCode) && areaCode.length() > 2){
+                areaCode = areaCode.substring(0, 2);
+            }
+            areaCode = areaCode == null ? "" : areaCode;
+            body.append("<PVCCOD>").append(areaCode).append("</PVCCOD>\r\n");//省份
+        }
+
+        //地市,需要传中文名称
+        DynamicObject city = bank.getDynamicObject("city");
+        if(city != null){
+            city =  BusinessDataServiceHelper.loadSingle(city.getLong("id"),"bd_admindivision");
+            String cityName = city.getString("name");
+            cityName = cityName == null ? "" : cityName;
+            body.append("<CTYCOD>").append(cityName).append("</CTYCOD>\r\n");//城市
+        }
+
+        body.append("<FIXFLG>0</FIXFLG>\r\n");//直连查定期标志
+        body.append("<OPNCNL>0</OPNCNL>\r\n");//开户渠道 填0-柜面即可
+
+        String openDate = DateFormatUtils.format(accountInfo.getDate("opendate"), "yyyy-MM-dd");
+        body.append("<OPNDAT>").append(openDate).append("</OPNDAT>\r\n");//开户日期
+
+//        body.append("<PACLMT></PACLMT>\r\n");//协定额度
+        //A 正常, F 禁止, L 受限
+        //nckd_paycontrol
+        String payflg = accountInfo.getString("nckd_paycontrol");
+        body.append("<PAYFLG>").append(payflg).append("</PAYFLG>\r\n");//支付控制标志
+        //0 定向, 1 不定向, 2 普通, 3 加急
+        body.append("<PAYPRM>2</PAYPRM>\r\n");//支付参数
+        body.append("<STSCOD>0</STSCOD>\r\n");//账户状态
+
+        String acctname = accountInfo.getString("acctname");
+        body.append("<ACTNAM>").append(acctname).append("</ACTNAM>\r\n");//户名
+        String openBank = bank.getString("name");
+        body.append("<OPNBNK>").append(openBank).append("</OPNBNK>\r\n");//开户行
+        body.append("<RPTBAL>1</RPTBAL>\r\n");//报表余额取值
+        body.append("<VTLFLG>0</VTLFLG>\r\n");//账户标识0 实体户,1 虚拟户,2 招标管家虚拟户
+        //当账户标识为虚拟户或者招标管家虚拟户,填入对应的账户。如果账户标识为实户,则该字段无须填写
+//        body.append("<ACTACC></ACTACC>\r\n");//实体账号
+        body.append("</ACACTINFX>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+}

+ 103 - 0
main/java/kd/cosmic/jkjt/tmc/am/opplugin/bankacct/ReportOpPlugin.java

@@ -0,0 +1,103 @@
+package kd.cosmic.jkjt.tmc.am.opplugin.bankacct;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.args.AfterOperationArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.sdk.plugin.Plugin;
+
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/04/21
+ * @description 账户更新报备单操作插件
+ */
+public class ReportOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+    private static final Log logger = LogFactory.getLog(ReportOpPlugin.class);
+    private final static String KEY_ENTITY_REPORT = "nckd_am_cbs";
+    private final static String KEY_ENTITY_ACCOUNT = "am_accountbank";
+    private static final String KEY_OP_AUDIT = "audit";
+    private static final String KEY_OP_UNAUDIT = "unaudit";
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        super.beforeExecuteOperationTransaction(e);
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        super.beginOperationTransaction(e);
+    }
+
+    @Override
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        super.endOperationTransaction(e);
+    }
+
+    @Override
+    public void afterExecuteOperationTransaction(AfterOperationArgs e) {
+        super.afterExecuteOperationTransaction(e);
+
+        String opKey = e.getOperationKey();
+        if (KEY_OP_AUDIT.equals(opKey) || KEY_OP_UNAUDIT.equals(opKey)){
+            updateBankAccountReportStatus(e, opKey);
+        }
+    }
+
+    private void updateBankAccountReportStatus(AfterOperationArgs e, String type){
+        DynamicObject[] dataEntities = e.getDataEntities();
+        List<DynamicObject> listObj = new ArrayList<>();
+
+        for (DynamicObject obj : dataEntities) {
+            obj = BusinessDataServiceHelper.loadSingle(obj.getPkValue(), KEY_ENTITY_REPORT);
+            String updatetype = obj.getString("nckd_manualuptp");
+            DynamicObjectCollection reportEntryCols = obj.getDynamicObjectCollection("entryentity");
+
+            if(reportEntryCols.size() > 0){
+                for(DynamicObject reportEntry : reportEntryCols) {
+                    DynamicObject accountbank = reportEntry.getDynamicObject("nckd_accountbank");
+                    accountbank = BusinessDataServiceHelper.loadSingle(accountbank.getLong("id"), KEY_ENTITY_ACCOUNT);
+
+                    if(KEY_OP_AUDIT.equals(type)) {
+                        String remark = reportEntry.getString("nckd_remark");
+                        Date cbsDate = reportEntry.getDate("nckd_opcbsdate");
+
+                        //未开通CBS是否报备
+                        accountbank.set("nckd_isreport", true);
+                        //不开通CBS原因
+                        accountbank.set("nckd_manualupr", remark);
+                        //预计开通CBS日期
+                        accountbank.set("nckd_excbsdate", cbsDate);
+                        //手动更新类型
+                        accountbank.set("nckd_manualuptp", updatetype);
+                    } else {
+                        //未开通CBS是否报备
+                        accountbank.set("nckd_isreport", false);
+                        //不开通CBS原因
+                        accountbank.set("nckd_manualupr", null);
+                        //预计开通CBS日期
+                        accountbank.set("nckd_excbsdate", null);
+                        //手动更新类型
+                        accountbank.set("nckd_manualuptp", null);
+                    }
+
+                    if(accountbank != null) {
+                        listObj.add(accountbank);
+                    }
+                }
+            }
+        }
+
+        if(listObj.size() > 0) {
+            SaveServiceHelper.update(listObj.toArray(new DynamicObject[]{}));
+        }
+    }
+}

+ 164 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/AcctDetailDataListDataPluginExt.java

@@ -0,0 +1,164 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+import kd.bos.algo.DataSet;
+import kd.bos.db.DB;
+import kd.bos.db.DBRoute;
+import kd.bos.entity.report.AbstractReportListDataPluginExt;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.bos.event.AfterQueryEvent;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.tmc.fbp.common.helper.SnapDataHelper;
+import kd.tmc.fbp.common.util.DateUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 报表取数扩展插件
+ * 账户明细查询表:nckd_am_acctdetlrpt
+ * 1、增加字段:一级子公司,账号直连接口,直连类型
+ * @author wanghaiwu_kd
+ * @date 2025/03/06
+ */
+public class AcctDetailDataListDataPluginExt extends AbstractReportListDataPluginExt {
+    @Override
+    public void afterQuery(AfterQueryEvent event) {
+        super.afterQuery(event);
+
+        Map<String, Object> paramMap = this.transQueryParam(event.getReportQueryParam());
+
+        DataSet result = event.getDataSet();
+        result = addExtField(result, paramMap);
+
+        event.setDataSet(result);
+    }
+
+    protected Map<String, Object> transQueryParam(ReportQueryParam param) {
+        return SnapDataHelper.transQueryParam(param);
+    }
+
+    public DataSet addExtField(DataSet result, Map<String, Object> paramMap) {
+        List<Long> idList = new ArrayList(10);
+
+
+        //过滤掉截止日期已还清的记录
+        result.copy().iterator().forEachRemaining((v) -> {
+            idList.add(v.getLong("bankacctid"));
+        });
+
+        if(idList.size() > 0){
+            DataSet accountExt = queryAccounts(idList);
+
+            result = result.leftJoin(accountExt).on("bankacctid", "bankacctid").select(result.getRowMeta().getFieldNames()
+                    , new String[]{"nckd_firstorg", "nckd_interface", "nckd_connect"}).finish();
+
+            DataSet balanceDataSet = queryAccountBalance(idList, paramMap);
+
+            result = result.leftJoin(balanceDataSet).on("bankacctid", "bankacctid").select(result.getRowMeta().getFieldNames()
+                    , new String[]{"nckd_avgamt"}).finish();
+        }
+
+        return result;
+    }
+
+    private String getSelcetFileds() {
+        String bankAcctSic = "id as bankacctid, nckd_firstlevelorg.name as nckd_firstorg, case when nckd_interface = '1' then '开通' else '不开通' end as nckd_interface, " +
+                "case when nckd_connect = 'cbs' then 'CBS直连' when nckd_connect = 'kingdee' then '苍穹直连' when nckd_connect = 'rpa' then 'RPA直连' end as nckd_connect";
+        return bankAcctSic;
+    }
+
+    /**
+     * 扩展查询银行账户字段
+     * @param idList
+     * @return
+     */
+    private DataSet queryAccounts(List<Long> idList) {
+        QFilter qFilter = new QFilter("id", QCP.in, idList);
+        DataSet bankAcctSet = QueryServiceHelper.queryDataSet("BankAcctDtl", "am_accountbank", this.getSelcetFileds(), qFilter.toArray(), (String)null);
+
+        return bankAcctSet;
+    }
+
+    private DataSet queryAccountBalance(List<Long> idList, Map<String, Object> paramMap){
+        String sql = builderSQL(idList, paramMap);
+        DataSet balanceDataSet = DB.queryDataSet(this.getClass().getName(), DBRoute.of("fi"), sql);
+
+        return balanceDataSet;
+    }
+
+    private String builderSQL(List<Long> idList, Map<String, Object> paramMap){
+        StringBuilder ids = new StringBuilder();
+        for(Long id : idList){
+            if(ids.length() > 0) {
+                ids.append(" union ");
+            }
+            ids.append("select " + id.toString() + " ");
+        }
+
+        String startDate = DateUtils.formatString((Date)paramMap.get("nckd_balbegindate"), "yyyy-MM-dd");
+        String endDate = DateUtils.formatString((Date)paramMap.get("nckd_balenddate"), "yyyy-MM-dd");
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("/*dialect*/");
+        sb.append("with t_account as ( \n");
+        sb.append("select faccountid, fbankaccountnumber, to_date('2024-12-31', 'yyyy-MM-dd') fbegindate \n");
+        sb.append("from v_am_accountbanks\n");
+        sb.append("where faccountid in (" + ids + ")\n");
+        sb.append(")\n");
+        sb.append(", t_acct_init as (\n");
+        sb.append("     select t2.fbankacctid, t1.fday\n");
+        sb.append("         , case when t1.fday = t2.fbegindate then 1 else 0 end finit\n");
+        sb.append("         , round(case when t1.fday = t2.fbegindate then t2.fbalanceamt else 0 end,2) finitamount\n");
+        sb.append("     from (\n");
+        sb.append("         select fday::date \n");
+        sb.append("         from generate_series(to_date('2000-01-01','yyyy-mm-dd'), to_date('" + endDate + "','yyyy-mm-dd'), '1 day') s(fday)\n");
+        sb.append("     ) t1\n");
+        sb.append("     inner join (\n");
+        sb.append("         select t2.faccountid fbankacctid, to_date('2024-12-31', 'yyyy-MM-dd') fbegindate,t1.fbalance fbalanceamt\n");
+        sb.append("         from tk_nckd_accountbanksbalance t1\n");
+        sb.append("         inner join t_account t2 on t1.faccountnumber = t2.fbankaccountnumber\n");
+        sb.append("     ) t2 on 1 = 1 and t1.fday >= t2.fbegindate\n");
+        sb.append("     order by t2.fbankacctid, t1.fday\n");
+        sb.append(" )\n");
+        sb.append(", t_transdetail as (\n");
+        sb.append("     select t1.faccountbankid fbankacctid, t1.fbizdate fbookdate, sum(t1.fcreditamount-t1.fdebitamount) factamt \n");
+        sb.append("     from t_bei_transdetail t1 \n");
+        sb.append("     inner join t_account t2 on t1.faccountbankid = t2.faccountid\n");
+        sb.append("     where t1.fbizdate > t2.fbegindate\n");
+        sb.append("     group by t1.faccountbankid, fbizdate\n");
+        sb.append(" )\n");
+        sb.append(", t_dayamt as (\n");
+        sb.append("     select *\n");
+        sb.append("     from (\n");
+        sb.append("         select fbankacctid, fday, sum(famount) over(partition by fbankacctid order by fbankacctid, fday) fdayamt\n");
+        sb.append("         from (\n");
+        sb.append("             select fbankacctid, fday, sum(famount) famount\n");
+        sb.append("             from (\n");
+        sb.append("                 select fbankacctid, fday, finitamount famount\n");
+        sb.append("                 from t_acct_init\n");
+        sb.append("                 where finit = 1\n");
+        sb.append("                 union all\n");
+        sb.append("                 select t1.fbankacctid, t1.fday, case when t2.fbookdate is null then 0 else t2.factamt end\n");
+        sb.append("                 from t_acct_init t1\n");
+        sb.append("                 left join t_transdetail t2 on t1.fbankacctid = t2.fbankacctid and t1.fday = t2.fbookdate\n");
+        sb.append("             ) t \n");
+        sb.append("             group by t.fbankacctid, fday\n");
+        sb.append("         ) t\n");
+        sb.append("     ) t\n");
+        sb.append("     where t.fday between '" + startDate + "' and '" + endDate + "'\n");
+        sb.append("     order by fbankacctid, fday\n");
+        sb.append(" )\n");
+        sb.append(", t_dayamt_avg as (\n");
+        sb.append("     select fbankacctid bankacctid, avg(fdayamt) nckd_avgamt\n");
+        sb.append("     from t_dayamt \n");
+        sb.append("     group by fbankacctid\n");
+        sb.append(" )\n");
+        sb.append("select * from t_dayamt_avg");
+
+        return sb.toString();
+    }
+}

文件差异内容过多而无法显示
+ 90 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/AcctDetailFormListPluginExt.java


+ 344 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/AcctSumDataRptPlugin.java

@@ -0,0 +1,344 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Field;
+import kd.bos.algo.Row;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.LocaleString;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.entity.report.AbstractReportColumn;
+import kd.bos.entity.report.ReportColumn;
+import kd.bos.entity.report.ReportColumnGroup;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.tmc.am.report.bankacct.helper.AcctDataListHelper;
+import kd.tmc.am.report.bankacct.helper.QingRptFilterParamHelper;
+import kd.tmc.fbp.common.helper.TmcOrgDataHelper;
+import kd.tmc.fbp.common.util.EmptyUtil;
+import kd.tmc.fbp.report.data.AbstractTmcListDataPlugin;
+
+import java.util.*;
+
+/**
+ * @author turborao
+ * @date 2023/11/15
+ * @description 报表取数插件:账户CBS开通情况表
+ */
+public class AcctSumDataRptPlugin extends AbstractTmcListDataPlugin {
+
+    private QFilter[] filter = null;
+    private static final String[] OPEN_ORGGF = new String[]{"nckd_openorg", "nckd_bank_cate", "finorgtype"};
+    private static final String[] OPEN_ORGOF = new String[]{"number asc", "nckd_openorg desc", "finorgtype asc", "nckd_bank_cate desc"};
+
+    public AcctSumDataRptPlugin() {
+    }
+
+    @Override
+    public DataSet query(ReportQueryParam param, Object info) throws Throwable {
+
+        DataSet bizDs = null;
+        Map<String, Object> paramMap = this.transQueryParam(param);
+        String statdim = (String)paramMap.get("statdim");
+        this.filter = QingRptFilterParamHelper.initBankAcctFilter(paramMap);
+        String[] groupField = null;
+        String allTotalFieldName = null;
+        String[] orderByField = null;
+        String sumNameField = "";
+
+        bizDs = this.queryDataByOpenOrg(this.filter);
+
+        printDataset(bizDs);
+
+        allTotalFieldName = "nckd_openorg";
+        groupField = OPEN_ORGGF;
+        orderByField = OPEN_ORGOF;
+        sumNameField = "nckd_bank_cate";
+
+        DataSet dimenBizDs = this.addStatDimensionData(bizDs, groupField).orderBy(orderByField);
+
+        printDataset(dimenBizDs);
+
+        DataSet mergeDs = null;
+        List<String> gruopFields = Collections.singletonList("longnumber");
+        mergeDs = dimenBizDs.orderBy((String[])gruopFields.toArray(new String[gruopFields.size()]));
+
+        printDataset(mergeDs);
+
+        mergeDs = this.validationColumn(mergeDs);
+        if (String.valueOf(paramMap.getOrDefault("filter_stat", "")).contains("group_bankinterface")) {
+            mergeDs = mergeDs.addField("case when (bankinterface_true + bankinterface_false)=0 then 0 else (( bankinterface_true * 100 / (bankinterface_true + bankinterface_false) * 100 ) / 100) end", "bankinterface_rate");
+        }
+
+        printDataset(mergeDs);
+
+        return mergeDs;
+    }
+
+    protected DataSet queryDataByOpenOrg(QFilter[] filter) {
+        String bankAcctSic = "openorg.id nckd_openorgid,finorgtype,openorg.number number,openorg.name nckd_openorg, case when finorgtype='0' then bank.bank_cate.name else bank.name end as nckd_bank_cate, 0 sumlevel";
+        DataSet bankAcctSet = QueryServiceHelper.queryDataSet("BankAcctSum", "bd_accountbanks", bankAcctSic, filter, (String)null);
+        List<String> bankAcctgb = Arrays.asList("number", "nckd_openorg", "nckd_openorgid", "finorgtype", "nckd_bank_cate", "sumlevel");
+
+        DataSet bizDs = bankAcctSet.groupBy((String[])bankAcctgb.toArray(new String[0])).count("nckd_stat").finish();
+        List<Long> orgIdList = TmcOrgDataHelper.getAuthorizedBankOrgId(RequestContext.get().getCurrUserId(), "am", "am_acctsummaryrpt", "47150e89000000ac");
+        bizDs = AcctDataListHelper.getBizDsWithOrgTree(bizDs, "rowid", "nckd_openorgid", Collections.singletonList("nckd_stat"), new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+
+            {
+                this.put("number", "longnumber");
+                this.put("nckd_openorg", "orgname");
+                this.put("nckd_openorgid", "rowid");
+            }
+        }, new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+
+            {
+                this.put("finorgtype", "'-1'");
+                this.put("nckd_bank_cate", "''");
+            }
+        }, orgIdList);
+        bizDs = AcctDataListHelper.getSumDataSet(bizDs, Collections.singletonList("nckd_stat"), true, this);
+        return bizDs;
+    }
+
+
+
+
+    private DataSet addStatDimensionData(DataSet bizDs, String[] groupField) {
+        if (bizDs == null) {
+            return bizDs;
+        } else {
+            String sicFields = "0 as sumlevel,finorgtype,company.name company, openorg.name nckd_openorg,bank.city.id cityid, case when finorgtype='0' then bank.bank_cate.name else bank.name end as nckd_bank_cate,bank.name finorginfo, bank.country.name country, bank.province.name province, bank.city.name city, acctproperty.id, acctproperty.number, acctstyle, accttype, nckd_issetcbs issetbankinterface,case when isnull(nckd_issetcbs,'0')='0' and isnull(nckd_isreport,'0')='1' then 1 else 0 end bankinterface_back_true, case when isnull(nckd_issetcbs,'0')='0' and isnull(nckd_isreport,'0')='0' then 1  else 0 end bankinterface_back_false";
+            DataSet dsOther = QueryServiceHelper.queryDataSet("BankAcct", "bd_accountbanks", sicFields, this.filter, (String)null);
+            ReportQueryParam param = this.getQueryParam();
+            String stats = (String)param.getFilter().getFilterItem("filter_stat").getValue();
+            List<String> sumList = new ArrayList(0);
+            String[] var8 = stats.split(",");
+            int var9 = var8.length;
+
+            for(int var10 = 0; var10 < var9; ++var10) {
+                String stat = var8[var10];
+                String dimField = null;
+                String beginChangeField = null;
+                if ("group_acctpurpose".equals(stat)) {
+                    dimField = "acctproperty.id";
+                    beginChangeField = "acctproperty_";
+                } else if ("group_acctstyle".equals(stat)) {
+                    dimField = "acctstyle";
+                } else if ("group_accttype".equals(stat)) {
+                    dimField = "accttype";
+                } else if ("group_bankinterface".equals(stat)) {
+                    dimField = "issetbankinterface";
+                    beginChangeField = "bankinterface_";
+                }
+
+                if (EmptyUtil.isEmpty(beginChangeField)) {
+                    beginChangeField = dimField + "_";
+                }
+
+                String[] dimFields = {"bankinterface_back_false","bankinterface_back_true"};
+
+                DataSet ds = this.initStatdimensionDataSet(dsOther, groupField, dimField);
+                DataSet dsTmp = this.initStatdimensionDataSet(dsOther, OPEN_ORGGF, dimFields);
+
+                System.out.println("处理开通CBS dataSet:========================");
+                printDataset(ds);
+                System.out.println("处理开通报备 dataSet:========================");
+                printDataset(dsTmp);
+
+                ds = this.ChangeRowToColDataSet(ds, dimField, String.join(",", groupField), beginChangeField);
+
+                System.out.println("处理开通CBS 行转列 dataSet:========================");
+                printDataset(ds);
+                if (ds == null) {
+                    return bizDs;
+                }
+
+                Field[] fields = ds.getRowMeta().getFields();
+                StringBuilder sb = new StringBuilder();
+                Field[] var17 = fields;
+                int var18 = fields.length;
+
+                for(int var19 = 0; var19 < var18; ++var19) {
+                    Field field = var17[var19];
+                    String fieldName = field.getName();
+                    sb.append(fieldName).append(',');
+                    if (fieldName.startsWith(beginChangeField)) {
+                        sumList.add(fieldName);
+                    }
+                }
+                sumList.add("bankinterface_back_false");
+                sumList.add("bankinterface_back_true");
+
+                String substring = sb.substring(0, sb.length() - 1);
+                ds = ds.select(substring);
+                Field[] fields1 = bizDs.getRowMeta().getFields();
+                Field[] fieds2 = ds.getRowMeta().getFields();
+                bizDs = this.mergeDataSet(bizDs, ds, groupField, groupField);
+
+                System.out.println("合并 dataSet1:========================");
+                this.printDs(bizDs);
+                bizDs = this.mergeDataSet(bizDs, dsTmp, OPEN_ORGGF, groupField);
+                System.out.println("合并 dataSet2:========================");
+                this.printDs(bizDs);
+            }
+
+            groupField[groupField.length - 1] = this.sumField;
+            if (sumList.size() > 0 && Arrays.asList("company", "openorg").contains((String)param.getFilter().getFilterItem("statdim").getValue())) {
+                bizDs = AcctDataListHelper.getSumDataSet(bizDs, sumList, false, this);
+            }
+
+            return bizDs;
+        }
+
+
+    }
+
+    private DataSet initStatdimensionDataSet(DataSet ds, String[] groupField, String dimGroupField) {
+        DataSet temp = ds.copy();
+        List<String> groupFieldList = new ArrayList();
+        Collections.addAll(groupFieldList, groupField);
+        groupFieldList.add(dimGroupField);
+        return temp.groupBy((String[])groupFieldList.toArray(new String[groupFieldList.size()])).count("count").finish();
+    }
+    private DataSet initStatdimensionDataSet(DataSet ds, String[] groupField, String[] dimGroupField) {
+        DataSet temp = ds.copy();
+        List<String> groupFieldList = new ArrayList();
+        Collections.addAll(groupFieldList, groupField);
+
+        return temp.groupBy((String[])groupFieldList.toArray(new String[groupFieldList.size()])).sum("bankinterface_back_false").sum("bankinterface_back_true").finish();
+    }
+
+    private DataSet validationColumn(DataSet ds) {
+        Field[] fields = ds.getRowMeta().getFields();
+        Set<String> fieldSet = new HashSet(fields.length);
+        Field[] var4 = fields;
+        int var5 = fields.length;
+
+        for(int var6 = 0; var6 < var5; ++var6) {
+            Field field = var4[var6];
+            fieldSet.add(field.getName());
+        }
+
+        String selectFields = String.join(",", (CharSequence[])fieldSet.toArray(new String[0]));
+        if (!fieldSet.contains("bankinterface_true")) {
+            ds = ds.copy().select(selectFields + ",0 as bankinterface_true");
+            selectFields = selectFields + ",bankinterface_true";
+        }
+
+        if (!fieldSet.contains("bankinterface_false")) {
+            ds = ds.copy().select(selectFields + ",0 as bankinterface_false");
+        }
+
+        return ds;
+    }
+
+    public List<AbstractReportColumn> getColumns(List<AbstractReportColumn> columns) throws Throwable {
+        Map<String, String> acctPurposeSet = new LinkedHashMap();
+        Set<String> acctStyleSet = new HashSet();
+        Set<String> acctTypeSet = new HashSet();
+        DataSet ds = this.queryBankAcctStatField(this.filter);
+
+        printDataset(ds);
+
+        while(ds.hasNext()) {
+            Row row = ds.next();
+            if (EmptyUtil.isNoEmpty(row.get("acctproperty.id"))) {
+                acctPurposeSet.put(row.getString("acctproperty.id"), row.getString("acctproperty.name"));
+            }
+
+            if (EmptyUtil.isNoEmpty(row.get("acctstyle"))) {
+                acctStyleSet.add(row.getString("acctstyle"));
+            }
+
+            if (EmptyUtil.isNoEmpty(row.get("accttype"))) {
+                acctTypeSet.add(row.getString("accttype"));
+            }
+        }
+
+        ReportQueryParam param = this.getQueryParam();
+        String stats = (String)param.getFilter().getFilterItem("filter_stat").getValue();
+        String[] var8 = stats.split(",");
+        int var9 = var8.length;
+
+        for(int var10 = 0; var10 < var9; ++var10) {
+            String stat = var8[var10];
+            if ("group_acctpurpose".equals(stat)) {
+                //this.createAcctPurposeColumn(acctPurposeSet, columns);
+            } else if ("group_acctstyle".equals(stat)) {
+               // this.createAcctStyleColumn(acctStyleSet, columns);
+            } else if ("group_accttype".equals(stat)) {
+                //this.createAcctTypeColumn(acctTypeSet, columns);
+            } else if ("group_bankinterface".equals(stat)) {
+                this.createBankInterfaceColumn(columns);
+            }
+        }
+
+        return columns;
+    }
+
+    private List<AbstractReportColumn> createBankInterfaceColumn(List<AbstractReportColumn> columns) {
+        Iterator var2 = columns.iterator();
+
+        while(var2.hasNext()) {
+            AbstractReportColumn column = (AbstractReportColumn)var2.next();
+            if (column instanceof ReportColumnGroup && ((ReportColumnGroup)column).getCaption().getLocaleValue().equals("CBS银企接口")) {
+                ReportColumn col = new ReportColumn();
+                col.setFieldKey("bankinterface_true");
+                col.setFieldType("integer");
+                col.setCaption(new LocaleString(ResManager.loadKDString("开通", "AcctSumDataListPlugin_4", "tmc-am-report", new Object[0])));
+                col.setZeroShow(true);
+                ((ReportColumnGroup)column).getChildren().add(col);
+                ReportColumn col2 = new ReportColumn();
+                col2.setFieldKey("bankinterface_false");
+                col2.setFieldType("integer");
+                col2.setCaption(new LocaleString(ResManager.loadKDString("未开通", "AcctSumDataListPlugin_5", "tmc-am-report", new Object[0])));
+                col2.setZeroShow(true);
+                ((ReportColumnGroup)column).getChildren().add(col2);
+
+                /**
+                 * 增加开通报备情况字段
+                 */
+                ReportColumn col4 = new ReportColumn();
+                col4.setFieldKey("bankinterface_back_true");
+                col4.setFieldType("integer");
+                col4.setCaption(new LocaleString("其中:未开通已报备"));
+                col4.setZeroShow(true);
+                ((ReportColumnGroup)column).getChildren().add(col4);
+
+                ReportColumn col5 = new ReportColumn();
+                col5.setFieldKey("bankinterface_back_false");
+                col5.setFieldType("integer");
+                col5.setCaption(new LocaleString("其中:未开通未报备"));
+                col5.setZeroShow(true);
+                ((ReportColumnGroup)column).getChildren().add(col5);
+
+                ReportColumn col3 = new ReportColumn();
+                col3.setCaption(new LocaleString(ResManager.loadKDString("开通率(%)", "AcctSumDataListPlugin_6", "tmc-am-report", new Object[0])));
+                col3.setFieldType("integer");
+                col3.setZeroShow(true);
+                col3.setFieldKey("bankinterface_rate");
+                ((ReportColumnGroup)column).getChildren().add(col3);
+
+
+
+            }
+        }
+
+        return columns;
+    }
+
+    protected DataSet queryBankAcctStatField(QFilter[] filter) {
+        String bankAcctSic = "acctproperty.id, acctproperty.number, acctproperty.name, acctstyle, accttype";
+        return QueryServiceHelper.queryDataSet("BankAcct", "bd_accountbanks", bankAcctSic, filter, "acctproperty.number");
+    }
+
+    protected void printDataset(DataSet ds){
+        DataSet temp = ds.copy();
+        temp.print(true);
+    }
+
+
+}

+ 231 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/AcctSumFormListRptPlugin.java

@@ -0,0 +1,231 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.resource.ResManager;
+import kd.bos.dataentity.serialization.SerializationUtils;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.report.*;
+import kd.bos.form.ShowType;
+import kd.bos.form.events.HyperLinkClickEvent;
+import kd.bos.form.events.HyperLinkClickListener;
+import kd.bos.form.field.BasedataEdit;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.report.ReportList;
+import kd.bos.report.ReportShowParameter;
+import kd.bos.report.events.TreeReportListEvent;
+import kd.bos.report.plugin.AbstractReportFormPlugin;
+import kd.tmc.am.common.errorcode.BankAcctErrorCode;
+import kd.tmc.am.common.exception.AmException;
+import kd.tmc.am.report.bankacct.helper.AcctDataListHelper;
+import kd.tmc.fbp.common.helper.TmcOrgDataHelper;
+import kd.tmc.fbp.common.util.EmptyUtil;
+
+import java.util.*;
+
+/**
+ * @author turborao
+ * @date 2023/06/05
+ * @description 报表表单插件:账户CBS开通情况表
+ */
+public class AcctSumFormListRptPlugin extends AbstractReportFormPlugin implements HyperLinkClickListener {
+
+    private Map<String, Object> paramMap = null;
+
+    public AcctSumFormListRptPlugin() {
+    }
+
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        ReportList reportlist = (ReportList)this.getView().getControl("reportlistap");
+        reportlist.addHyperClickListener(this);
+    }
+
+    public void setTreeReportList(TreeReportListEvent event) {
+        super.setTreeReportList(event);
+        ///String statdim = (String)this.getModel().getValue("filter_statdim");
+        String statdim = "openorg";
+        event.setTreeReportList(true);
+        if (Arrays.asList("company", "openorg").contains(statdim)) {
+            event.setTreeExpandColId("nckd_openorg");
+        } else {
+            event.setTreeExpandColId("number");
+        }
+
+    }
+
+    public void beforeQuery(ReportQueryParam param) {
+        if (param.getFilter().getFilterItem("filter_company_id") == null) {
+            throw new AmException(BankAcctErrorCode.COMMON, new String[]{ResManager.loadKDString("申请组织必录。", "RestrictedFundsFormListPlugin_15", "tmc-am-report", new Object[0])});
+        } else {
+            ///String statdim = (String)this.getModel().getValue("filter_statdim");
+            String statdim = "openorg";
+            FilterItemInfo filterItem = param.getFilter().getFilterItem("statdim");
+            if (filterItem == null) {
+                param.getFilter().addFilterItem("statdim", statdim);
+            } else {
+                filterItem.setValue(statdim);
+            }
+
+            if (param.getFilter().getFilterItem("filter_stat") == null) {
+                param.getFilter().addFilterItem("filter_stat", "group_bankinterface", QCP.equals);
+            }
+
+            this.initQueryColumn(statdim, param);
+            super.beforeQuery(param);
+        }
+    }
+
+    public void initDefaultQueryParam(ReportQueryParam queryParam) {
+        super.initDefaultQueryParam(queryParam);
+        RequestContext context = RequestContext.get();
+        BasedataEdit company = (BasedataEdit)this.getControl("filter_company");
+        //this.getView().setVisible(false,"filter_company","filter_statdim");
+        String appId = this.getView().getFormShowParameter().getAppId();
+        List<Long> comIdList = TmcOrgDataHelper.getAuthorizedBankOrgId(context.getCurrUserId(), appId, this.getModel().getDataEntityType().getName(), "47150e89000000ac");
+        company.setQFilter(new QFilter("id", "in", comIdList));
+        BasedataEdit openorg = (BasedataEdit)this.getControl("filter_openorg");
+        List<Long> orgIdList = TmcOrgDataHelper.getAuthorizedAccountLegalOrgId(context.getCurrUserId(), appId, this.getModel().getDataEntityType().getName(), "47150e89000000ac");
+        openorg.setQFilter(new QFilter("id", "in", orgIdList));
+    }
+
+    public void hyperLinkClick(HyperLinkClickEvent event) {
+        if ("nckd_stat".equals(event.getFieldName())) {
+            int rowIndex = event.getRowIndex();
+            ReportList rptList = (ReportList)event.getSource();
+            DynamicObject row = rptList.getReportModel().getRowData(rowIndex);
+            ReportQueryParam queryParam = this.getQueryParam();
+            FilterInfo filter = queryParam.getFilter();
+            Map<String, QFilter> commFilters = new HashMap();
+            String statdim = (String)this.getModel().getValue("filter_statdim");
+            String sumlevel = row.getString("sumlevel");
+
+            if (!sumlevel.equals("2")) {
+                if (sumlevel.equals("1")) {
+                    row = rptList.getReportModel().getRowData(rowIndex - 1);
+                } else {
+                    commFilters.putAll(this.getBankCateQfilter(row));
+                }
+
+                commFilters.put("filter_openorg", new QFilter("openorg.id", "in", new Long[]{Long.valueOf(row.get("openorgid").toString())}));
+            }
+
+
+            ReportShowParameter param = new ReportShowParameter();
+            param.getCustomParams().put("statdim", statdim);
+            param.getCustomParams().put("filter", SerializationUtils.serializeToBase64(filter));
+            param.getCustomParams().put("commFilters", SerializationUtils.serializeToBase64(commFilters));
+            param.setFormId("am_acctdetlrpt");
+            param.getOpenStyle().setShowType(ShowType.MainNewTabPage);
+            this.getView().showForm(param);
+        }
+
+    }
+
+    private void initQueryColumn(String statdim, ReportQueryParam param) {
+        Map<String, Object> paramMap = this.getParamMap(param);
+        String stat = (String) paramMap.get("filter_stat");
+        StringBuilder fields = new StringBuilder();
+        switch (statdim) {
+
+            case "openorg":
+                this.visableListColumn("nckd_openorg,nckd_bank_cate,nckd_stat,nckd_group_bankinterface");
+                fields.append(",");
+                fields.append(ResManager.loadKDString("开户公司", "AcctSumFormListPlugin_14", "tmc-am-report", new Object[0]));
+                fields.append(",");
+                fields.append(ResManager.loadKDString("金融机构", "AcctSumFormListPlugin_12", "tmc-am-report", new Object[0]));
+                fields.append(",");
+                fields.append(ResManager.loadKDString("开户公司ID", "AcctSumFormListPlugin_15", "tmc-am-report", new Object[0]));
+                break;
+            default:
+
+        }
+        fields.append(",");
+        fields.append(ResManager.loadKDString("汇总", "AcctSumFormListPlugin_21", "tmc-am-report", new Object[0]));
+        fields.append(",");
+        fields.append(ResManager.loadKDString("金融机构类别", "AcctSumFormListPlugin_17", "tmc-am-report", new Object[0]));
+        fields.append(",");
+        fields.append(ResManager.loadKDString("排序", "AcctSumFormListPlugin_22", "tmc-am-report", new Object[0]));
+
+        fields.append(",");
+        fields.append("CBS银企接口");
+
+        String fieldStr = fields.toString();
+        if (EmptyUtil.isNoEmpty(fieldStr)) {
+            this.rebuildColumn(fieldStr.split(","));
+        }
+
+    }
+
+    private void rebuildColumn(String[] feilds) {
+        ReportList listTable = (ReportList)this.getView().getControl("reportlistap");
+        AcctDataListHelper.rebuildColumn(feilds, listTable);
+    }
+
+    private Map<String, QFilter> getBankCateQfilter(DynamicObject row) {
+        Map<String, QFilter> map = new HashMap();
+        String finOrgType = row.getString("finorgtype");
+        String bankcate = row.getString("bank_cate");
+        QFilter bankQFilter;
+        if ("0".equals(finOrgType)) {
+            bankQFilter = new QFilter("bank.bank_cate.name", "=", bankcate);
+            if (StringUtils.isBlank(bankcate)) {
+                bankQFilter.or(QFilter.isNull("bank.bank_cate.name"));
+            }
+
+            map.put("bank.bank_cate.name", bankQFilter);
+            map.put("finorgtype", new QFilter("finorgtype", "=", finOrgType));
+        } else if (!"-1".equals(finOrgType)) {
+            bankQFilter = new QFilter("bank.name", "=", bankcate);
+            if (StringUtils.isBlank(bankcate)) {
+                bankQFilter.or(QFilter.isNull("bank.name"));
+            }
+
+            map.put("bank.name", bankQFilter);
+            map.put("finorgtype", new QFilter("finorgtype", "=", finOrgType));
+        }
+
+        return map;
+    }
+
+    private void visableListColumn(String fields) {
+        ReportList listTable = (ReportList)this.getView().getControl("reportlistap");
+        Iterator<AbstractReportColumn> it = listTable.getColumns().iterator();
+
+        while(true) {
+            while(it.hasNext()) {
+                AbstractReportColumn column = (AbstractReportColumn)it.next();
+                if (column instanceof ReportColumn) {
+                    if (fields.indexOf(((ReportColumn)column).getFieldKey()) >= 0) {
+                        ((ReportColumn)column).setHide(false);
+                    } else {
+                        ((ReportColumn)column).setHide(true);
+                    }
+                } else if (column instanceof ReportColumnGroup) {
+                    Iterator var5 = ((ReportColumnGroup)column).getChildren().iterator();
+
+                    while(var5.hasNext()) {
+                        AbstractReportColumn child = (AbstractReportColumn)var5.next();
+                        if (fields.indexOf(((ReportColumn)child).getFieldKey()) >= 0) {
+                            ((ReportColumn)child).setHide(false);
+                        } else {
+                            ((ReportColumn)child).setHide(true);
+                        }
+                    }
+                }
+            }
+
+            return;
+        }
+    }
+
+    private Map<String, Object> getParamMap(ReportQueryParam param) {
+        if (this.paramMap == null) {
+            this.paramMap = AcctDataListHelper.transQueryParam(param);
+        }
+
+        return this.paramMap;
+    }
+
+}

+ 25 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/AcctUsageDataListExtPlugin.java

@@ -0,0 +1,25 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+import kd.bos.algo.*;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.tmc.am.report.bankacct.data.AcctUsageDataListPlugin;
+
+/**
+ * @author wanghaiwu_kd
+ * @Date 2024/04/22
+ * 插件表述:报表取数插件
+ * 表单标识:账户使用情况分析表(nckd_am_acctusagerpt_ext)
+ */
+public class AcctUsageDataListExtPlugin extends AcctUsageDataListPlugin {
+    public AcctUsageDataListExtPlugin() {
+    }
+
+    public DataSet query(ReportQueryParam param, Object obj) throws Throwable {
+        DataSet result = super.query(param, obj);
+
+        //重写标准,增加排序条件:bankcateid desc
+        result = result.orderBy(new String[]{"longnumber", "bankcateid desc", "flow", "stroke_count desc", "amount desc"});
+
+        return result;
+    }
+}

+ 186 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/ConcreteStrategyDetailEx.java

@@ -0,0 +1,186 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+import kd.bos.orm.query.QFilter;
+import kd.tmc.am.report.bankacct.data.usage.ConcreteStrategyDetail;
+import kd.tmc.fbp.common.util.EmptyUtil;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
+public class ConcreteStrategyDetailEx extends ConcreteStrategyDetail {
+    public ConcreteStrategyDetailEx(List<QFilter> qFilterList, Map<String, Object> paramMap, String dimension) {
+        super(qFilterList, paramMap, dimension);
+    }
+
+    public BigDecimal getBigDecimal(Object objValue) {
+        BigDecimal ret = null;
+
+        if(objValue == null || objValue.equals("")){
+            return BigDecimal.ZERO;
+        }
+        if(objValue instanceof BigDecimal) {
+            ret = (BigDecimal) objValue;
+        } else if( objValue instanceof String ) {
+            ret = new BigDecimal( (String) objValue );
+        } else if( objValue instanceof BigInteger) {
+            ret = new BigDecimal( (BigInteger) objValue );
+        } else if( objValue instanceof Number ) {
+            ret = new BigDecimal( ((Number)objValue).doubleValue() );
+        } else {
+            throw new ClassCastException("Not possible to coerce ["+ objValue + "] from class "+ objValue.getClass()+" into a BigDecimal.");
+        }
+
+        return ret;
+    }
+
+    @Override
+    public void setFilter() {
+        super.setFilter();
+
+        if (!EmptyUtil.isEmpty(paramMap.get("nckd_amountitem"))) {
+            setAmountFilter();
+        }
+    }
+
+    private void setAmountFilter(){
+        String flow = "";
+        if (!EmptyUtil.isEmpty(this.paramMap.get("filter_flow"))) {
+            flow = String.valueOf(this.paramMap.get("filter_flow"));
+        }
+        String amountField = "debitamount";
+        switch ((String)paramMap.get("nckd_amountitem")) {
+            case "0"://等于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, "=");
+                }
+                break;
+            case "1"://大于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, ">");
+                }
+                break;
+            case "2"://小于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, "<");
+                }
+                break;
+            case "3"://介于
+                if (paramMap.get("nckd_stramount") != null) {
+                    super.getFilterList().add(new QFilter(amountField, ">=", getBigDecimal(paramMap.get("nckd_stramount"))));
+                }
+                if (paramMap.get("nckd_endamount") != null) {
+                    super.getFilterList().add(new QFilter(amountField, "<=", getBigDecimal(paramMap.get("nckd_endamount"))));
+                }
+                break;
+            case "4"://不介于
+                QFilter qFilter = null;
+                if (paramMap.get("nckd_stramount") != null) {
+                    qFilter = new QFilter(amountField, "<", getBigDecimal(paramMap.get("nckd_stramount")));
+                }
+                if (paramMap.get("nckd_endamount") != null) {
+                    if(qFilter == null){
+                        qFilter = new QFilter(amountField, ">", getBigDecimal(paramMap.get("nckd_endamount")));
+                    } else {
+                        qFilter.or(new QFilter(amountField, ">", getBigDecimal(paramMap.get("nckd_endamount"))));
+                    }
+                }
+                super.getFilterList().add(qFilter);
+                break;
+        }
+    }
+
+    private void setAmountFilterSingle(String flow, String operator){
+        BigDecimal amount = getBigDecimal(paramMap.get("nckd_amount"));
+
+        if ("in".equals(flow)) {
+            super.getFilterList().add(new QFilter("creditamount", operator, amount));
+        } else if ("out".equals(flow)) {
+            super.getFilterList().add(new QFilter("debitamount", operator, amount));
+        } else {
+            QFilter qFilter = new QFilter("creditamount", operator, amount).and(new QFilter("creditamount", ">", 0));
+            qFilter.or(new QFilter("debitamount", operator, amount).and(new QFilter("debitamount", ">", 0)));
+            super.getFilterList().add(qFilter);
+        }
+    }
+
+    private void setAmountFilterBetween(String flow){
+        BigDecimal amount = getBigDecimal(paramMap.get("nckd_amount"));
+        if ("in".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                super.getFilterList().add(new QFilter("creditamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                super.getFilterList().add(new QFilter("creditamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+            }
+        } else if ("out".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                super.getFilterList().add(new QFilter("debitamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                super.getFilterList().add(new QFilter("debitamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+        } else {
+            if (paramMap.get("nckd_stramount") != null) {
+                QFilter qFilter = new QFilter("creditamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+                super.getFilterList().add(qFilter);
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                QFilter qFilter = new QFilter("creditamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+                super.getFilterList().add(qFilter);
+            }
+        }
+    }
+
+    private void setAmountFilterNotBetween(String flow){
+        QFilter qFilter = null;
+        if ("in".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("creditamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+                }
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        } else if ("out".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("debitamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+                }
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        } else {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("creditamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+                }
+                qFilter.or(new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        }
+    }
+}

+ 186 - 0
main/java/kd/cosmic/jkjt/tmc/am/report/ConcreteStrategyJournalEx.java

@@ -0,0 +1,186 @@
+package kd.cosmic.jkjt.tmc.am.report;
+
+import kd.bos.orm.query.QFilter;
+import kd.tmc.am.report.bankacct.data.usage.ConcreteStrategyJournal;
+import kd.tmc.fbp.common.util.EmptyUtil;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
+public class ConcreteStrategyJournalEx extends ConcreteStrategyJournal {
+    public ConcreteStrategyJournalEx(List<QFilter> qFilterList, Map<String, Object> paramMap, String dimension) {
+        super(qFilterList, paramMap, dimension);
+    }
+
+    public BigDecimal getBigDecimal(Object objValue) {
+        BigDecimal ret = null;
+
+        if(objValue == null || objValue.equals("")){
+            return BigDecimal.ZERO;
+        }
+        if(objValue instanceof BigDecimal) {
+            ret = (BigDecimal) objValue;
+        } else if( objValue instanceof String ) {
+            ret = new BigDecimal( (String) objValue );
+        } else if( objValue instanceof BigInteger) {
+            ret = new BigDecimal( (BigInteger) objValue );
+        } else if( objValue instanceof Number ) {
+            ret = new BigDecimal( ((Number)objValue).doubleValue() );
+        } else {
+            throw new ClassCastException("Not possible to coerce ["+ objValue + "] from class "+ objValue.getClass()+" into a BigDecimal.");
+        }
+
+        return ret;
+    }
+
+    @Override
+    public void setFilter() {
+        super.setFilter();
+
+        if (!EmptyUtil.isEmpty(paramMap.get("nckd_amountitem"))) {
+            setAmountFilter();
+        }
+    }
+
+    private void setAmountFilter(){
+        String flow = "";
+        if (!EmptyUtil.isEmpty(this.paramMap.get("filter_flow"))) {
+            flow = String.valueOf(this.paramMap.get("filter_flow"));
+        }
+        String amountField = "debitamount";
+        switch ((String)paramMap.get("nckd_amountitem")) {
+            case "0"://等于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, "=");
+                }
+                break;
+            case "1"://大于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, ">");
+                }
+                break;
+            case "2"://小于
+                if (paramMap.get("nckd_amount") != null) {
+                    setAmountFilterSingle(flow, "<");
+                }
+                break;
+            case "3"://介于
+                if (paramMap.get("nckd_stramount") != null) {
+                    super.getFilterList().add(new QFilter(amountField, ">=", getBigDecimal(paramMap.get("nckd_stramount"))));
+                }
+                if (paramMap.get("nckd_endamount") != null) {
+                    super.getFilterList().add(new QFilter(amountField, "<=", getBigDecimal(paramMap.get("nckd_endamount"))));
+                }
+                break;
+            case "4"://不介于
+                QFilter qFilter = null;
+                if (paramMap.get("nckd_stramount") != null) {
+                    qFilter = new QFilter(amountField, "<", getBigDecimal(paramMap.get("nckd_stramount")));
+                }
+                if (paramMap.get("nckd_endamount") != null) {
+                    if(qFilter == null){
+                        qFilter = new QFilter(amountField, ">", getBigDecimal(paramMap.get("nckd_endamount")));
+                    } else {
+                        qFilter.or(new QFilter(amountField, ">", getBigDecimal(paramMap.get("nckd_endamount"))));
+                    }
+                }
+                super.getFilterList().add(qFilter);
+                break;
+        }
+    }
+
+    private void setAmountFilterSingle(String flow, String operator){
+        BigDecimal amount = getBigDecimal(paramMap.get("nckd_amount"));
+
+        if ("in".equals(flow)) {
+            super.getFilterList().add(new QFilter("creditamount", operator, amount));
+        } else if ("out".equals(flow)) {
+            super.getFilterList().add(new QFilter("debitamount", operator, amount));
+        } else {
+            QFilter qFilter = new QFilter("creditamount", operator, amount).and(new QFilter("creditamount", ">", 0));
+            qFilter.or(new QFilter("debitamount", operator, amount).and(new QFilter("debitamount", ">", 0)));
+            super.getFilterList().add(qFilter);
+        }
+    }
+
+    private void setAmountFilterBetween(String flow){
+        BigDecimal amount = getBigDecimal(paramMap.get("nckd_amount"));
+        if ("in".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                super.getFilterList().add(new QFilter("creditamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                super.getFilterList().add(new QFilter("creditamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+            }
+        } else if ("out".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                super.getFilterList().add(new QFilter("debitamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                super.getFilterList().add(new QFilter("debitamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+        } else {
+            if (paramMap.get("nckd_stramount") != null) {
+                QFilter qFilter = new QFilter("creditamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", ">=", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+                super.getFilterList().add(qFilter);
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                QFilter qFilter = new QFilter("creditamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", "<=", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+                super.getFilterList().add(qFilter);
+            }
+        }
+    }
+
+    private void setAmountFilterNotBetween(String flow){
+        QFilter qFilter = null;
+        if ("in".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("creditamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+                }
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        } else if ("out".equals(flow)) {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("debitamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+                }
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        } else {
+            if (paramMap.get("nckd_stramount") != null) {
+                qFilter = new QFilter("creditamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("creditamount", ">", 0));
+                qFilter.or(new QFilter("debitamount", "<", getBigDecimal(paramMap.get("nckd_stramount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if (paramMap.get("nckd_endamount") != null) {
+                if(qFilter == null){
+                    qFilter = new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0));
+                } else {
+                    qFilter.or(new QFilter("creditamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("creditamount", ">", 0)));
+                }
+                qFilter.or(new QFilter("debitamount", ">", getBigDecimal(paramMap.get("nckd_endamount"))).and(new QFilter("debitamount", ">", 0)));
+            }
+            if(qFilter != null) {
+                super.getFilterList().add(qFilter);
+            }
+        }
+    }
+}

+ 39 - 0
main/java/kd/cosmic/jkjt/tmc/am/servicehelper/ServiceFactory.java

@@ -0,0 +1,39 @@
+package kd.cosmic.jkjt.tmc.am.servicehelper;
+
+import kd.bos.dataentity.TypesContainer;
+import kd.bos.dataentity.resource.ResManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+public class ServiceFactory {
+    private static Map<String, String> serviceMap = new HashMap();
+
+    public ServiceFactory() {
+
+    }
+
+    public static <T> T getService(Class<T> clazz) {
+        return (T) getService(clazz.getSimpleName());
+    }
+
+    public static void putService(String serviceName, String serviceImpl) {
+        serviceMap.put(serviceName, serviceImpl);
+    }
+
+    public static Object getService(String serviceName) {
+        String className = (String)serviceMap.get(serviceName);
+        if (className == null) {
+            throw new RuntimeException(String.format(ResManager.loadKDString("%s对应的服务实现未找到", "ServiceFactory_0", "jkjt-tmc-servicehelper", new Object[0]), serviceName));
+        } else {
+            return TypesContainer.getOrRegisterSingletonInstance(className);
+        }
+    }
+
+    static {
+        serviceMap.put("UpdateAccountCBSService", "kd.cosmic.jkjt.tmc.am.mservice.UpdateAccountCBSServiceImpl");
+    }
+}

+ 349 - 0
main/java/kd/cosmic/jkjt/tmc/am/task/AccountOpenCBSWarningTask.java

@@ -0,0 +1,349 @@
+package kd.cosmic.jkjt.tmc.am.task;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.cosmic.jkjt.msg.feishu.SSLClient;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import java.rmi.ConnectException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * 插件说明:调度任务类,查询未同开CBS的银行账户并发送消息至飞书
+ * @author wanghaiwu_kd
+ * @date 2024-09-27
+ */
+public class AccountOpenCBSWarningTask extends AbstractTask {
+    private static final Log logger = LogFactory.getLog(AccountOpenCBSWarningTask.class);
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        QFilter qFilter = new QFilter("status", QCP.equals, "C");
+        qFilter.and(new QFilter("enable", QCP.equals, true));
+        qFilter.and(new QFilter("nckd_issetcbs", QCP.equals, false));
+        qFilter.and(new QFilter("finorgtype", QCP.equals, "0"));
+        qFilter.and(new QFilter("manager", QCP.is_notnull, null));
+//        qFilter.and(new QFilter("number", QCP.equals, "36050100195000000223"));
+
+        DataSet managers = QueryServiceHelper.queryDataSet("algoKey", "am_accountbank", "manager.id", qFilter.toArray(), (String)null).distinct();
+
+        if(managers.isEmpty()){
+            return;
+        }
+
+        String selectField = "nckd_feishuappid, nckd_feishuappsecret, nckd_accounttempid, nckd_accountver";
+        DynamicObject[] commonParams = BusinessDataServiceHelper.load("nckd_commonparams", selectField, null);
+        if(commonParams.length == 0){
+            return;
+        }
+
+        String appId = commonParams[0].getString("nckd_feishuappid");
+        String appSecret = commonParams[0].getString("nckd_feishuappsecret");
+        String templateId = commonParams[0].getString("nckd_accounttempid");
+        String templateVersion = commonParams[0].getString("nckd_accountver");
+
+        Iterator var13 = managers.iterator();
+
+        while(var13.hasNext()) {
+            Row row = (Row)var13.next();
+            Long managerId = row.getLong("manager.id");
+
+            String token = getToken(appId, appSecret);
+            DynamicObject userInfo = BusinessDataServiceHelper.loadSingle(managerId,"bos_user");
+            String userid = getUserId(userInfo.getString("phone"),token);
+
+            QFilter managerFilter = new QFilter("manager", QCP.equals, managerId);
+
+            DynamicObject[] accounts = BusinessDataServiceHelper.load("am_accountbank", "id, acctname, name, bankaccountnumber, manager", new QFilter[]{qFilter, managerFilter});
+
+            JSONArray jsonArray = new JSONArray();
+            for(DynamicObject account : accounts){
+                JSONObject json = new JSONObject();
+
+                json.put("account_name", account.getString("acctname"));
+                json.put("account_shortname", account.getString("name"));
+                json.put("account_number", account.getString("bankaccountnumber"));
+
+                jsonArray.add(json);
+            }
+
+            jsonArray.toJSONString();
+
+            //发送飞书消息
+            sendCustomMessage(jsonArray, userid, token, templateId, templateVersion);
+        }
+    }
+
+    /**
+     * 发送飞书卡片消息
+     */
+    public void sendCustomMessage(JSONArray jsonArray, String userid, String token, String templateId, String templateVersion){
+        //列表数据变量
+        JSONObject variable = new JSONObject();
+        variable.put("table_raw_array_1", jsonArray);
+
+        //消息卡片
+        JSONObject data = new JSONObject();
+        data.put("template_id", templateId);
+        data.put("template_version_name", templateVersion);
+        data.put("template_variable", variable);
+
+        JSONObject content = new JSONObject();
+        content.put("type", "template");
+        content.put("data", data);
+
+        JSONObject m = new JSONObject();
+        m.put("receive_id", userid);
+        m.put("msg_type","interactive");
+        m.put("content", content.toJSONString());
+//        m.put("uuid", uuid);
+
+        String userIdUrl = "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id";
+        try {
+            logger.info("门户发送消息:"+m.toString());
+
+            //发送普通消息
+            String uuid = saveMsgLog(userid, "银行账户开通预警消息", "银行账户开通预警消息", m, "custom", null);
+
+            String response = doPostByHttpClient(userIdUrl, m.toString(),true, token, 3);
+            logger.info("推送审批bot,url:{}, 结果:{}", userIdUrl, response);
+            JSONObject userRes = JSONObject.parseObject(response);
+
+            if (!ObjectUtils.isEmpty(uuid)) {
+                // 将发送消息的结果回填到数据库日志记录
+                String sendStatus = "fail";
+                if(userRes.getIntValue("code") == 0){
+                    sendStatus = "success";
+                }
+                updateMsgLog(uuid, response, sendStatus);
+            }
+
+
+            logger.info("门户消息发送成功,发送消息:{}, 结果:{}", m.toJSONString(), response);
+        } catch (ConnectException e) {
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取飞书token
+     * @return
+     */
+    public static String getToken(String appId, String appSercret){
+        String returnstr = "";
+        String url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
+        JSONObject tokenJson = new JSONObject();
+        tokenJson.put("app_id", appId);
+        tokenJson.put("app_secret", appSercret);
+        try {
+            String returnSt =  doPostByHttpClient(url,tokenJson.toJSONString(),false,null, 3);
+            JSONObject returnJson = JSONObject.parseObject(returnSt);
+            if("0".equals(returnJson.getString("code"))){//成功
+                returnstr = returnJson.getString("tenant_access_token");
+            }
+
+        } catch (ConnectException e) {
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return returnstr;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserId(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id";
+        try {
+            logger.info("获取用户json: " + m.toString());
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            logger.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    /**
+     * 根据手机号(暂定)获取userId/openId
+     */
+    public static String getUserIdTrue(String cell,String token) {
+        String user_id = "";
+        JSONObject m = new JSONObject();
+        JSONArray mobiles = new JSONArray();
+        mobiles.add(cell);
+        m.put("mobiles", mobiles);
+        String userIdUrl="https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id?user_id_type=user_id";
+        try {
+            logger.info("获取用户json: " + m.toString());
+
+            String response = doPostByHttpClient(userIdUrl, m.toJSONString(),true,token, 3);
+            JSONObject userRes = JSONObject.parseObject(response);
+            user_id = userRes.getJSONObject("data").getJSONArray("user_list").getJSONObject(0).getString("user_id");
+            logger.info("获取用户结束json: " + userRes.toString());
+        } catch (ConnectException e) {
+            // TODO Auto-generated catch block
+            logger.info(e.getMessage());
+            e.printStackTrace();
+        }
+        return user_id;
+    }
+
+    public static String doPostByHttpClient(String url, String data, boolean isBearer,String token, int times) throws ConnectException {
+        System.out.println("url =" + url);
+        System.out.println("data =" + data);
+        try {
+            // System.setProperty("javax.net.debug", "ssl");
+            DefaultHttpClient httpClient = null;
+            if (url.toLowerCase().startsWith("https://")) {
+                System.out.println("use ssl");
+                httpClient = new SSLClient();
+            } else {
+                System.out.println("use nonssl");
+                httpClient = new DefaultHttpClient();
+            }
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+            if(isBearer) {
+                httpPost.addHeader("Authorization", "Bearer" + " " + token);
+            }
+
+            StringEntity se = new StringEntity(data, "UTF-8");
+            se.setContentType("text/json");
+            se.setContentEncoding(new BasicHeader("Content-Type", "application/json; charset=UTF-8"));
+            httpPost.setEntity(se);
+            HttpResponse response = httpClient.execute(httpPost);
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                throw new ConnectException("连接服务器发生错误!");
+            }
+            String body = EntityUtils.toString(response.getEntity());
+            System.out.println(body);
+            return body;
+        } catch (Exception e) {
+            String errMsg = e.getMessage() == null ? "未知异常" : e.getMessage();
+            logger.error("请求url " + url + " 超时", errMsg);
+            // 执行重试
+            if (times > 0){
+                times--;
+                if (times > 3){
+                    times = 3;
+                }
+                return doPostByHttpClient(url, data, isBearer, token, times);
+            } else {
+                throw new ConnectException("请求飞书接口失败:" + errMsg);
+            }
+        } finally {
+            System.clearProperty("javax.net.debug");
+        }
+    }
+    /**
+     * 保存飞书消息日志
+     *
+     * @param userid  用户id
+     * @param title   标题
+     * @param content 内容
+     * @param m       发送消息体
+     * @return uuid
+     */
+    private String saveMsgLog(String userid, String title, String content, JSONObject m, String logtype, String instanceid) {
+        try {
+            logger.info("记录新增飞书推送数据日志");
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject("nckd_log_feishumsg");
+
+            dynamicObject.set("billno", uuid);
+            dynamicObject.set("nckd_logtype", logtype);
+            dynamicObject.set("nckd_instanceid", instanceid);
+            dynamicObject.set("nckd_sendstatus", "fail");
+            dynamicObject.set("nckd_to_userid", userid);
+            dynamicObject.set("nckd_msg_title", title);
+
+            String msgContent = m.toString();
+            if (msgContent.length() < 200){
+                dynamicObject.set("nckd_msg_content_l", msgContent);
+            }else {
+                dynamicObject.set("nckd_msg_content_l", msgContent.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_l_tag", msgContent);
+            if (content.length() < 200){
+                dynamicObject.set("nckd_msg_content", content);
+            }else {
+                dynamicObject.set("nckd_msg_content", content.substring(0,200) + "...");
+            }
+            dynamicObject.set("nckd_msg_content_tag", content);
+            dynamicObject.set("nckd_send_time", new Date());
+
+
+            SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+            logger.info("记录新增飞书推送数据日志成功:{}", uuid);
+            return uuid;
+        } catch (Exception e) {
+            logger.info("记录新增飞书推送数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 更新消息响应到对应日志记录
+     * @param uuid uuid
+     * @param response 消息响应
+     */
+    private void updateMsgLog(String uuid, String response, String sendStatus) {
+        try {
+            logger.info("记录更新飞书推送结果数据日志");
+            QFilter qFilterCas = new QFilter("billno", QCP.equals, uuid);
+            DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load("nckd_log_feishumsg", "id", new QFilter[]{qFilterCas});
+            if (dynamicObjects != null && dynamicObjects.length > 0) {
+                String id = dynamicObjects[0].getPkValue().toString();
+                DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle(id, "nckd_log_feishumsg");
+                if (response.length() < 200){
+                    dynamicObject.set("nckd_send_result_l", response);
+                }else {
+                    dynamicObject.set("nckd_send_result_l", response.substring(0,200) + "...");
+                }
+                dynamicObject.set("nckd_send_result_l_tag", response);
+                dynamicObject.set("nckd_sendstatus", sendStatus);
+
+                SaveServiceHelper.update(dynamicObject);
+                logger.info("记录更新飞书推送结果数据日志成功");
+            }
+        } catch (Exception e) {
+            logger.info("记录更新飞书推送结果数据日志异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}

+ 106 - 0
main/java/kd/cosmic/jkjt/tmc/am/task/DepositAgreeForStatusTask.java

@@ -0,0 +1,106 @@
+package kd.cosmic.jkjt.tmc.am.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * @author turbo rao
+ *
+ *  协定存款的协议状态自动关闭功能,需要开发调度任务,具体为到期自动延期=否,且协议结束日期<当前日期,执行协议状态=关闭
+ *
+ *  银行账户开通协定存款自动更新功能,需要做动态字段实时显示 (nckd_agreement)
+ *  数据来源:协定存款单据
+ *  条件:协议状态=正常 , 银行账号=协定账号,银行账户.开通协定存款=是
+ *  未找到记录则 银行账户.开通协定存款=否
+ */
+public class DepositAgreeForStatusTask extends AbstractTask {
+
+
+    private String ENTITY_NAME = "am_accountbank";
+    private String DepositEntityName = "cim_agreement_deposit";
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+
+        //QFilter filterAgreement1 = new QFilter("enddate", QCP.less_than, DateUtil.getCurrentDate());
+        QFilter filterAgreement2 = new QFilter("status", QCP.equals, "A");
+        //QFilter[] filtersAgreeement = new QFilter[]{filterAgreement1.and(filterAgreement2)};
+        QFilter[] filtersAgreeement = new QFilter[]{filterAgreement2};
+        DynamicObject[] agreementBillEntities = BusinessDataServiceHelper.load(DepositEntityName, "id,billno", filtersAgreeement);
+        for (DynamicObject dataEntity : agreementBillEntities) {
+            DynamicObject bankBillEntity = BusinessDataServiceHelper.loadSingle(dataEntity.getPkValue(), DepositEntityName);
+            Date enddate  = bankBillEntity.getDate("enddate");
+            ///到期自动延期
+            boolean autoredeposit = bankBillEntity.getBoolean("autoredeposit");
+            Object bankBillPK = bankBillEntity.getDynamicObject("bankacct").getPkValue();
+            if(enddate!=null && compareByDay(enddate, getCurrentDate())<0 && !autoredeposit) {
+                /**
+                 * 正常 A   关闭 B
+                 */
+                bankBillEntity.set("status", "B");
+
+                /**
+                 * 关闭账户管理单据的  开通协定存款
+                 */
+                updateBankBillIsAgreement(bankBillPK,false);
+            }else{
+                updateBankBillIsAgreement(bankBillPK,true);
+            }
+
+            SaveServiceHelper.update(new DynamicObject[]{bankBillEntity});
+        }
+    }
+
+
+    public void updateBankBillIsAgreement(Object bankBillPK,boolean isAgreement){
+
+        DynamicObject bankBillEntity = BusinessDataServiceHelper.loadSingle(bankBillPK, ENTITY_NAME);
+
+        if(bankBillEntity.getBoolean("nckd_agreement")==isAgreement) {
+            return;
+        }
+
+        bankBillEntity.set("nckd_agreement",isAgreement);
+
+        SaveServiceHelper.update(new DynamicObject[]{bankBillEntity});
+
+    }
+
+    public int compareByDay(Date oneDate, Date otherDate) {
+        if (oneDate != null && otherDate != null) {
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(oneDate);
+            int year = calendar.get(1);
+            int dayOfYear = calendar.get(6);
+            calendar.clear();
+            calendar.setTime(otherDate);
+            int otherYear = calendar.get(1);
+            int otherDayOfYear = calendar.get(6);
+            if (year < otherYear) {
+                return -1;
+            } else if (year > otherYear) {
+                return 1;
+            } else if (dayOfYear < otherDayOfYear) {
+                return -1;
+            } else {
+                return dayOfYear > otherDayOfYear ? 1 : 0;
+            }
+        } else {
+            throw new IllegalArgumentException("The date must not be null");
+        }
+    }
+
+    public Date getCurrentDate() {
+        return new Date();
+    }
+}

+ 100 - 0
main/java/kd/cosmic/jkjt/tmc/am/task/SynchAccount2CBSTask.java

@@ -0,0 +1,100 @@
+package kd.cosmic.jkjt.tmc.am.task;
+
+import kd.bos.algo.DataSet;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * description:自动将未同步的银行账户同步至CBS7系统
+ * @author wanghaiwu_kd
+ * @date 2024/10/15
+ */
+public class SynchAccount2CBSTask extends AbstractTask {
+    private static final Log logger = LogFactory.getLog(AccountOpenCBSWarningTask.class);
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        logger.info("SynchAccount2CBSTask:自动同步银行账户至CBS start");
+
+        QFilter qFilter = new QFilter("status", QCP.equals, "C");
+        qFilter.and(new QFilter("enable", QCP.equals, true));
+        //账户状态 = 正常
+        qFilter.and(new QFilter("acctstatus", QCP.equals, "normal"));
+        //已同步至CBS = 否
+        qFilter.and(new QFilter("nckd_ispushcbs", QCP.equals, false));
+        //金融机构类别 = 银行
+        qFilter.and(new QFilter("finorgtype", QCP.equals, "0"));
+//        //测试时加的条件
+//        qFilter.and(new QFilter("number", QCP.equals, "51050189523600001471")
+//                .or(new QFilter("number", QCP.equals, "8111001011700853999")));
+
+        logger.info("SynchAccount2CBSTask:查询条件:" + qFilter.toArray());
+
+        DataSet accountDS = QueryServiceHelper.queryDataSet("SynchAccount2CBSTask.algoKey", "am_accountbank", "id", qFilter.toArray(), (String)null);
+        List<Long> idList = new ArrayList<>();
+        accountDS.copy().iterator().forEachRemaining((v) -> {
+            idList.add(v.getLong("id"));
+        });
+
+        if(idList.size() == 0){
+            logger.info("SynchAccount2CBSTask:未查询到需要同步的银行账户");
+            return;
+        }
+
+        logger.info("SynchAccount2CBSTask:查询到" + idList.size() + "条需要同步的银行账户");
+
+        DynamicObject[] accounts = BusinessDataServiceHelper.load(idList.toArray(), EntityMetadataCache.getDataEntityType("am_accountbank"));
+        List<DynamicObject> listObj = new ArrayList<>();
+
+        for(DynamicObject account : accounts){
+            listObj.add(account);
+
+            if(listObj.size() == 500){
+                OperationResult resultSave = OperationServiceHelper.executeOperate("save", "am_accountbank", listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+                StringBuilder err = new StringBuilder();
+                if (resultSave.getSuccessPkIds().size() <= 0) {
+                    for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                        String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                        err.append("/").append(message);
+                    }
+                    if(err.length() > 0) {
+                        logger.info("SynchAccount2CBSTask:同步异常:" + err.toString());
+                    }
+                }
+
+                listObj.clear();
+            }
+        }
+
+        if(listObj.size() > 0) {
+            OperationResult resultSave = OperationServiceHelper.executeOperate("save", "am_accountbank", listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                    err.append("/").append(message);
+                }
+                if(err.length() > 0) {
+                    logger.info("SynchAccount2CBSTask:同步异常:" + err.toString());
+                }
+            }
+        }
+
+        logger.info("SynchAccount2CBSTask:自动同步银行账户至CBS end");
+    }
+}

+ 385 - 0
main/java/kd/cosmic/jkjt/tmc/api/SynBentransByBalance2Plugin.java

@@ -0,0 +1,385 @@
+package kd.cosmic.jkjt.tmc.api;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/05/08
+ * @description API插件,未开通银企直连的账户,根据CBS系统的账户余额,与星瀚中的余额比较,如果有差额,则自动生成交易明细。
+ */
+@ApiController(
+        desc = "未开通银企直连的账户根据CBS账户余额生产交易明细",
+        value = "jkjt_cbsapi"
+)
+public class SynBentransByBalance2Plugin implements Serializable {
+    private static final long serialVersionUID = -5560815141256121757L;
+    private static Log logger = LogFactory.getLog(SynBentransByBalance2Plugin.class);
+    //出纳初始化
+    private static final String KEY_ENTITY_CASHINIT = "cas_cashmgtinit";
+    //银行账户
+    private static final String KEY_ENTITY_ACCOUNT = "am_accountbank";
+    //离线明细引入实体编码
+    private static final String KEY_ENTITY_BETRANSDETAIL = "bei_betransdetail_imp";
+
+    @ApiPostMapping("SynBetransByBalance")
+    public CustomApiResult<JSONObject> doCustomerService(){
+        //先更新cbs开通状态
+        List<Long> orgIds = new ArrayList<>();
+        CBSToolUtil.synAccountOpenStatus(orgIds);
+
+        String selectProperties = "bankaccountnumber, openorg, currency, bank, company";
+        QFilter qFilter = new QFilter("nckd_issetcbs", QCP.equals, false);
+        qFilter.and(new QFilter("acctstatus", QCP.equals, "normal"));
+//        qFilter.and(new QFilter("bankaccountnumber", QCP.equals, "791909297510506"));
+
+        DynamicObject[] acctList = BusinessDataServiceHelper.load(KEY_ENTITY_ACCOUNT, selectProperties, new QFilter[]{qFilter});
+
+        List<DynamicObject> listObj = new ArrayList<>();
+        int count = 0;
+
+        for(DynamicObject acct : acctList){
+            JSONObject lastBal = queryLastBal(acct);
+//            if("500".equals(lastBal.getString("code"))){
+//                continue;
+//            }
+
+            String accountNumber = acct.getString("bankaccountnumber");
+            DynamicObject openOrg = acct.getDynamicObject("openorg");
+            if(openOrg == null){
+                continue;
+            }
+            Long orgId = openOrg.getLong("id");
+            String cbsUrl = ParamsUtil.getCBSURLByMaster(orgId);
+
+            if(StringUtils.isEmpty(cbsUrl)){
+                logger.info("同步交易明细失败:组织【" + openOrg.getString("name") + "】(" + orgId.toString() + ")未配置相应的CBS地址");
+                continue;
+            }
+
+            //账户实时余额查询ERCURBAL报文内容
+            String body = getDetailBodyString(accountNumber);
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            //保存CBS调用日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            logger.info("调用CBS接口ERCURBAL开始时间:" + sTime);
+
+            String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            logger.info("调用CBS接口ERCURBAL结束时间:" + eTime);
+
+            //更新CBS调用日志
+            ParamsUtil.updateCBSLogData(uuid, sr);
+
+            String cbssr = CBSToolUtil.parseXMLcbs(sr);
+            DynamicObject result = getBalanceDetailInfo(cbssr, acct, lastBal.getBigDecimal("bal"));
+
+            if(result != null){
+                listObj.add(result);
+            }
+
+            count++;
+            //批量保存
+            if(listObj.size() > 0 && count == 1000) {
+                count = 0;
+                OperationResult resultSave = OperationServiceHelper.executeOperate("save", KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+                StringBuilder err = new StringBuilder();
+                if (resultSave.getSuccessPkIds().size() <= 0) {
+                    for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                        String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                        err.append("/").append(message);
+                    }
+                }
+
+                listObj.clear();
+            }
+        }
+
+        //批量保存
+        if(listObj.size() > 0) {
+            OperationResult resultSave = OperationServiceHelper.executeOperate("save", KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+            StringBuilder err = new StringBuilder();
+            if (resultSave.getSuccessPkIds().size() <= 0) {
+                for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                    String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                    err.append("/").append(message);
+                }
+            }
+        }
+
+        //自动生成余额
+//        try {
+//            ParamsUtil.createBalance();
+//        } catch (ParseException e) {
+//            throw new RuntimeException(e);
+//        }
+
+        return returnResult("200", "同步成功!");
+    }
+
+    /**
+     * 查询账号在星瀚的当前余额
+     * @param acct
+     * @return
+     */
+    private JSONObject queryLastBal(DynamicObject acct){
+        String code = "200";
+        BigDecimal bal = BigDecimal.ZERO;
+
+        //查询是否存在交易明细
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
+        //交易时间
+        Date time = new Date();
+        //交易日期
+        Date date = null;
+        try {
+            date = sdf1.parse(sdf1.format(time));
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+
+        QFilter qFilter = new QFilter("accountbank.id", QCP.equals, acct.getLong("id"));
+        qFilter.and(new QFilter("bizdate", QCP.less_than, date));
+
+        String selectProperties = "transbalance";
+        String orderBy = "biztime desc";
+        DynamicObject[] detailList = BusinessDataServiceHelper.load(KEY_ENTITY_BETRANSDETAIL, selectProperties, qFilter.toArray(), orderBy);
+
+        //如果没有交易明细,则查找是否有做出纳初始化初始余额
+        if(detailList.length == 0){
+            qFilter = new QFilter("entrybank.bank_accountbank", QCP.equals, acct.getLong("id"));
+
+            selectProperties = "entrybank.bank_journalbalance";
+            orderBy = "startperiod.number desc";
+            DynamicObjectCollection objCols = QueryServiceHelper.query(KEY_ENTITY_CASHINIT, selectProperties, qFilter.toArray(), orderBy);
+            if(objCols.size() > 0){
+                code = "200";
+                bal = objCols.get(0).getBigDecimal("entrybank.bank_journalbalance");
+            }
+        } else {
+            code = "200";
+            bal = detailList[0].getBigDecimal("transbalance");
+        }
+
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("bal", bal);
+
+        return reslutData;
+    }
+
+    /**
+     * 交易明细参数body
+     * @return
+     */
+    private String getDetailBodyString(String accountNumber){
+        StringBuffer body = new StringBuffer();
+
+        body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ERCURBAL</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<ERCURBALX>\r\n");
+        body.append("<ACTNBR>").append(accountNumber).append("</ACTNBR>\r\n");
+        body.append("</ERCURBALX>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 处理cbs返回结果
+     * @param cbssr
+     * @return
+     */
+    private DynamicObject getBalanceDetailInfo(String cbssr, DynamicObject acct, BigDecimal lastBal){
+        JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(cbsObj != null && cbsObj.get("ACACTINFY") != null){
+            JSONArray array = (JSONArray)cbsObj.get("ACACTINFY");
+            JSONObject account = array.getJSONObject(0);
+            List<DynamicObject> listObj = new ArrayList<>();
+            DynamicObject detailInfo = null;
+            try {
+                detailInfo = parseBetransDetail(account, acct, lastBal);
+            } catch (ParseException e) {
+                throw new RuntimeException(e);
+            }
+
+            return detailInfo;
+        }
+        return null;
+    }
+
+
+
+    /**
+     * 创建离线明细引入对象
+     * @param cbsObj
+     * @return
+     */
+    private DynamicObject parseBetransDetail(JSONObject cbsObj, DynamicObject acct, BigDecimal lastBal) throws ParseException {
+        //账户余额
+        BigDecimal actBal = cbsObj.getBigDecimal("ACTBAL");
+        if(actBal == null){
+            actBal = BigDecimal.ZERO;
+        }
+
+        //处理日期类字段
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
+        //交易时间
+        Date time = new Date();
+        //交易日期
+        Date date = sdf1.parse(sdf1.format(time));
+
+        //如果当天存在cbs交易明细,则不增加交易明细
+        Boolean isExistDetail = ParamsUtil.isExistsDetail(acct, date, new String[]{"curday", "history"});
+        if(isExistDetail){
+            return null;
+        }
+
+        DynamicObject objectInfo = ParamsUtil.getDetailInfo(acct, date, "balancecompare");
+        if(objectInfo == null){
+            objectInfo = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_BETRANSDETAIL);
+        }
+        objectInfo.set("nckd_generatemethod", "balancecompare");
+
+        //借贷方向:1:借方,2:贷方
+        String direct = "";
+        BigDecimal dayActAmt = actBal.subtract(lastBal).abs();
+        if(lastBal.compareTo(actBal) > 0){
+            direct = "1";
+        } else if(lastBal.compareTo(actBal) < 0){
+            direct = "2";
+        } else {
+            return null;
+        }
+
+        String detailid = UUID.randomUUID().toString().replace("-", "");
+        detailid = (detailid == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : detailid;
+
+        objectInfo.set("oppunit", null);//对方用户名
+        objectInfo.set("oppbanknumber", null);//对方账号
+        objectInfo.set("oppbank", null);//对方开户行
+        objectInfo.set("detailid", detailid);//明细流水号
+        objectInfo.set("bizrefno", detailid);//业务参考号
+        objectInfo.set("uniqueseq", detailid + "01");//银行主键
+        objectInfo.set("description", null);//摘要
+
+        //判断此明细流水号是否已存在,已存在的记录不接收
+        QFilter filterBizRefNo = new QFilter("detailid", QCP.equals, objectInfo.getString("detailid"));
+        QFilter filterDataSource = new QFilter("datasource", QCP.equals, "import");
+        QFilter[] filters = new QFilter[]{filterBizRefNo, filterDataSource};
+        DynamicObject betransDetailInfo = BusinessDataServiceHelper.loadSingle(KEY_ENTITY_BETRANSDETAIL, "id, billno", filters);
+        if(betransDetailInfo != null){
+            return null;
+        }
+
+        //交易时间
+        objectInfo.set("bizTime", new Timestamp(time.getTime()));
+        //交易日期
+        objectInfo.set("bizdate", new Timestamp(date.getTime()));
+
+        //入账状态
+        if("2".equals(direct)){
+            objectInfo.set("receredtype", "0");
+        }
+
+        //发生额
+        objectInfo.set("debitamount", BigDecimal.ZERO);
+        objectInfo.set("creditamount", BigDecimal.ZERO);
+
+        String amountField = ParamsUtil.getAmountFieldName(direct);
+        objectInfo.set(amountField, dayActAmt);
+        //手续费
+//        objectInfo.set("transfercharge", BigDecimal.ZERO);
+        //余额
+        objectInfo.set("transbalance", actBal);
+
+        //单据状态
+        objectInfo.set("billstatus", "A");
+        //数据来源
+        objectInfo.set("datasource", "import");
+        //业务类型
+        objectInfo.set("biztype", "1");
+
+        //币别
+        DynamicObjectCollection collection = acct.getDynamicObjectCollection("currency");
+        Long currencyId = 0L;
+        for (DynamicObject item : collection) {
+            //引用该基础资料的单据id
+            long pkid = item.getLong("pkid");
+
+            // 获取该基础资料的动态对象
+            DynamicObject fbasedataid = item.getDynamicObject("fbasedataid");
+
+            //获取该基础资料的id
+            currencyId = item.getLong("fbasedataid_id");
+        }
+
+        DynamicObject currency = BusinessDataServiceHelper.loadSingleFromCache(currencyId, "bd_currency");
+        objectInfo.set("currency", currency);
+
+        //银行账号
+        objectInfo.set("accountbank", acct);
+        objectInfo.set("bank", acct.get("bank"));
+        objectInfo.set("company", acct.get("company"));
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(objectInfo.getDataEntityType().getName(), objectInfo, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, objectInfo);
+        objectInfo.set("billno", billno);
+
+        return objectInfo;
+    }
+
+    /**
+     * 自定义返回data对象
+     * @param code
+     * @param message
+     * @return
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+        CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
+
+        return result;
+    }
+}

+ 361 - 0
main/java/kd/cosmic/jkjt/tmc/api/SynBetransDetail2Plugin.java

@@ -0,0 +1,361 @@
+package kd.cosmic.jkjt.tmc.api;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static kd.cosmic.jkjt.tmc.util.CBSToolUtil.getBigDecimal;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023-03-31
+ * description 获取CSB交易明细,生成星瀚的交易明细API插件
+ */
+@ApiController(
+        desc = "同步CBS交易明细",
+        value = "jkjt_cbsapi"
+)
+public class SynBetransDetail2Plugin implements Serializable {
+    private static final long serialVersionUID = 5293479394680694712L;
+    private static final String KEY_OP_SAVE = "save";
+    //离线明细引入实体编码
+    private static final String KEY_ENTITY_BETRANSDETAIL = "bei_betransdetail_imp";
+
+    @ApiPostMapping("SynBetransDetail")
+    public CustomApiResult<JSONObject> doCustomerService(){
+        List<String> cbsUrlList = ParamsUtil.getCBSList();
+
+        if(cbsUrlList == null || cbsUrlList.size() == 0){
+            return returnResult("500", "请正确配置CBS相关参数!");
+        }
+
+        for (String cbsUrl : cbsUrlList) {
+//            cbsUrl = "http://192.168.250.213:3030";
+
+            //交易明细ERQRYTRS报文内容
+            String body = getDetailBodyString("0");
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            //保存CBS调用日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN开始时间:" + sTime);
+
+            String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN结束时间:" + eTime);
+
+            //更新CBS调用日志
+            ParamsUtil.updateCBSLogData(uuid, sr);
+
+            String cbssr = CBSToolUtil.parseXMLcbs(sr);
+            CustomApiResult<JSONObject> result = saveDetailData(cbssr, cbsUrl);
+        }
+
+        return returnResult("200", "同步成功!");
+    }
+
+    /**
+     * 交易明细参数body
+     * @return
+     */
+    private String getDetailBodyString(String dtlSeq){
+        StringBuffer body = new StringBuffer();
+
+        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
+
+        Calendar calStart = Calendar.getInstance();
+        int days = -1 * (ParamsUtil.getQueryDays() + 1);
+        calStart.setTime(new Date());
+        calStart.add(Calendar.DATE, days);
+        String startDate = sf.format(calStart.getTime());
+
+        Calendar calEnd = Calendar.getInstance();
+        calEnd.setTime(new Date());
+        calEnd.add(Calendar.DATE, -1);
+        String endDate = sf.format(calEnd.getTime());
+
+        body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ERQRYTRS</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<ERQRYTRSA>\r\n");
+        body.append("<BGNDAT>").append(startDate).append("</BGNDAT>\r\n");
+        body.append("<ENDDAT>").append(endDate).append("</ENDDAT>\r\n");
+
+//        body.append("<BGNDAT>2023-06-28</BGNDAT>\r\n");
+//        body.append("<ENDDAT>2023-06-28</ENDDAT>\r\n");
+//        body.append("<ACTNBR>1001252409200285813</ACTNBR>\r\n");
+
+        body.append("</ERQRYTRSA>\r\n");
+        body.append("<ERDTLSEQZ>\r\n");
+        body.append("<DTLSEQ>").append(dtlSeq).append("</DTLSEQ>\r\n");
+        body.append("</ERDTLSEQZ>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 处理cbs返回结果
+     * @param cbssr
+     * @return
+     */
+    private CustomApiResult<JSONObject> saveDetailData(String cbssr, String cbsUrl){
+        JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(cbsObj != null && cbsObj.get("EREXPTRSZ") != null){
+            JSONArray array = (JSONArray)cbsObj.get("EREXPTRSZ");
+            List<DynamicObject> listObj = new ArrayList<>();
+            int count = 0;
+            for(int i = 0; i < array.size(); i++){
+                DynamicObject detailInfo = null;
+                try {
+                    detailInfo = parseBetransDetail(array.getJSONObject(i));
+                } catch (ParseException e) {
+                    throw new RuntimeException(e);
+                }
+
+                if(detailInfo != null) {
+                    listObj.add(detailInfo);
+                }
+
+                count++;
+                //批量保存
+                if(listObj.size() > 0 && count == 500) {
+                    count = 0;
+                    OperationResult resultSave = OperationServiceHelper.executeOperate(KEY_OP_SAVE, KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+                    StringBuilder err = new StringBuilder();
+                    if (resultSave.getSuccessPkIds().size() <= 0) {
+                        for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                            String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                            err.append("/").append(message);
+                        }
+
+                        return returnResult("500", err.toString());
+                    }
+
+                    listObj.clear();
+                }
+            }
+
+            //批量保存
+            if(listObj.size() > 0) {
+                OperationResult resultSave = OperationServiceHelper.executeOperate(KEY_OP_SAVE, KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+                StringBuilder err = new StringBuilder();
+                if (resultSave.getSuccessPkIds().size() <= 0) {
+                    for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                        String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                        err.append("/").append(message);
+                    }
+
+                    return returnResult("500", err.toString());
+                }
+            }
+        }
+
+
+        if(cbsObj != null && cbsObj.get("ERDTLSEQZ") != null){
+            JSONArray array = (JSONArray)cbsObj.get("ERDTLSEQZ");
+            JSONObject object = array.getJSONObject(0);
+
+            String dtlSeq = object.getString("DTLSEQ");
+
+            //交易明细ERQRYTRS报文内容
+            String body = getDetailBodyString(dtlSeq);
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            //保存CBS调用日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN开始时间:"+ sTime);
+
+            String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN结束时间:"+ eTime);
+
+            //更新CBS调用日志
+            ParamsUtil.updateCBSLogData(uuid, sr);
+
+            String cbsstring = CBSToolUtil.parseXMLcbs(sr);
+            CustomApiResult<JSONObject> result = saveDetailData(cbsstring, cbsUrl);
+        }
+
+        String code = "100";
+        String message = "同步成功";
+        return ParamsUtil.returnResult(code, message);
+    }
+
+    /**
+     * 创建离线明细引入对象
+     * @param cbsObj
+     * @return
+     */
+    private DynamicObject parseBetransDetail(JSONObject cbsObj) throws ParseException {
+        //不接收情况:交易额为空、余额为空、当日明细流水号为空
+        if (StringUtils.isEmpty(cbsObj.getString("TRSBAL"))
+                || StringUtils.isEmpty(cbsObj.getString("ACTBAL"))
+                || StringUtils.isEmpty(cbsObj.getString("CURSEQ"))) {
+            return null;
+        }
+
+        String accountNumber = cbsObj.getString("ACTNBR");
+        QFilter filterNumber = new QFilter("bankaccountnumber", QCP.equals, accountNumber);
+        QFilter[] filters = new QFilter[]{filterNumber};
+        DynamicObject account = BusinessDataServiceHelper.loadSingleFromCache("bd_accountbanks", filters);
+        if(account == null){
+            return null;
+        }
+
+        //处理日期类字段
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
+        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss");
+
+        //交易时间
+        Date time = sdf.parse(cbsObj.getString("BNKTIM"));
+        //交易日期
+        Date date = sdf1.parse(cbsObj.getString("BNKTIM"));
+        //更新时间
+        Date updateTime = sdf2.parse(cbsObj.getString("UPDTIM"));
+
+        //如果当天存在根据余额生成的交易明细,则不增加交易明细
+        Boolean isExistDetail = ParamsUtil.isExistsDetail(account, date, new String[]{"balancecompare"});
+        if(isExistDetail){
+            return null;
+        }
+
+        DynamicObject objectInfo = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_BETRANSDETAIL);
+
+        objectInfo.set("accountbank", account);//银行账户
+        objectInfo.set("bank", account.get("bank"));//开户行
+        objectInfo.set("company", account.get("company"));//资金组织
+        objectInfo.set("nckd_generatemethod", "history");
+
+        //处理文本类字段
+        ParamsUtil.BetransDetailFieldStringMap.forEach((key, value) -> {
+            objectInfo.set(value, cbsObj.getString(key));
+        });
+
+        //历史交易明细使用CURSEQ做为唯一值,对应当日交易明细的DTLSEQ
+        String detailId = cbsObj.getString("CURSEQ");
+        String uniqueseq = detailId + "-01";
+
+        //判断此银行主键是否已存在,已存在的记录不接收
+        QFilter filter = new QFilter("uniqueseq", QCP.equals, uniqueseq);
+        filter.or(new QFilter("detailid", QCP.equals, detailId));
+        DynamicObject betransDetailInfo = BusinessDataServiceHelper.loadSingle(KEY_ENTITY_BETRANSDETAIL, "id, billno", filter.toArray());
+        if(betransDetailInfo != null){
+            return null;
+        }
+        //明细流水号
+        objectInfo.set("detailid", detailId);
+        //银行主键
+        objectInfo.set("uniqueseq", uniqueseq);
+
+        Long dtlseq = Long.valueOf(cbsObj.getString("DTLSEQ"));
+        objectInfo.set("sortno", dtlseq);
+
+        String usage = cbsObj.getString("NUSAGE") == null ? "" : cbsObj.getString("NUSAGE");
+        String description = objectInfo.getString("description") == null ? "" : objectInfo.getString("description");
+
+        description = description.concat(usage);
+        objectInfo.set("description", description);
+
+        //cbs更新时间
+        objectInfo.set("nckd_cbsbiztime", new Timestamp(updateTime.getTime()));
+        //交易时间
+        objectInfo.set("bizTime", new Timestamp(time.getTime()));
+        //交易日期
+        objectInfo.set("bizdate", new Timestamp(date.getTime()));
+
+        //处理数字类字段
+        //借贷方向:1:借方,2:贷方
+        String direct = cbsObj.getString("ITMDIR");
+        //入账状态
+        if("2".equals(direct)){
+            objectInfo.set("receredtype", "0");
+        }
+
+        //发生额
+        String amountField = ParamsUtil.getAmountFieldName(direct);
+        objectInfo.set(amountField, getBigDecimal(cbsObj.get("TRSBAL")));
+        //手续费
+        objectInfo.set("transfercharge", getBigDecimal(cbsObj.get("SERFEE")));
+        //余额
+        objectInfo.set("transbalance", getBigDecimal(cbsObj.get("ACTBAL")));
+
+        //单据状态
+        objectInfo.set("billstatus", "A");
+        //数据来源
+        objectInfo.set("datasource", "import");
+        //业务类型
+        String bizType = ParamsUtil.getBizType(cbsObj.getString("PTCTYP"));
+        objectInfo.set("biztype", bizType);
+        //上划
+        if("2".equals(bizType)){
+            objectInfo.set("istransup", "1");
+        }
+        //下拨
+        if("3".equals(bizType)){
+            objectInfo.set("istransdown", "1");
+        }
+
+        //币别
+        filterNumber = new QFilter("number", QCP.equals, ParamsUtil.CurrencyMap.get(cbsObj.getString("CCYNBR")));
+        filters = new QFilter[]{filterNumber};
+        DynamicObject currency = BusinessDataServiceHelper.loadSingleFromCache("bd_currency", filters);
+        objectInfo.set("currency", currency);
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(objectInfo.getDataEntityType().getName(), objectInfo, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, objectInfo);
+        objectInfo.set("billno", billno);
+
+        return objectInfo;
+    }
+
+    /**
+     * 自定义返回data对象
+     * @param code
+     * @param message
+     * @return
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+        CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
+
+        return result;
+    }
+}

+ 355 - 0
main/java/kd/cosmic/jkjt/tmc/api/SynBetransDetailCur2Plugin.java

@@ -0,0 +1,355 @@
+package kd.cosmic.jkjt.tmc.api;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static kd.cosmic.jkjt.tmc.util.CBSToolUtil.getBigDecimal;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023/05/10
+ * @description 同步CBS当日交易明细API插件
+ */
+@ApiController(
+        desc = "同步CBS当日交易明细",
+        value = "jkjt_cbsapi"
+)
+public class SynBetransDetailCur2Plugin  implements Serializable {
+    private static final long serialVersionUID = -6847342046863288630L;
+    private static final String KEY_OP_SAVE = "save";
+    //离线明细引入实体编码
+    private static final String KEY_ENTITY_BETRANSDETAIL = "bei_betransdetail_imp";
+
+    @ApiPostMapping("SynBetransDetailCur")
+    public CustomApiResult<JSONObject> doCustomerService(){
+        List<String> cbsUrlList = ParamsUtil.getCBSList();
+
+        if(cbsUrlList == null || cbsUrlList.size() == 0){
+            return returnResult("500", "请正确配置CBS相关参数!");
+        }
+
+        for (String cbsUrl : cbsUrlList) {
+//            cbsUrl = "http://192.168.250.213:3030";
+
+            //交易明细ERQRYTRS报文内容
+            String body = getDetailBodyString("0");
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            //保存CBS调用日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERCURDTL开始时间:" + sTime);
+
+            String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERCURDTL结束时间:" + eTime);
+
+            //更新CBS调用日志
+            ParamsUtil.updateCBSLogData(uuid, sr);
+
+            String cbssr = CBSToolUtil.parseXMLcbs(sr);
+            CustomApiResult<JSONObject> result = saveDetailData(cbssr, cbsUrl);
+        }
+
+        return returnResult("200", "同步成功!");
+    }
+
+    /**
+     * 交易明细参数body
+     * @return
+     */
+    private String getDetailBodyString(String dtlSeq){
+        StringBuffer body = new StringBuffer();
+
+        body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ERCURDTL</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<ERCURDTLA>\r\n");
+        body.append("<ITMDIR>A</ITMDIR>\r\n");
+        body.append("</ERCURDTLA>\r\n");
+
+//        body.append("<ERCURDTLB>\r\n");
+//        body.append("<ACTNBR>1502001119000042047</ACTNBR>\r\n");
+//        body.append("</ERCURDTLB>\r\n");
+
+        body.append("<ERDTLSEQZ>\r\n");
+        body.append("<DTLSEQ>").append(dtlSeq).append("</DTLSEQ>\r\n");
+        body.append("</ERDTLSEQZ>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 处理cbs返回结果
+     * @param cbssr
+     * @return
+     */
+    private CustomApiResult<JSONObject> saveDetailData(String cbssr, String cbsUrl){
+        JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(cbsObj != null && cbsObj.get("ERCURDTLZ") != null){
+            JSONArray array = (JSONArray)cbsObj.get("ERCURDTLZ");
+            List<DynamicObject> listObj = new ArrayList<>();
+            int count = 0;
+            for(int i = 0; i < array.size(); i++){
+                DynamicObject detailInfo = null;
+                try {
+                    detailInfo = parseBetransDetail(array.getJSONObject(i));
+                } catch (ParseException e) {
+                    throw new RuntimeException(e);
+                }
+
+                if(detailInfo != null) {
+                    listObj.add(detailInfo);
+                }
+
+                count++;
+                //批量保存
+                if(listObj.size() > 0 && count == 1000) {
+                    count = 0;
+                    OperationResult resultSave = OperationServiceHelper.executeOperate(KEY_OP_SAVE, KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+                    StringBuilder err = new StringBuilder();
+                    if (resultSave.getSuccessPkIds().size() <= 0) {
+                        for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                            String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                            err.append("/").append(message);
+                        }
+
+                        return returnResult("500", err.toString());
+                    }
+
+                    listObj.clear();
+                }
+            }
+
+            //批量保存
+            if(listObj.size() > 0) {
+                OperationResult resultSave = OperationServiceHelper.executeOperate(KEY_OP_SAVE, KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
+
+                StringBuilder err = new StringBuilder();
+                if (resultSave.getSuccessPkIds().size() <= 0) {
+                    for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
+                        String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
+                        err.append("/").append(message);
+                    }
+
+                    return returnResult("500", err.toString());
+                }
+            }
+        }
+
+
+        if(cbsObj != null && cbsObj.get("ERDTLSEQZ") != null){
+            JSONArray array = (JSONArray)cbsObj.get("ERDTLSEQZ");
+            JSONObject object = array.getJSONObject(0);
+
+            String dtlSeq = object.getString("DTLSEQ");
+
+            //交易明细ERQRYTRS报文内容
+            String body = getDetailBodyString(dtlSeq);
+
+            String paramBody = CBSToolUtil.getBodyString(body);
+
+            //保存CBS调用日志
+            String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
+
+            String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN开始时间:"+ sTime);
+
+            String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
+
+            String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+            System.out.println("调用CBS接口ERQRYTRN结束时间:"+ eTime);
+
+            //更新CBS调用日志
+            ParamsUtil.updateCBSLogData(uuid, sr);
+
+            String cbsstring = CBSToolUtil.parseXMLcbs(sr);
+            CustomApiResult<JSONObject> result = saveDetailData(cbsstring, cbsUrl);
+        }
+
+        String code = "100";
+        String message = "同步成功";
+        return ParamsUtil.returnResult(code, message);
+    }
+
+    /**
+     * 创建离线明细引入对象
+     * @param cbsObj
+     * @return
+     */
+    private DynamicObject parseBetransDetail(JSONObject cbsObj) throws ParseException {
+        //不接收情况:交易额为空、余额为空、当日明细流水号为空
+        if(StringUtils.isEmpty(cbsObj.getString("TRSBAL"))
+                || StringUtils.isEmpty(cbsObj.getString("ACTBAL"))
+                || StringUtils.isEmpty(cbsObj.getString("DTLSEQ"))){
+            return null;
+        }
+
+        String accountNumber = cbsObj.getString("ACTNBR");
+        QFilter filterNumber = new QFilter("bankaccountnumber", QCP.equals, accountNumber);
+        QFilter[] filters = new QFilter[]{filterNumber};
+        DynamicObject account = BusinessDataServiceHelper.loadSingleFromCache("bd_accountbanks", filters);
+
+        if(account == null){
+            return null;
+        }
+
+        //处理日期类字段
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
+        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss");
+
+        //交易时间
+        Date time = sdf.parse(cbsObj.getString("BNKTIM"));
+        //交易日期
+        Date date = sdf1.parse(cbsObj.getString("BNKTIM"));
+        //更新时间
+        Date updateTime = sdf2.parse(cbsObj.getString("UPDTIM"));
+
+        //如果交易日期与当前日期不一致则不接收
+        Date curDate = new Date();
+        if(!sdf1.format(time).equals(sdf1.format(curDate))){
+            return null;
+        }
+
+        //如果当天存在根据余额生成的交易明细,则不增加交易明细
+        Boolean isExistDetail = ParamsUtil.isExistsDetail(account, date, new String[]{"balancecompare"});
+        if(isExistDetail){
+            return null;
+        }
+
+        DynamicObject objectInfo = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_BETRANSDETAIL);
+
+        objectInfo.set("accountbank", account);//银行账户
+        objectInfo.set("bank", account.get("bank"));//开户行
+        objectInfo.set("company", account.get("company"));//资金组织
+        objectInfo.set("nckd_generatemethod", "curday");
+
+        //处理文本类字段
+        ParamsUtil.BetransDetailFieldStringMap.forEach((key, value) -> {
+            objectInfo.set(value, cbsObj.getString(key));
+        });
+
+        //当日交易明细使用DTLSEQ做为唯一值
+        String detailId = cbsObj.getString("DTLSEQ");
+        String uniqueseq = detailId + "-01";
+
+        //判断此银行主键是否已存在,已存在的记录不接收
+        QFilter filter = new QFilter("uniqueseq", QCP.equals, uniqueseq);
+        filter.or(new QFilter("detailid", QCP.equals, detailId));
+        DynamicObject betransDetailInfo = BusinessDataServiceHelper.loadSingle(KEY_ENTITY_BETRANSDETAIL, "id, billno", filter.toArray());
+        if(betransDetailInfo != null){
+            return null;
+        }
+        objectInfo.set("detailid", detailId);
+        objectInfo.set("uniqueseq", uniqueseq);
+
+        Long dtlseq = Long.valueOf(cbsObj.getString("DTLSEQ"));
+        objectInfo.set("sortno", dtlseq);
+
+        String usage = cbsObj.getString("NUSAGE") == null ? "" : cbsObj.getString("NUSAGE");
+        String description = objectInfo.getString("description") == null ? "" : objectInfo.getString("description");
+
+        description = description.concat(usage);
+        objectInfo.set("description", description);
+
+        //cbs更新时间
+        objectInfo.set("nckd_cbsbiztime", new Timestamp(updateTime.getTime()));
+        //交易时间
+        objectInfo.set("bizTime", new Timestamp(time.getTime()));
+        //交易日期
+        objectInfo.set("bizdate", new Timestamp(date.getTime()));
+
+        //处理数字类字段
+        //借贷方向:1:借方,2:贷方
+        String direct = cbsObj.getString("ITMDIR");
+        //入账状态
+        if("2".equals(direct)){
+            objectInfo.set("receredtype", "0");
+        }
+
+        //发生额
+        String amountField = ParamsUtil.getAmountFieldName(direct);
+        objectInfo.set(amountField, getBigDecimal(cbsObj.get("TRSBAL")));
+        //手续费
+        objectInfo.set("transfercharge", getBigDecimal(cbsObj.get("SERFEE")));
+        //余额
+        objectInfo.set("transbalance", getBigDecimal(cbsObj.get("ACTBAL")));
+
+        //单据状态
+        objectInfo.set("billstatus", "A");
+        //数据来源
+        objectInfo.set("datasource", "import");
+        //业务类型
+        String bizType = ParamsUtil.getBizType(cbsObj.getString("PTCTYP"));
+        objectInfo.set("biztype", bizType);
+        //上划
+        if("2".equals(bizType)){
+            objectInfo.set("istransup", "1");
+        }
+        //下拨
+        if("3".equals(bizType)){
+            objectInfo.set("istransdown", "1");
+        }
+
+        //币别
+        filterNumber = new QFilter("number", QCP.equals, ParamsUtil.CurrencyMap.get(cbsObj.getString("CCYNBR")));
+        filters = new QFilter[]{filterNumber};
+        DynamicObject currency = BusinessDataServiceHelper.loadSingleFromCache("bd_currency", filters);
+        objectInfo.set("currency", currency);
+
+        CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(objectInfo.getDataEntityType().getName(), objectInfo, null);
+        String billno = CodeRuleServiceHelper.getNumber(codeRule, objectInfo);
+        objectInfo.set("billno", billno);
+
+        return objectInfo;
+    }
+
+    /**
+     * 自定义返回data对象
+     * @param code
+     * @param message
+     * @return
+     */
+    public CustomApiResult<JSONObject> returnResult(String code, String message){
+        JSONObject reslutData = new JSONObject();
+        reslutData.put("code", code);
+        reslutData.put("message", message);
+        CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
+
+        return result;
+    }
+}

+ 254 - 0
main/java/kd/cosmic/jkjt/tmc/api/SynElectronicReceipt2Plugin.java

@@ -0,0 +1,254 @@
+package kd.cosmic.jkjt.tmc.api;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.algo.DataSet;
+import kd.bos.algo.Row;
+import kd.bos.db.DB;
+import kd.bos.db.DBRoute;
+import kd.bos.openapi.common.custom.annotation.ApiController;
+import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
+import kd.bos.openapi.common.result.CustomApiResult;
+import kd.bos.orm.ORM;
+import kd.bos.util.StringUtils;
+import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
+import kd.cosmic.jkjt.tmc.util.ParamsUtil;
+import kd.cosmic.jkjt.tmc.util.XmlUtils;
+import org.apache.commons.lang.time.DateFormatUtils;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author wanghaiwu_kd
+ * @date 2023-03-31
+ * @description 同步电子回单API插件
+ */
+@ApiController(
+        desc = "同步CBS电子回单",
+        value = "jkjt_cbsapi"
+)
+public class SynElectronicReceipt2Plugin implements Serializable {
+    private static final long serialVersionUID = 2436590275855963609L;
+    //附件实体编码
+    private static final String KEY_ENTITY_ATTACHMENT = "bos_attachment_management";
+    private static final String KEY_TABLE_ATTACHMAPPING = "T_BAS_FILEPATHMAPPING";
+
+    @ApiPostMapping("SynElectronicReceipt")
+    public CustomApiResult<JSONObject> doCustomerService(){
+        //电子回单查询ERDRCQRY报文内容
+        String receiptQueryBody = getReceiptQueryBody();
+
+        String paramQueryBody = CBSToolUtil.getBodyString(receiptQueryBody);
+
+        String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        System.out.println("调用CBS接口ERDRCQRY开始时间:"+ sTime);
+
+        String sr = CBSToolUtil.sendPost("http://192.168.250.225:8887", paramQueryBody);
+
+        String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        System.out.println("调用CBS接口ERDRCQRY结束时间:"+ eTime);
+
+        String cbssr = CBSToolUtil.parseXMLcbs(sr);
+        CustomApiResult<JSONObject> result = saveDetailData(cbssr);
+
+        return result;
+    }
+
+    /**
+     * 回单查询Body
+     * @return
+     */
+    private String getReceiptQueryBody(){
+        StringBuffer body = new StringBuffer();
+
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>ERDRCQRY</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<DCDRCQRYA></DCDRCQRYA>");//账号
+        body.append("<DCDRCQRYX>\r\n");
+        body.append("<STADAT>2023-03-23</STADAT>\r\n");
+        body.append("<ENDDAT>2023-03-23</ENDDAT>\r\n");
+        body.append("</DCDRCQRYX>\r\n");
+        body.append("<DCDRCSEQZ>\r\n");
+        body.append("<SQRNB1></SQRNB1>\r\n");//续传时序号,从响应报文直接获取并提交
+        body.append("</DCDRCSEQZ>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 回单打印body
+     * @return
+     */
+    private String getReceiptPrintBody(JSONObject queryObj){
+        StringBuffer body = new StringBuffer();
+
+        //流水号
+        String sqrnb1 = queryObj.getString("SQRNB1");
+        //打印实例号
+        String istnbr = queryObj.getString("ISTNBR");
+        //本方账号
+        String accnbr = queryObj.getString("ACCNBR");
+
+        body.append("<CBSERPPGK>\r\n");
+        body.append("<INFO>\r\n");
+        body.append("<FUNNAM>DCPRTRCP</FUNNAM>\r\n");
+        body.append("</INFO>\r\n");
+        body.append("<DCPRTRCPX>\r\n");
+        body.append("<SQRNB1>").append(sqrnb1).append("</SQRNB1>\r\n");
+        body.append("<ISTNBR>").append(istnbr).append("</ISTNBR>\r\n");
+        body.append("<ACCNBR>").append(accnbr).append("</ACCNBR>\r\n");
+        body.append("<PRTFLG>2</PRTFLG>\r\n");
+        body.append("</DCPRTRCPX>\r\n");
+        body.append("</CBSERPPGK>\r\n");
+
+        return body.toString();
+    }
+
+    /**
+     * 处理cbs返回结果
+     * @param cbssr
+     * @return
+     */
+    private CustomApiResult<JSONObject> saveDetailData(String cbssr){
+        JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(cbsObj != null && cbsObj.get("DCDRQRYZ1") != null){
+            JSONArray array = (JSONArray)cbsObj.get("DCDRQRYZ1");
+
+            List<Object[]> params = new ArrayList<>();
+
+            String sql = "/*dialect*/INSERT INTO T_BAS_ATTACHMENT(FID, FNUMBER, FBILLTYPE, FINTERID, FCREATETIME, FCREATEMEN, FMODIFYTIME, FMODIFYMEN, \r\n";
+            sql += "FFILESTORAGE, FFILEID, FALIASFILENAME, FATTACHMENTNAME, FEXTNAME, FATTACHMENTSIZE, FATTACHMENTPANEL, FSORT, FFILESOURCE) \r\n";
+            sql += "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, replace(newid(), '-', ''), ?, ?, ?, ?, ?, ?, ?)";
+
+            for(int i = 0; i < array.size(); i++){
+                Object[] param = null;
+                try {
+                    param = parseElectronicReceipt(array.getJSONObject(i));
+                } catch (ParseException e) {
+                    throw new RuntimeException(e);
+                }
+
+                if(param != null) {
+                    params.add(param);
+                }
+            }
+
+            if(params.size() > 0){
+                int[] execBatchAttach = DB.executeBatch(new DBRoute("sys"), sql, params);
+                if (execBatchAttach.length > 0) {
+                }
+
+                sql = "/*dialect*/select t1.ffileid, t1.fattachmentname from t_bas_attachment t1 left join t_bas_filepathmapping t2 on t1.ffileid = t2.ffileid \r\n";
+                sql += "where t1.fnumber like 'cbs%' and t2.ffileid is null";
+                DataSet dataSet = DB.queryDataSet(this.getClass().getName(), DBRoute.of("sys"), sql, null);
+                if(!dataSet.isEmpty() && dataSet.hasNext()){
+                    params.clear();
+
+                    sql = "/*dialect*/INSERT INTO " + KEY_TABLE_ATTACHMAPPING + "(FID, FFILEID, FPATH) \r\n";
+                    sql += "VALUES(?, ?, ?)";
+                    for (Row row : dataSet) {
+                        Long fid = DB.genLongId(KEY_TABLE_ATTACHMAPPING);
+                        String fileId = row.getString("ffileid");
+                        String attachname = row.getString("fattachmentname");
+                        String path = "/jingkong-uat/cbs/" + attachname;
+                        params.add(new Object[]{fid, fileId, path});
+                    }
+                    dataSet.close();
+
+                    if(params.size() > 0) {
+                        int[] execBatchMapping = DB.executeBatch(new DBRoute("sys"), sql, params);
+                        if (execBatchMapping.length > 0) {
+                        }
+                    }
+                }
+            }
+        }
+
+        String code = "100";
+        String message = "同步成功";
+        return ParamsUtil.returnResult(code, message);
+    }
+
+    /**
+     * 创建电子回单附件
+     * @param cbsObj
+     * @return
+     */
+    private Object[] parseElectronicReceipt(JSONObject cbsObj) throws ParseException {
+        //电子回单查询DCPRTRCP报文内容
+        String receiptPrintBody = getReceiptPrintBody(cbsObj);
+
+        String paramPrintBody = CBSToolUtil.getBodyString(receiptPrintBody);
+
+        String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        System.out.println("调用CBS接口DCPRTRCP开始时间:"+ sTime);
+
+        String sr = CBSToolUtil.sendPost("http://192.168.250.225:8887", paramPrintBody);
+
+        String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        System.out.println("调用CBS接口DCPRTRCP结束时间:"+ eTime);
+
+        String cbssr = CBSToolUtil.parseXMLcbs(sr);
+
+        JSONObject attObj = XmlUtils.documentToJSONObject(cbssr);
+
+        if(attObj == null || attObj.get("DCPRTRCPZ") == null){
+            return null;
+        }
+
+        JSONArray array = (JSONArray)attObj.get("DCPRTRCPZ");
+
+        if(array.size() == 0){
+            return null;
+        }
+        attObj = array.getJSONObject(0);
+
+        //回单路径为空时,忽略此记录
+        if(StringUtils.isEmpty(attObj.getString("IMPATH"))){
+            return null;
+        }
+
+        String filePath = attObj.getString("IMPATH");
+        String filename = filePath.substring(filePath.lastIndexOf("\\") + 1);
+        String type = filename.substring(filename.indexOf(".pdf") + 1);
+
+        String billType = "conm_purcontract";
+        String billId = "1641317307160461312";
+
+        if(isExists(billType, billId, filename)){
+            return null;
+        }
+
+        Long fid = ORM.create().genLongId(KEY_ENTITY_ATTACHMENT);
+        String fnumber = "cbs-" + fid.toString();
+        Timestamp ts = new Timestamp((new Date()).getTime());
+        Long userId = 1610812001158702080L;
+
+        Object[] param = new Object[]{fid, fnumber, billType, billId, ts, userId, ts, userId, '0', filename, filename, type, "98662", "attachmentpanel", 0, 1};
+
+        return param;
+    }
+
+    /**
+     * 校验附件是否已存在
+     * @param billId
+     * @param attName
+     * @return
+     */
+    private boolean isExists(String billType, String billId, String attName) {
+        String sql = "/*dialect*/select fid from t_bas_attachment where fnumber like 'cbs%' and fbilltype = ? and finterid = ? and fattachmentname = ?";
+        DataSet dataSet = DB.queryDataSet(this.getClass().getName(), DBRoute.of("sys"), sql, new Object[]{billType, billId, attName});
+        boolean isExists = dataSet.hasNext();
+        dataSet.close();
+        return isExists;
+    }
+}

部分文件因为文件数量过多而无法显示