瀏覽代碼

完整流程代码提交

“lqy 4 天之前
父節點
當前提交
24da7a7fc2

+ 20 - 4
mjava-lilin/src/main/java/com/malk/lilin/Controller/taskController.java

@@ -25,8 +25,8 @@ import java.time.format.DateTimeFormatter;
 @RestController
 @Configuration
 @Slf4j
-//@EnableScheduling
-//@ConditionalOnProperty(name = {"enable.scheduling"})
+@EnableScheduling
+@ConditionalOnProperty(name = {"enable.scheduling"})
 @RequestMapping("/ll")
 public class taskController {
     @Autowired
@@ -59,9 +59,10 @@ public class taskController {
     public String  token1() {
         return (String) token().getData();
     }
-    @Scheduled(cron = "0 */5 * * * ?")
+//    @Scheduled(cron = "0 44 14 * * ?")
+//    @Scheduled(fixedRate = 30 * 60 * 1000)
     public void task() {
-        log.info("定时获取付款结果开始:{}", LocalDateTime.now());
+        log.info("定时获取单笔付款结果开始:{}", LocalDateTime.now());
         try {
             lilinService.lilinJG(null);
         } catch (Exception e) {
@@ -71,6 +72,21 @@ public class taskController {
 
 
     }
+
+//    @Scheduled(fixedRate = 60 * 60 * 1000)
+    public void taskLS() {
+        log.info("定时获取银行流水结果开始:{}", LocalDateTime.now());
+        try {
+            lilinService.lilinLS(null);
+        } catch (Exception e) {
+            // 记录错误信息
+            e.printStackTrace();
+        }
+
+
+    }
+
+
     private static McR token() {
         String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
         String s = SecureUtil.md5("KHPuj0JMS6nS1ACzc#" + today);

+ 588 - 275
mjava-lilin/src/main/java/com/malk/lilin/Service/impl/LiLinServiceImpl.java

@@ -3,6 +3,7 @@ package com.malk.lilin.Service.impl;
 
 import cn.hutool.crypto.SecureUtil;
 
+import com.alibaba.excel.util.StringUtils;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
@@ -324,10 +325,10 @@ public class LiLinServiceImpl implements LiLinService {
                                 .build(), YDConf.FORM_OPERATION.multi_update);
                     }
                 }
-                } catch(Exception e){
-                    log.error("更新主表单或子表单状态时出错", e);
-                    return McR.errorParam("更新失败");
-                }
+            } catch (Exception e) {
+                log.error("更新主表单或子表单状态时出错", e);
+                return McR.errorParam("更新失败");
+            }
 
             return McR.success();
 
@@ -629,329 +630,606 @@ public class LiLinServiceImpl implements LiLinService {
         Map params = new HashMap<>();
 //        mapAll.put("securityCode", securityCode);
         mapAll.put("securityCode", securityCode);
-        String response = UtilHttp.doPost(url, headers,  null,null,mapAll);//批量制单
+        String response = UtilHttp.doPost(url, headers, null, null, mapAll);//批量制单
         log.info("response:{}", response);
     }
 
     @Override
     public McR lilinZD(Map<String, Object> map) throws IOException {
-//        String formInstanceId = String.valueOf(map.get("formInstanceId"));
-//        log.info("formInstanceId:{}", formInstanceId);
-//        //根据实例ID获取表单数据
-//        Map data = (Map) ydClient.queryData(YDParam.builder().formInstId(formInstanceId)
-//                .appType(ydConf.getAppType()).systemToken(ydConf.getSystemToken())
-//                .userId(ddConf.getOperator()).build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
-//        log.info("data:{}", data);
-//        String instructionId = String.valueOf(data.get("serialNumberField_m9ze3rjd"));//电子凭证号
-//        String totalTrxNumber = String.valueOf(data.get("serialNumberField_m9ze3rjd"));//批量转账总流水号
-        String totalTrxNumber = String.valueOf(map.get("totalTrxNumber"));//批量转账总流水号
-        String source = "OA";//来源
-//        String note = String.valueOf(data.get("textField_m9ze3rk3"));//付款用途
-//        String toAccountCode = String.valueOf(data.get("textField_m9ze3rjs"));//收款账号
-//        String toAccountName = String.valueOf(data.get("textField_m9ze3rjl"));//收款账户名
-//        String toBank = String.valueOf(data.get("textField_m9ze3rjr"));//收款账户所在银行支行
-//        String fromAccountCode = String.valueOf(data.get("textField_mdfnuyl9"));//付款账号
-//        String fromAccountName = String.valueOf(data.get("textField_m9ze3rj8"));//付款账户名
-//        String payAmount = String.valueOf(data.get("numberField_m9ze3rk4"));//转账金额
-//     //   String  toAccountType = String.valueOf(data.get("selectField_m9ze3rjf"));//收款账户所在银行支行
-//        String xmbh = String.valueOf(data.get("textField_madmzxce"));
-//        String xmmc = String.valueOf(data.get("textField_mayubz25"));
-//        String projectInfo = xmbh + "-" + xmmc;//项目信息
-//        HashMap metaJson1 = new HashMap();
-//        metaJson1.put("projectInfo", projectInfo);
-//        String metaJson = JSON.toJSONString(metaJson1);
-
-
-//        List<Map<String, Object>> tableField = (List<Map<String, Object>>) data.get("tableField_m9m8zhyy");
-        List<Map<String, Object>> tableField = new ArrayList<>();
-
-        Map<String, Object> data1 = new HashMap<>();
-        data1.put("trxNumber", "FK20250723123");
-        data1.put("fromAccountCode", "755915704010801");
-        data1.put("fromAccountName", "企业网银新20161341");
-        data1.put("toAccountCode", "6214831150131511");
-        data1.put("toAccountType", "0");
-        data1.put("toAccountName", "吴极客");
-        data1.put("payAmount", "1.00");
-        data1.put("uses", "这是一个测试用途");
-//        data1.put("instructionId", "F00000123");
+        String formInstanceId = String.valueOf(map.get("formInstanceId"));
+        log.info("formInstanceId:{}", formInstanceId);
 
-        Map<String, Object> data2 = new HashMap<>();
-        data2.put("trxNumber", "FK20250723121");
-        data2.put("fromAccountCode", "755915704010801");
-        data2.put("fromAccountName", "企业网银新20161341");
-        data2.put("toAccountCode", "6214831150131511");
-        data2.put("toAccountType", "0");
-        data2.put("toAccountName", "吴极客");
-        data2.put("payAmount", "1.00");
-        data2.put("uses", "这是一个测试用途");
-//        data2.put("instructionId", "F00000123");
+        // 获取表单数据
+        Map data = (Map) ydClient.queryData(YDParam.builder()
+                .formInstId(formInstanceId)
+                .appType(ydConf.getAppType())
+                .systemToken(ydConf.getSystemToken())
+                .userId(ddConf.getOperator())
+                .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
 
-// 添加到集合
-        tableField.add(data1);
-        tableField.add(data2);
-        String  totalRecord = String.valueOf(tableField.size());//总笔数
-//
-//        List<Map<String, Object>> list = new ArrayList<>();
-//        for (int i = 0; i < tableField.size(); i++) {
-//
-//            Map<String, Object> tableField1 = tableField.get(i);
-//            String ltrxNumber = String.valueOf(tableField1.get("textField_m9ze3rjt"));
-//            String lfromAccountCode = String.valueOf(tableField1.get("textField_m9ze3rjv"));
-//            String lfromAccountName = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            String ltoAccountCode = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            String ltoAccountType = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            String ltoAccountName = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            String lpayAmount = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            String luses = String.valueOf(tableField1.get("selectField_m9ze3rjx"));
-//            HashMap<String, Object> tableField2 = new HashMap<>();
-//            tableField2.put("trxNumber", ltrxNumber);
-//            tableField2.put("fromAccountCode", lfromAccountCode);
-//            tableField2.put("fromAccountName", lfromAccountName);
-//            tableField2.put("toAccountCode", ltoAccountCode);
-//            tableField2.put("toAccountType", ltoAccountType);
-//            tableField2.put("toAccountName", ltoAccountName);
-//            tableField2.put("payAmount", lpayAmount);
-//            tableField2.put("uses", luses);
-//            list.add(tableField2);
-//        }
-        String securityCode = String.valueOf(token().getData());
-        HashMap mapAll = new HashMap<>();
-        mapAll.put("totalRecord", totalRecord);
-        mapAll.put("list", JSONObject.toJSONString(tableField));
-        mapAll.put("totalTrxNumber", totalTrxNumber);
-        mapAll.put("source", source);
-        mapAll.put("securityCode", securityCode);
+        log.info("form data: {}", data);
 
-        String url = "https://openapi.xencio.com/sandbox/cfa/api/payment/batchTransfer";
+        // 主表字段(复用)
+        String fklx = String.valueOf(data.get("selectField_m9ze3rjf")); // 付款类型
+        String note = String.valueOf(data.get("textField_m9ze3rk3")); // 付款用途
+        String toAccountCode = String.valueOf(data.get("textField_m9ze3rjs")); // 收款账号
+        String toAccountName = String.valueOf(data.get("textField_m9ze3rjl")); // 收款户名
+        String toBank = String.valueOf(data.get("textField_m9ze3rjr")); // 收款银行支行
+        String fromAccountCode = String.valueOf(data.get("textField_mdfnuyl9")); // 付款账号
+        String fromAccountName = String.valueOf(data.get("textField_m9ze3rj8")); // 付款户名
 
-        Map headers = new HashMap<>();
+        // 公共参数
+        String securityCode = String.valueOf(token().getData());
+        Map<String, String> headers = new HashMap<>();
         headers.put("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a");
-        headers.put("content-Type", "application/x-www-form-urlencoded");
-//        Map params = new HashMap<>();
+        headers.put("Content-Type", "application/x-www-form-urlencoded");
+        String url = "https://openapi.xencio.com/sandbox/cfa/api/payment/transfer";
+
+        // 判断是否为物流付款单
+        if ("物流付款单".equals(fklx)) {
+            // 是物流付款单:遍历子表,每行发起一次支付
+            List<Map<String, Object>> tableField = (List<Map<String, Object>>) data.get("tableField_m986c5os");
+            if (tableField == null || tableField.isEmpty()) {
+                log.warn("物流付款单但子表为空,formInstanceId: {}", formInstanceId);
+                return McR.errorParam("子表数据为空");
+            }
 
-        String response = UtilHttp.doPost(url, headers, null, null, mapAll);//批量制单
-        log.info("response:{}", response);
+            for (Map<String, Object> row : tableField) {
+                String instructionId = String.valueOf(row.get("textField_mdnuob9a")); // 子表电子凭证号
+                String xmmc = String.valueOf(row.get("textField_m9lfmh1o")); // 项目名称
+                String xmbh = String.valueOf(row.get("textField_m9lfmh1p")); // 项目编号
+                String payAmount = String.valueOf(row.get("numberField_m8yf6gn1")); // 子表金额
+
+                // 构建 projectInfo
+                String projectInfo = xmbh + "-" + xmmc;
+                Map<String, Object> metaJson1 = new HashMap<>();
+                metaJson1.put("projectInfo", projectInfo);
+                String metaJson = JSON.toJSONString(metaJson1);
+
+                // 组装请求参数
+                Map<String, Object> mapAll = new HashMap<>();
+                mapAll.put("instructionId", instructionId);
+                mapAll.put("fromAccountCode", fromAccountCode);
+                mapAll.put("fromAccountName", fromAccountName);
+                mapAll.put("toAccountCode", toAccountCode);
+                mapAll.put("toAccountName", toAccountName);
+                mapAll.put("payAmount", payAmount);
+                mapAll.put("note", note);
+                mapAll.put("toBank", toBank);
+                mapAll.put("securityCode", securityCode);
+                mapAll.put("metaJson", metaJson);
+
+                // 发起支付
+                String response = UtilHttp.doPost(url, headers, null, null, mapAll);
+                log.info("物流付款单 - 支付成功 [凭证号: {}, 金额: {}], 响应: {}", instructionId, payAmount, response);
+            }
 
+        } else {
+            // 不是物流付款单:使用主表字段,发起一次支付
+            String instructionId = String.valueOf(data.get("serialNumberField_m9ze3rjd")); // 主表电子凭证号
+            String payAmount = String.valueOf(data.get("numberField_m9ze3rk4")); // 主表金额
+            String xmbh = String.valueOf(data.get("textField_madmzxce"));
+            String xmmc = String.valueOf(data.get("textField_mayubz25"));
+            String projectInfo = xmbh + "-" + xmmc;
+
+            Map<String, Object> metaJson1 = new HashMap<>();
+            metaJson1.put("projectInfo", projectInfo);
+            String metaJson = JSON.toJSONString(metaJson1);
+
+            Map<String, Object> mapAll = new HashMap<>();
+            mapAll.put("instructionId", instructionId);
+            mapAll.put("fromAccountCode", fromAccountCode);
+            mapAll.put("fromAccountName", fromAccountName);
+            mapAll.put("toAccountCode", toAccountCode);
+            mapAll.put("toAccountName", toAccountName);
+            mapAll.put("payAmount", payAmount);
+            mapAll.put("note", note);
+            mapAll.put("toBank", toBank);
+            mapAll.put("securityCode", securityCode);
+            mapAll.put("metaJson", metaJson);
+
+            String response = UtilHttp.doPost(url, headers, null, null, mapAll);
+            log.info("非物流付款单 - 支付成功 [凭证号: {}, 金额: {}], 响应: {}", instructionId, payAmount, response);
+        }
 
-//        OkHttpClient client = new OkHttpClient();
-//
-//        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
-//        RequestBody body = RequestBody.create(mediaType, "securityCode=" + token().getData() + "&fromAccountCode=" + fromAccountCode + "&fromAccountName=" + fromAccountName + "&toAccountCode=" + toAccountCode + "&toAccountName=" + toAccountName + "&toAccountType=0&toBank=" + toBank + "&payAmount=" + payAmount + "&note=" + note + "&instructionId=" + instructionId + "&metaJson=" + metaJson);
-//        Request request = new Request.Builder()
-//                .url("https://openapi.xencio.com/sandbox/cfa/api/payment/transfer")
-//                .post(body)
-//                .addHeader("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a")
-//                .addHeader("Content-Type", "application/x-www-form-urlencoded")
-//                .build();
-//
-//        Response response = client.newCall(request).execute();
-//        String jsonstr = response.body().string();
-//        JSONObject jsonObject = JSONObject.parseObject(jsonstr);
-//        JSONObject data1 = jsonObject.getJSONObject("data");
-//        String trxNumber = data1.getString("trxNumber");
-
-//        return McR.success(trxNumber);
-        return McR.success();
+        return McR.success("处理完成");
     }
+//TODO
+
 
     @Override
     public McR lilinJG(Map<String, Object> map) throws IOException {
-        String formInstanceId = String.valueOf(map.get("formInstanceId"));
-        log.info("formInstanceId:{}", formInstanceId);
-        //根据实例ID获取表单数据
-        Map data = (Map) ydClient.queryData(YDParam.builder().formInstId(formInstanceId)
-                .appType(ydConf.getAppType()).systemToken(ydConf.getSystemToken())
-                .userId(ddConf.getOperator()).build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
-        log.info("data:{}", data);
-        String instructionId = String.valueOf(data.get("serialNumberField_m9ze3rjd"));//表单流水号
-//        String instructionId = String.valueOf(map.get("instructionId"));//表单流水号
-
-        String url = "https://openapi.xencio.com/sandbox/cfa/api/payment/batchTransferStatus";
-        String securityCode = String.valueOf(token().getData());
+        // 1. 查询所有需要处理的单据(状态为“已审批待付款”或“已审批部分付款”)
+        List<Map> list = (List<Map>) ydClient.queryData(YDParam.builder()
+                .formUuid("FORM-1A612AA91BAC467CB9823B6CB92491152WZH")
+                .searchFieldJson(JSONObject.toJSONString(Arrays.asList(
+                        new YDSearch("selectField_ma0w9yrb",
+                                Arrays.asList("已审批待付款", "已审批部分付款"),
+                                "SelectField",
+                                YDSearch.Type.RADIO_FIELD,
+                                YDSearch.Operator.CONTAINS)
+                )))
+            .build(), YDConf.FORM_QUERY.retrieve_list_all).getData();
+
+        if (list == null || list.isEmpty()) {
+            log.info("没有找到需要处理的单据");
+            return McR.success("无待处理数据");
+        }
 
-        Map headers = new HashMap<>();
+        log.info("共查询到 {} 条待处理单据", list.size());
 
-        headers.put("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a");
-        headers.put("content-Type", "application/x-www-form-urlencoded");
+        // 公共参数提前准备(安全码、URL、请求头等)
+        String url = "https://openapi.xencio.com/sandbox/cfa/api/payment/transferStatus";
+        String securityCode = String.valueOf(token().getData());
 
-        Map params = new HashMap<>();
+        Map<String, String> headers = new HashMap<>();
+        headers.put("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a");
+        headers.put("Content-Type", "application/x-www-form-urlencoded");
 
+        Map<String, Object> params = new HashMap<>();
         params.put("securityCode", securityCode);
-        params.put("totalTrxNumber", instructionId);
-        String response1 = UtilHttp.doGet(url, headers, params);//批量结果
-        JSONObject jsonObject = JSONObject.parseObject(response1);
-        JSONObject data1 = jsonObject.getJSONObject("data");
-        String message = data1.getString("message");
-//        OkHttpClient client = new OkHttpClient();
-//        Request request = new Request.Builder()
-//                .url("https://openapi.xencio.com/sandbox/cfa/api/payment/transferStatus?securityCode=" + token().getData() + "&trxNumber=" + instructionId)
-//                .get()
-//                .addHeader("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a")
-//                .addHeader("content-type", "application/x-www-form-urlencoded")
-//                .build();
-//        Response response = client.newCall(request).execute();
-//        String jsonstr = response.body().string();
-//        JSONObject jsonObject = JSONObject.parseObject(jsonstr);
-//        JSONObject data1 = jsonObject.getJSONObject("data");
-//        String message = data1.getString("message");
-//        if (message.contains("成功")) {
-//            ydClient.operateData(YDParam.builder().formInstanceId(formInstanceId)
-//                    .updateFormDataJson(JSON.toJSONString(UtilMap.map("selectField_ma0w9yrb", "已审批已付款")))
-//                    .build(), YDConf.FORM_OPERATION.update);
-//        }
-        return McR.success(response1);
+
+        int successCount = 0;
+        int failCount = 0;
+        List<String> failedIds = new ArrayList<>();
+
+        // 2. 遍历 list,逐个执行原 lilinJG 中的处理逻辑
+        for (Map item : list) {
+            String formInstanceId = String.valueOf(item.get("formInstanceId"));
+            if (formInstanceId == null || "null".equals(formInstanceId.trim()) || formInstanceId.trim().isEmpty()) {
+                log.warn("跳过无效的 formInstanceId: {}", item.get("formInstanceId"));
+                continue;
+            }
+
+            try {
+                log.info("开始处理单据: formInstanceId={}", formInstanceId);
+
+                // ===== 开始执行原 lilinJG 方法中的核心逻辑 =====
+
+                // 获取表单数据
+                Map data = (Map) ydClient.queryData(YDParam.builder()
+                        .formInstId(formInstanceId)
+                        .appType(ydConf.getAppType())
+                        .systemToken(ydConf.getSystemToken())
+                        .userId(ddConf.getOperator())
+                        .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+
+                if (data == null) {
+                    log.warn("获取表单数据为空,formInstanceId: {}", formInstanceId);
+                    failCount++;
+                    failedIds.add(formInstanceId);
+                    continue;
+                }
+
+                log.info("form data: {}", data);
+
+                String fklx = String.valueOf(data.get("selectField_m9ze3rjf")); // 付款类型
+                String mainInstructionId = String.valueOf(data.get("serialNumberField_m9ze3rjd")); // 主表流水号
+
+                // ===== 区分处理:是否为物流付款单 =====
+                if ("物流付款单".equals(fklx)) {
+                    // 物流付款单:处理子表
+                    List<Map<String, Object>> tableField = (List<Map<String, Object>>) data.get("tableField_m986c5os");
+                    if (tableField == null || tableField.isEmpty()) {
+                        log.warn("物流付款单但子表为空,formInstanceId: {}", formInstanceId);
+                        failCount++;
+                        failedIds.add(formInstanceId);
+                        continue;
+                    }
+
+                    List<Map<String, Object>> updatedRows = new ArrayList<>();
+                    int completedCount = 0;
+
+                    for (int i = 0; i < tableField.size(); i++) {
+                        Map<String, Object> row = new HashMap<>(tableField.get(i)); // 复制原数据用于更新
+                        String subInstructionId = String.valueOf(row.get("textField_mdnuob9a")); // 子表流水号
+
+                        // 查询子表支付状态
+                        params.put("instructionId", subInstructionId);
+                        String response = UtilHttp.doGet(url, headers, params);
+                        log.info("查询子表支付状态 [instructionId: {}], 响应: {}", subInstructionId, response);
+
+                        JSONObject jsonResponse = JSON.parseObject(response);
+                        JSONObject dataObj = jsonResponse.getJSONObject("data");
+                        String message = dataObj != null ? dataObj.getString("message") : "";
+
+                        String status = "未完成";
+                        if (message != null && message.contains("成功")) {
+                            status = "已完成";
+                            completedCount++;
+                        }
+
+                        // 更新子表字段:selectField_mdmwfibp
+                        row.put("selectField_mdmwfibp", status);
+                        updatedRows.add(row);
+                    }
+
+                    // 构建要更新的子表数据
+                    Map<String, Object> updateData = new HashMap<>();
+                    updateData.put("tableField_m986c5os", updatedRows);
+
+                    // 更新主表状态
+                    String mainStatus;
+                    if (completedCount == 0) {
+                        mainStatus = "已审批待付款";
+                    } else if (completedCount == updatedRows.size()) {
+                        mainStatus = "已审批已付款";
+                    } else {
+                        mainStatus = "已审批部分付款";
+                    }
+
+                    updateData.put("selectField_ma0w9yrb", mainStatus);
+
+                    // 提交更新
+                    ydClient.operateData(YDParam.builder()
+                            .formInstanceId(formInstanceId)
+                            .updateFormDataJson(JSON.toJSONString(updateData))
+                            .build(), YDConf.FORM_OPERATION.update);
+
+                    log.info("【物流】状态更新完成,主表状态: {}", mainStatus);
+                    successCount++;
+
+                } else {
+                    // 非物流付款单:主表单处理
+                    if (mainInstructionId == null || mainInstructionId.isEmpty()) {
+                        log.warn("主表流水号为空,formInstanceId: {}", formInstanceId);
+                        failCount++;
+                        failedIds.add(formInstanceId);
+                        continue;
+                    }
+
+                    params.put("instructionId", mainInstructionId);
+                    String response = UtilHttp.doGet(url, headers, params);
+                    log.info("查询主表支付状态 [instructionId: {}], 响应: {}", mainInstructionId, response);
+
+                    JSONObject jsonResponse = JSON.parseObject(response);
+                    JSONObject dataObj = jsonResponse.getJSONObject("data");
+                    String message = dataObj != null ? dataObj.getString("message") : "";
+
+                    if (message != null && message.contains("成功")) {
+                        // 更新主表状态为“已审批已付款”
+                        ydClient.operateData(YDParam.builder()
+                                .formInstanceId(formInstanceId)
+                                .updateFormDataJson(JSON.toJSONString(
+                                        UtilMap.map("selectField_ma0w9yrb", "已审批已付款")))
+                                .build(), YDConf.FORM_OPERATION.update);
+                        log.info("【非物流】支付成功,已更新状态");
+                        successCount++;
+                    } else {
+                        log.info("【非物流】支付未成功,状态未更新");
+                        failCount++;
+                        failedIds.add(formInstanceId);
+                    }
+                }
+
+                // ===== 单条处理结束 =====
+
+            } catch (Exception e) {
+                log.error("处理单据失败: formInstanceId={}", formInstanceId, e);
+                failCount++;
+                failedIds.add(formInstanceId);
+            }
+        }
+
+        // 3. 返回批量处理结果
+        return McR.success("批量状态同步完成");
+
     }
 
+
     @Override
-    public McR lilinLS(Map<String, Object> map) throws IOException {
-        String accountNo = String.valueOf(map.get("accountNo"));
-        OkHttpClient client = new OkHttpClient();
-        String dayFromId = LocalDate.now().minusDays(3).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+    public McR lilinLS(Map<String, Object> map) {
+//        String accountNo = String.valueOf(map.get("accountNo"));
+        String accountNo = "888001";
+        if (accountNo == null || accountNo.trim().isEmpty()) {
+            return McR.errorParam("accountNo 不能为空");
+        }
+        accountNo = accountNo.trim();
+
+        log.info("开始对账单同步,accountNo: {}", accountNo);
+
+        // 准备时间范围
+        String dayFromId = LocalDate.now().minusDays(10).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
         String dayToId = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
 
+        // 获取 token 一次
+        String securityCode;
+        try {
+            securityCode = String.valueOf(token().getData());
+            if (securityCode == null || securityCode.isEmpty()) {
+                log.error("获取 securityCode 失败");
+                return McR.errorParam("安全码获取失败");
+            }
+        } catch (Exception e) {
+            log.error("调用 token() 异常", e);
+            return McR.errorParam("认证信息获取失败");
+        }
+
+        // 构建 POST 请求体
         MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
-        RequestBody body = RequestBody.create(mediaType, "securityCode=" + token().getData() + "&accountNo=" + accountNo + "&dayFromId=" + dayFromId + "&dayToId=" + dayToId + "&instructionIdFlag=1&pageNow=1&pageSize=20");
+        String requestBodyStr = "securityCode=" + securityCode +
+                "&accountNo=" + accountNo +
+                "&dayFromId=" + dayFromId +
+                "&dayToId=" + dayToId +
+                "&instructionIdFlag=1" +
+                "&pageNow=1" +
+                "&trxFlag=R" +
+                "&pageSize=20";
+
+        RequestBody body = RequestBody.create(mediaType, requestBodyStr);
+
         Request request = new Request.Builder()
                 .url("https://openapi.xencio.com/sandbox/cfa/api/bs/list")
                 .post(body)
                 .addHeader("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a")
-                .addHeader("content-type", "application/x-www-form-urlencoded")
+                .addHeader("content-Type", "application/x-www-form-urlencoded")
                 .build();
 
-        Response response = client.newCall(request).execute();
-        String jsonstr = response.body().string();
-        JSONObject jsonObject = JSONObject.parseObject(jsonstr);
-        // 获取 data 对象
-        JSONObject data = jsonObject.getJSONObject("data");
-        // 获取 searchBankStatementList 数组
-        JSONArray statementList = data.getJSONArray("searchBankStatementList");
-        if (statementList == null) {
-            log.error("响应体为空");
-            return McR.errorParam("响应体为空");
-        }
-        // 用于存储提取的数据
-        List<Map<String, String>> extractedList = new ArrayList<>();
-
-        // 遍历数组,提取指定字段
-        for (int i = 0; i < statementList.size(); i++) {
-            JSONObject item = statementList.getJSONObject(i);
-
-            Map<String, String> extractedItem = new HashMap<>();
-
-            String bankStatementId = item.getString("bankStatementId");
-            String bankComments = item.getString("bankComments");
-            String userRemarks = item.getString("userRemarks");//用户备注
-            String crAmount = item.getString("crAmount");
-            String userMemo = item.getString("userMemo");
-            String transFlag = item.getString("transFlag");
-            String catalogName = item.getString("catalogName");
-            String trxDate = item.getString("trxDate");
-            long timestamp = LocalDateTime.parse(item.getString("trxDate"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
-            extractedItem.put("bankStatementId", bankStatementId);
-            extractedItem.put("bankComments", bankComments);
-            extractedItem.put("crAmount", crAmount);
-            extractedItem.put("trxDate", trxDate);
-            extractedItem.put("userRemarks", userRemarks);
-            extractedItem.put("customerName", item.getString("customerName"));
-
-            extractedList.add(extractedItem);
-            List<Map> list = (List<Map>) ydClient.queryData(YDParam.builder().formUuid("FORM-AC0D84A40AD64E469C6AC6D049931C22IDO8").appType("APP_V25MK3XKODKB02UVTGSH")
-                            .systemToken("IQC66GC1L5TW5HKF6SX645RC4AXG2CFGQOMCMOQ4").searchCondition(JSONObject.toJSONString(UtilMap.map("textField_mdh05xw6", bankStatementId)))
-                            .build(),
-                    YDConf.FORM_QUERY.retrieve_list_all).getData();
-            if (list == null || list.isEmpty()) {
-                Map formData = new HashMap();
-                formData.put("dateField_m8yeduje", timestamp);
-                formData.put("dateField_mbg6gyre", timestamp);
-                formData.put("numberField_m9ibr418", crAmount);
-                formData.put("textField_mdh05xw6", bankStatementId);
-                formData.put("textField_m9ibr419", userRemarks);
-                formData.put("textField_mdh51nxq", userMemo);
-                formData.put("textField_mdh51nxr", transFlag);
-                formData.put("textField_mdh51nxs", catalogName);
-                ydClient.operateData(YDParam.builder()
-                        .formUuid("FORM-AC0D84A40AD64E469C6AC6D049931C22IDO8")
-                        .processCode("TPROC--K8C66U6152SW46P3DKE38BFVC2AA3HQGQOMCM13")
-                        .formDataJson(JSON.toJSONString(formData))
-                        .userId("3044156512693449078")
-                        .build(), YDConf.FORM_OPERATION.start);
+        OkHttpClient client = new OkHttpClient();
+
+        Response response = null;
+        try {
+            response = client.newCall(request).execute();
+            if (!response.isSuccessful()) {
+                log.error("银行接口调用失败,HTTP状态码: {}", response.code());
+                return McR.errorParam("接口调用失败: " + response.code());
             }
 
+            String jsonStr = response.body().string();
+            log.debug("银行接口响应原始数据: {}", jsonStr);
 
+            JSONObject jsonObject = JSON.parseObject(jsonStr);
+            JSONObject data = jsonObject.getJSONObject("data");
+            if (data == null) {
+                log.error("响应中缺少 'data' 字段");
+                return McR.errorParam("响应数据格式错误:缺少 data");
+            }
+
+            JSONArray statementList = data.getJSONArray("searchBankStatementList");
+            if (statementList == null || statementList.isEmpty()) {
+                log.info("银行接口返回空列表,accountNo: {}", accountNo);
+                return McR.success("无对账单数据");
+            }
+
+            List<Map<String, String>> extractedList = new ArrayList<>();
+
+            for (int i = 0; i < statementList.size(); i++) {
+                JSONObject item = statementList.getJSONObject(i);
+                if (item == null) continue;
+
+                Map<String, String> extractedItem = new HashMap<>();
+                String bankStatementId = getStringValue(item, "bankStatementId");
+                String bankComments = getStringValue(item, "bankComments");
+                String crAmount = getStringValue(item, "crAmount");
+                String trxDate = getStringValue(item, "trxDate");
+                String userRemarks = getStringValue(item, "userRemarks");
+                String userMemo = getStringValue(item, "userMemo");
+                String transFlag = getStringValue(item, "transFlag");
+                String catalogName = getStringValue(item, "catalogName");
+                String customerName = getStringValue(item, "customerName");
+
+                // 提取时间戳
+                Long timestamp = null;
+                if (trxDate != null && !trxDate.isEmpty()) {
+                    try {
+                        timestamp = LocalDateTime.parse(trxDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+                                .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+                    } catch (Exception e) {
+                        log.warn("日期解析失败: {}", trxDate);
+                    }
+                }
+
+                // 填充提取数据
+                extractedItem.put("bankStatementId", bankStatementId);
+                extractedItem.put("bankComments", bankComments);
+                extractedItem.put("crAmount", crAmount);
+                extractedItem.put("trxDate", trxDate);
+                extractedItem.put("userRemarks", userRemarks);
+                extractedItem.put("customerName", customerName);
+                extractedList.add(extractedItem);
+
+                // ===== 查询是否已存在 =====
+                boolean exists = false;
+                try {
+                    List<Map> list = (List<Map>) ydClient.queryData(
+                            YDParam.builder()
+                                    .formUuid("FORM-AC0D84A40AD64E469C6AC6D049931C22IDO8")
+                                    .appType("APP_V25MK3XKODKB02UVTGSH")
+                                    .systemToken("IQC66GC1L5TW5HKF6SX645RC4AXG2CFGQOMCMOQ4")
+                                    .searchCondition(JSON.toJSONString(UtilMap.map("textField_mdh05xw6", bankStatementId)))
+                                    .build(),
+                            YDConf.FORM_QUERY.retrieve_list_all
+                    ).getData();
+
+                    exists = list != null && !list.isEmpty();
+                } catch (Exception e) {
+                    log.error("查询宜搭系统失败,bankStatementId: {}", bankStatementId, e);
+                }
+
+                // ===== 若不存在则创建 =====
+                if (!exists && bankStatementId != null && !bankStatementId.isEmpty()) {
+                    try {
+                        Map<String, Object> formData = new HashMap<>();
+                        formData.put("dateField_m8yeduje", timestamp != null ? timestamp : System.currentTimeMillis());
+                        formData.put("dateField_mbg6gyre", timestamp != null ? timestamp : System.currentTimeMillis());
+                        formData.put("numberField_m9ibr418", crAmount);
+                        formData.put("employeeField_m8yeduj3", Arrays.asList("275412081437800471"));
+                        formData.put("textField_mdh05xw6", bankStatementId);
+                        formData.put("textField_m9ibr419", userRemarks);
+                        formData.put("textField_mdh51nxq", userMemo);
+                        formData.put("textField_mdh51nxr", transFlag);
+                        formData.put("textField_mdh51nxs", catalogName);
+
+                        ydClient.operateData(YDParam.builder()
+                                .formUuid("FORM-AC0D84A40AD64E469C6AC6D049931C22IDO8")
+                                .processCode("TPROC--K8C66U6152SW46P3DKE38BFVC2AA3HQGQOMCM13")
+                                .formDataJson(JSON.toJSONString(formData))
+                                .userId("275412081437800471")
+                                .build(), YDConf.FORM_OPERATION.start);
+
+                        log.info("成功创建对账记录: bankStatementId={}", bankStatementId);
+                    } catch (Exception e) {
+                        log.error("创建对账记录失败: bankStatementId={}", bankStatementId, e);
+                    }
+                } else if (exists) {
+                    log.debug("记录已存在,跳过: bankStatementId={}", bankStatementId);
+                }
+            }
+
+            log.info("对账单同步完成,共处理 {} 条", extractedList.size());
+            return McR.success("同步完成"); // 可改为返回统计信息
+
+        } catch (IOException e) {
+            log.error("网络IO异常", e);
+            return McR.errorParam("网络请求失败: " + e.getMessage());
+        } catch (Exception e) {
+            log.error("未知异常", e);
+            return McR.errorParam("处理过程发生错误: " + e.getMessage());
+        } finally {
+            // 确保 response 被关闭
+            if (response != null) {
+                try {
+                    response.close();
+                } catch (Exception e) {
+                    log.warn("关闭 response 失败", e);
+                }
+            }
         }
+    }
 
-        return McR.success(extractedList);
+    // 工具方法:安全获取字符串值
+    private String getStringValue(JSONObject json, String key) {
+        Object value = json.get(key);
+        return value == null ? "" : value.toString();
     }
 
     @Override
     public McR lilinLSCF(Map<String, Object> map) throws IOException {
         String formInstanceId = String.valueOf(map.get("formInstanceId"));
+        if (formInstanceId == null || formInstanceId.trim().isEmpty()) {
+            return McR.errorParam("formInstanceId 不能为空");
+        }
+        log.info("开始处理对账单拆分,formInstanceId: {}", formInstanceId);
 
-        // 查询数据
-        Map data = (Map) ydClient.queryData(YDParam.builder()
-                .formInstId(formInstanceId)
-                .appType(ydConf.getAppType())
-                .systemToken(ydConf.getSystemToken())
-                .userId(ddConf.getOperator())
-                .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+        String bankStatementIdStr = String.valueOf(map.get("bankStatementId"));
+        if (bankStatementIdStr == null || bankStatementIdStr.trim().isEmpty()) {
+            return McR.errorParam("bankStatementId 不能为空");
+        }
 
-        // 初始化 body
-        HashMap body = new HashMap<>();
+        // 查询表单数据
+        Map data;
+        try {
+            data = (Map) ydClient.queryData(YDParam.builder()
+                    .formInstId(formInstanceId)
+                    .appType(ydConf.getAppType())
+                    .systemToken(ydConf.getSystemToken())
+                    .userId(ddConf.getOperator())
+                    .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+        } catch (Exception e) {
+            log.error("查询宜搭表单失败,formInstanceId: {}", formInstanceId, e);
+            return McR.errorParam("查询表单数据失败");
+        }
 
-        // 获取必要的字段值
-        String bankStatementId = String.valueOf(map.get("bankStatementId"));
-        String userMemo = String.valueOf(data.get("textField_mdh51nxq"));
-        String transFlag = String.valueOf(data.get("textField_mdh51nxr"));
-        String catalogName = String.valueOf(data.get("textField_mdh51nxs"));
+        if (data == null) {
+            log.warn("表单数据为空,formInstanceId: {}", formInstanceId);
+            return McR.errorParam("表单数据为空");
+        }
+
+        // 提取字段(安全获取)
+        String userMemo = getStringValue(data, "textField_mdh51nxq");
+        String transFlag = getStringValue(data, "textField_mdh51nxr");
+        String catalogName = getStringValue(data, "textField_mdh51nxs");
 
-        // 处理表格字段
+        // 处理子表数据
         List<Map<String, Object>> tableField = (List<Map<String, Object>>) data.get("tableField_m9m8zhyy");
-        ArrayList<Object> subBankStatementList = new ArrayList<>();
+        List<Map<String, Object>> subBankStatementList = new ArrayList<>();
 
         if (tableField != null && !tableField.isEmpty()) {
             for (int i = 0; i < tableField.size(); i++) {
-                Map<String, Object> map1 = tableField.get(i);
-                Map<String, Object> newMap = new HashMap<>();
-
-                int itemIndex = i + 1;
-                BigDecimal amount = getAmountFromMap(map1, "numberField_m9u2a5tf");
-                String projectInfo = String.valueOf(map1.get("textField_m8yedujm"));
-
-                newMap.put("itemIndex", itemIndex);
-                newMap.put("amount", amount.doubleValue()); // 转换为 double 类型
-                newMap.put("projectInfo", projectInfo);
-                newMap.put("userMemo", userMemo);
-                newMap.put("transFlag", transFlag);
-                newMap.put("catalogName", catalogName);
-
-                subBankStatementList.add(newMap);
+                Map<String, Object> row = tableField.get(i);
+                if (row == null) continue;
+
+                Map<String, Object> newItem = new HashMap<>();
+                newItem.put("itemIndex", i + 1);
+                newItem.put("amount", getAmountFromMap(row, "numberField_m9u2a5tf").doubleValue());
+                newItem.put("projectInfo", getStringValue(row, "textField_m9i9k5cl")+"-"+getStringValue(row, "textField_m8yedujm"));
+                newItem.put("userMemo", userMemo);
+                newItem.put("trxFlag", transFlag);
+                newItem.put("catalogName", catalogName);
+
+                subBankStatementList.add(newItem);
             }
         }
 
-        // 构建请求体
-        body.put("subBankStatementList", subBankStatementList);
-        body.put("bankStatementId", Integer.parseInt(bankStatementId)); // 需要转换为 integer 类型
+        // 构建主请求体
+        Map<String, Object> requestBody = new HashMap<>();
+        try {
+            requestBody.put("bankStatementId", Integer.parseInt(bankStatementIdStr));
+        } catch (NumberFormatException e) {
+            log.error("bankStatementId 格式错误: {}", bankStatementIdStr);
+            return McR.errorParam("bankStatementId 必须为整数");
+        }
+        requestBody.put("subBankStatementList", subBankStatementList);
 
-        // 发送 POST 请求
+        // 请求参数
         String url = "https://openapi.xencio.com/sandbox/cfa/api/sub/bs/splitBankStatement";
         String securityCode = String.valueOf(token().getData());
-        Map headers = new HashMap<>();
+        if (securityCode == null || securityCode.isEmpty()) {
+            log.error("获取 securityCode 失败");
+            return McR.errorParam("认证失败");
+        }
+
+        Map<String, String> headers = new HashMap<>();
         headers.put("x-xencio-client-id", "4d9414e89bc24b0d89b678d9f20bc56a");
-        Map params = new HashMap<>();
-        params.put("securityCode", securityCode);
-        String response = UtilHttp.doPost(url, headers, params, body);
-        JSONObject jsonObject = JSON.parseObject(response);
 
-        // 获取 subBankStatementList
-        JSONArray subBankStatementList1 = jsonObject
-                .getJSONObject("data")
-                .getJSONObject("subBankStatement")
-                .getJSONArray("subBankStatementList");
+//        Map params = new HashMap<>();
+        headers.put("securityCode", securityCode);
+
+        // 第一次调用:拆分对账单
+        String response;
+        try {
+            response = UtilHttp.doPost(url, headers, new HashMap<>(), requestBody);
+        } catch (Exception e) {
+            log.error("调用 splitBankStatement 接口失败", e);
+            return McR.errorParam("接口调用失败: splitBankStatement");
+        }
+
+        JSONObject jsonResponse;
+        try {
+            jsonResponse = JSON.parseObject(response);
+        } catch (Exception e) {
+            log.error("响应 JSON 解析失败: {}", response);
+            return McR.errorParam("响应格式错误");
+        }
+
+        // 检查响应是否成功(假设返回 { "code": 0, "data": { ... } })
+        Integer code = jsonResponse.getInteger("code");
+        if (code == null || code != 200) {
+            String msg = jsonResponse.getString("msg");
+            log.warn("splitBankStatement 接口调用失败,code: {}, msg: {}", code, msg);
+            return McR.errorParam("接口返回错误: " + msg);
+        }
+
+        JSONObject dataObj = jsonResponse.getJSONObject("data");
+        if (dataObj == null) {
+            log.warn("响应中缺少 data 字段: {}", response);
+            return McR.errorParam("响应数据缺失");
+        }
 
-        // 创建集合用于存储结果,每条记录是一个 Map
+        JSONObject subBankStatement = dataObj.getJSONObject("subBankStatement");
+        if (subBankStatement == null) {
+            log.warn("响应中缺少 subBankStatement 字段");
+            return McR.errorParam("拆分结果缺失");
+        }
+
+        JSONArray subBankStatementList1 = subBankStatement.getJSONArray("subBankStatementList");
+        if (subBankStatementList1 == null || subBankStatementList1.isEmpty()) {
+            log.info("拆分结果为空");
+            return McR.success("拆分成功,但无明细");
+        }
+
+        // 封装返回结果
         List<Map<String, Object>> result = new ArrayList<>();
-        // 遍历并封装到 Map 中
         for (int i = 0; i < subBankStatementList1.size(); i++) {
             JSONObject item = subBankStatementList1.getJSONObject(i);
             Map<String, Object> mapItem = new HashMap<>();
@@ -959,27 +1237,61 @@ public class LiLinServiceImpl implements LiLinService {
             mapItem.put("projectInfo", item.getString("projectInfo"));
             result.add(mapItem);
         }
-        for (int i = 0; i < result.size(); i++) {
-            Map<String, Object> stringObjectMap = result.get(i);
 
-            String url1 = "https://openapi.xencio.com/sandbox/cfa/api/sub/bs/updateProjectInfo";
+        int successCount = 0, failCount = 0;
+        String updateUrl = "https://openapi.xencio.com/sandbox/cfa/api/sub/bs/updateProjectInfo";
 
-            String response1 = UtilHttp.doPost(url1, headers, params, stringObjectMap);
+        for (Map<String, Object> item : result) {
+            try {
+                String updateResponse = UtilHttp.doPost(updateUrl, headers, new HashMap<>(), item);
+                // 可选:判断 updateResponse 是否成功
+                successCount++;
+            } catch (Exception e) {
+                log.error("更新 projectInfo 失败,id: {}", item.get("id"), e);
+                failCount++;
+            }
         }
-        // 返回结果
-        return McR.success(result); // 假设 McR.success 是一个返回成功响应的方法
+
+        log.info("拆分完成,共 {} 条,更新成功 {},失败 {}", result.size(), successCount, failCount);
+
+        // 返回结果(可附加统计)
+        return McR.success(result);
+    }
+
+    // 工具方法:安全获取字符串
+    private String getStringValue(Map data, String key) {
+        Object val = data.get(key);
+        return val == null ? "" : val.toString().trim();
     }
 
-    // 工具方法:从 Map 中安全地获取 BigDecimal 类型的金额
+    // 工具方法:安全获取金额(默认 0)
     private static BigDecimal getAmountFromMap(Map<String, Object> map, String key) {
         Object value = map.get(key);
-        if (value == null) return BigDecimal.ZERO;
+        if (value == null) {
+            log.debug("金额字段为空,key: {}", key);
+            return BigDecimal.ZERO;
+        }
+
         if (value instanceof Number) {
-            return BigDecimal.valueOf(((Number) value).doubleValue());
+            // 优先处理数值类型
+            try {
+                return BigDecimal.valueOf(((Number) value).doubleValue());
+            } catch (Exception e) {
+                log.warn("Number 转换为 BigDecimal 失败,key: {}, value: {}", key, value, e);
+                return BigDecimal.ZERO;
+            }
         }
+
+        // 尝试字符串解析
+        String strValue = value.toString().trim();
+        if (strValue.isEmpty()) {
+            return BigDecimal.ZERO;
+        }
+
         try {
-            return new BigDecimal(value.toString());
+            return new BigDecimal(strValue);
         } catch (NumberFormatException e) {
+            log.warn("金额字符串格式错误,key: {}, value: {}", key, value);
             return BigDecimal.ZERO;
         }
     }
@@ -1010,6 +1322,7 @@ public class LiLinServiceImpl implements LiLinService {
             return 0.0;
         }
     }
+
     private BigDecimal safeGetBigDecimal(Map formData, String key) {
         Object value = formData.get(key);
         if (value == null) return BigDecimal.ZERO;

+ 1 - 1
mjava-lilin/src/main/resources/application-dev.yml

@@ -5,7 +5,7 @@ server:
     context-path: /lilin
 
 enable:
-  scheduling: false
+  scheduling: true
 logging:
 #  config: classpath:logback-spring.xml
 #  path: /home/server/lianxiang/log/