3 Revīzijas bdb362d3d9 ... c3a9347a8d

Autors SHA1 Ziņojums Datums
  pruple_boy c3a9347a8d 结构化与需求调整 8 mēneši atpakaļ
  pruple_boy f729db7d3b Merge remote-tracking branch 'origin/master' 8 mēneši atpakaļ
  pruple_boy a548fe5840 结构化与需求调整 8 mēneši atpakaļ
19 mainītis faili ar 880 papildinājumiem un 302 dzēšanām
  1. 146 8
      mjava-aiwei/src/main/java/com/malk/aiwei/controller/TBxYDController.java
  2. 2 0
      mjava-aiwei/src/main/java/com/malk/aiwei/server/AWServer.java
  3. 1 1
      mjava-aiwei/src/main/java/com/malk/aiwei/service/AWClint.java
  4. 29 1
      mjava-aiwei/src/main/java/com/malk/aiwei/service/AWYDClient.java
  5. 0 229
      mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.bak
  6. 113 49
      mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.java
  7. 527 2
      mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWYDImplClient.java
  8. BIN
      mjava-aiwei/src/main/resources/templates/templates_checked_list.xlsx
  9. BIN
      mjava-aiwei/src/main/resources/templates/templates_project_requirements.xlsx
  10. BIN
      mjava-aiwei/src/main/resources/templates/templates_project_requirements_exception.xlsx
  11. BIN
      mjava-aiwei/src/main/resources/templates/templates_project_specification.xlsx
  12. BIN
      mjava-aiwei/src/main/resources/templates/templates_project_specification_exception.xlsx
  13. BIN
      mjava-aiwei/src/main/resources/templates/templates_xqwh.xlsx
  14. 25 7
      mjava-cloudpure/src/main/java/com/malk/cloudpure/controller/TLYController.java
  15. 19 2
      mjava-ruisi/src/main/java/com/malk/ruisi/controller/CosController.java
  16. 1 1
      mjava/src/main/java/com/malk/server/aliwork/YDConf.java
  17. 1 1
      mjava/src/main/java/com/malk/service/aliwork/YDService.java
  18. 6 0
      mjava/src/main/java/com/malk/utils/UtilList.java
  19. 10 1
      mjava/src/main/java/com/malk/utils/UtilMap.java

+ 146 - 8
mjava-aiwei/src/main/java/com/malk/aiwei/controller/TBxYDController.java

@@ -5,6 +5,7 @@ package com.malk.aiwei.controller;
  */
 
 import com.alibaba.fastjson.JSON;
+import com.malk.aiwei.server.AWServer;
 import com.malk.aiwei.service.AWClint;
 import com.malk.aiwei.service.AWYDClient;
 import com.malk.delegate.McDelegate;
@@ -26,10 +27,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 @Slf4j
 @RestController
@@ -300,12 +298,145 @@ public class TBxYDController {
         return UtilMap.map("options", result);
     }
 
+    /**
+     * 检查项导出, 全部检查项左关联已提交数据
+     */
+    @PostMapping("check-list/export")
+    void exportCheckList(@RequestBody Map data, HttpServletResponse response) {
+
+        log.info("检查项导出, {}", data);
+        McException.assertParamException_Null(data, "pCode", "proType");
+
+        List<Map> dataList = awClint.exportCheckList(UtilMap.getString(data, "pCode"), UtilMap.getString(data, "proType"));
+        UtilExcel.exportMapAndListByTemplate(response, dataList, Map.class, UtilMap.getString(data, "pCode") + " 技术检查项目", "templates_checked_list.xlsx");
+    }
+
     /************************************* 宜搭结构化 *************************************/
 
 
     @Autowired
     private AWYDClient awydClient;
 
+    /// 产品型号、产品版本、客户、专项需求 todo 非文本导出异常兼容, Can not find 'Converter' support class ArrayList.
+    private void _multiSelectCompatibility(List<Map> dataList) {
+        List<String> compIds = Arrays.asList("multiSelectField_lw678e43", "multiSelectField_m11u62on", "multiSelectField_m0afm8pc", "multiSelectField_lticsrqp");
+        for (Map map : dataList) {
+            for (String compId : compIds) {
+                if (compId.startsWith("multiSelectField_")) {
+                    map.put(compId, String.join(",", UtilMap.getList(map, compId)));
+                    continue;
+                }
+            }
+        }
+    }
+
+    /**
+     * 项目需求子表导出
+     */
+    @PostMapping("pro-req/export")
+    void exportProjectRequirements(@RequestBody Map data, HttpServletResponse response) {
+
+        log.info("项目需求子表导出, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "dataList", "fileName");
+
+        List<Map> dataList = UtilMap.getList(data, "dataList");
+        this._multiSelectCompatibility(dataList);
+        String fileName = UtilMap.getString(data, "fileName"); // 需求维护
+        UtilExcel.exportMapAndListByTemplate(response, dataList, Map.class, fileName, "templates_project_requirements.xlsx");
+    }
+
+    /**
+     * 项目需求子表导入
+     */
+    @PostMapping("pro-req/import")
+    McR importProjectRequirements(@RequestBody Map data, HttpServletResponse response) {
+
+        log.info("项目需求子表导入, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "charter", "dataList", "fileName");
+        Map result = awydClient.checkImportData_projectRequirements(UtilMap.getString(data, "charter"), UtilMap.getList(data, "dataList"), UtilMap.getBoolean(data, "isSubmit"));
+        List<Map> dataList = UtilMap.getList(result, "dataList");
+        if (UtilMap.getBoolean(result, "isSuccess")) {
+            return McR.success(dataList);
+        }
+        this._multiSelectCompatibility(dataList);
+        String fileName = UtilMap.getString(data, "fileName"); // 需求维护
+        UtilExcel.exportMapAndListByTemplate(response, dataList, Map.class, fileName, "templates_project_requirements_exception.xlsx");
+        return McR.errorAccess("数据校验失败");
+    }
+
+    /**
+     * 项目需求审批通过
+     */
+    @PostMapping("pro-req/approve")
+    McR approveProjectRequirements(HttpServletRequest request) {
+
+        Map<String, ?> data = UtilServlet.getParamMap(request);
+        log.info("项目需求审批通过, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "pCode", "instanceId", "userId");
+        awydClient.dealApprovedData_projectRequirements(UtilMap.getString(data, "pCode"), UtilMap.getString(data, "instanceId"), data);
+        return McR.success();
+    }
+
+    /**
+     * 覆盖项目需求档案需求列表 tmp
+     */
+    @PostMapping("pro-req/archive")
+    McR archiveProjectRequirements(HttpServletRequest request) {
+
+        Map<String, ?> data = UtilServlet.getParamMap(request);
+        log.info("覆盖项目需求档案需求列表, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "pCode", "instanceId");
+        awydClient.archiveProjectRequirements(UtilMap.getString(data, "pCode"), UtilMap.getString(data, "instanceId"));
+        return McR.success();
+    }
+
+    /**
+     * 项目规格子表导出
+     */
+    @PostMapping("pro-spec/export")
+    void exportSpecification(@RequestBody Map data, HttpServletResponse response) {
+
+        log.info("项目规格子表导出, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "dataList", "fileName");
+
+        List<Map> dataList = UtilMap.getList(data, "dataList");
+        this._multiSelectCompatibility(dataList);
+        String fileName = UtilMap.getString(data, "fileName"); // 规格维护
+        UtilExcel.exportMapAndListByTemplate(response, dataList, Map.class, fileName, "templates_project_specification.xlsx");
+    }
+
+    /**
+     * 项目规格子表导入
+     */
+    @PostMapping("pro-spec/import")
+    McR importProjectSpecification(@RequestBody Map data, HttpServletResponse response) {
+
+        log.info("项目规格子表导入, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "charter", "dataList", "fileName", "prList");
+        Map result = awydClient.checkImportData_projectSpecification(UtilMap.getString(data, "charter"), UtilMap.getList(data, "dataList"), UtilMap.getList(data, "prList"), UtilMap.getBoolean(data, "isSubmit"));
+        List<Map> dataList = UtilMap.getList(result, "dataList");
+        if (UtilMap.getBoolean(result, "isSuccess")) {
+            return McR.success(dataList);
+        }
+        this._multiSelectCompatibility(dataList);
+        String fileName = UtilMap.getString(data, "fileName"); // 需求维护
+        UtilExcel.exportMapAndListByTemplate(response, dataList, Map.class, fileName, "templates_project_specification_exception.xlsx");
+        return McR.errorAccess("数据校验失败");
+    }
+
+    /**
+     * 项规格审批通过
+     */
+    @PostMapping("pro-spec/approve")
+    McR approveProjectSpecification(HttpServletRequest request) {
+
+        Map<String, ?> data = UtilServlet.getParamMap(request);
+        log.info("项规格审批通过, {}", JSON.toJSONString(data));
+        McException.assertParamException_Null(data, "instanceId");
+        awydClient.dealApprovedData_projectSpecification(UtilMap.getString(data, "instanceId"), data);
+        return McR.success();
+    }
+
     /**
      * 提供verifier数据读取服务
      */
@@ -317,9 +448,8 @@ public class TBxYDController {
         return McR.success(dataList);
     }
 
-
     /**
-     * 导出
+     * 导出 [0909 作废版本1.0]
      */
     @PostMapping("export")
     void export(@RequestBody Map data, HttpServletResponse response) {
@@ -330,7 +460,7 @@ public class TBxYDController {
     }
 
     /**
-     * 导入前处理
+     * 导入前处理 [0909 作废版本1.0]
      */
     @PostMapping("beforeImportData")
     McR beforeImportData(@RequestBody Map data, HttpServletResponse response) {
@@ -388,8 +518,16 @@ public class TBxYDController {
 
         Map<String, ?> data = UtilServlet.getParamMap(request);
         log.info("test, {}", data);
+        
+        try {
+            Map body = TBConf.assembleCustomFieldName(AWServer.TASK_TRANSMIT, "已下达");
+            tbClient.updateTaskCustomField("659a681d44ade3345fdc0d39", tbConf.getOperatorId(), body);
+        } catch (McException e) {
+            log.error(e.getMessage(), e);
+        }
 
-        awClint.test();
+//        awClint.test();
+//        awydClient.test();
 
 //        ydClient.operateData(YDParam.builder()
 //                .formInstanceId("9d204da7-b01e-4bd9-a673-acb9b64b0e61")

+ 2 - 0
mjava-aiwei/src/main/java/com/malk/aiwei/server/AWServer.java

@@ -22,6 +22,8 @@ public class AWServer {
     public static final String TASK_PRODUCT_VERSION = "产品版本";
     public static final String TASK_WAFER = "wafer";
 
+    public static final String TASK_TRANSMIT = "下达状态";
+
     // prd 23.02.29 字段从 预检项 变更为 技术检查项
     public static final String TASK_CHECK_LINK = "技术检查项";
     public static final String TASK_CHECK_STATUS = "技术检查项检查状态";

+ 1 - 1
mjava-aiwei/src/main/java/com/malk/aiwei/service/AWClint.java

@@ -117,7 +117,7 @@ public interface AWClint {
     /**
      * 检查项导出, 全部检查项左关联已提交数据
      */
-    void exportCheckList(String pCode, String proType);
+    List<Map> exportCheckList(String pCode, String proType);
 
 
     void test();

+ 29 - 1
mjava-aiwei/src/main/java/com/malk/aiwei/service/AWYDClient.java

@@ -5,12 +5,40 @@ import java.util.Map;
 
 public interface AWYDClient {
 
+
     /**
      * 提供verifier数据读取服务
      */
     List<Map> syncVerifier(String projectCode);
 
-    /************************************* 0906 产品需求导入逻辑变革, 弃用保留 *************************************/
+    /**
+     * 项目需求导入校验
+     */
+    Map checkImportData_projectRequirements(String charter, List<Map> dataList, boolean isSubmit);
+
+    /**
+     * 项目需求审批通过后, 回写需求编号与关联表单, ppExt 并覆盖项目需求档案需求列表
+     */
+    void dealApprovedData_projectRequirements(String pCode, String instanceId, Map data);
+
+    /**
+     * tmp 覆盖项目需求档案需求列表, 兼容 dealApprovedData_projectRequirements
+     */
+    void archiveProjectRequirements(String pCode, String instanceId);
+
+    /**
+     * 项目规格导入校验
+     */
+    Map checkImportData_projectSpecification(String charter, List<Map> dataList, List<String> prList, boolean isSubmit);
+
+    /**
+     * 项目规格审批通过后, 回写规格编号与关联表单
+     */
+    void dealApprovedData_projectSpecification(String instanceId, Map data);
+
+    
+    void test();
 
+    // 0906 项目需求导入逻辑变更, 弃用保留
     Map checkImportData(Map data);
 }

+ 0 - 229
mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.bak

@@ -1,229 +0,0 @@
-public Map doCheck(String taskId, boolean isTask) {
-
-        log.info("检查项check, {},isTask:{}", taskId,isTask);
-        Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_CODE, AWServer.TASK_CHECK_LINK, AWServer.TASK_ROLE, AWServer.TASK_STAGE, AWServer.TASK_CHECK_STATUS, AWServer.TASK_PRODUCT, AWServer.TASK_PRODUCT_VERSION);
-        Map rTask = UtilMap.getMap(taskData, "task");
-        String pCode = UtilMap.getString(rTask, "projectId");
-        if (AWServer.PROJECT_IGNORE_ID.contains(pCode)) {
-            return UtilMap.map("result", "模板/复用项目");
-        }
-        List<Map> taskCheckLinkList=_getCustomFieldList(taskData, AWServer.TASK_CHECK_LINK);
-        Boolean nullFlag = taskCheckLinkList==null || taskCheckLinkList.size()<1;
-        String checkLinkTitle="",checkLinkUrl="";
-        if(!nullFlag){
-            Map taskCheckLink=taskCheckLinkList.get(0);
-            checkLinkTitle = UtilMap.getString(taskCheckLink,"title");
-            checkLinkUrl = JSONObject.parseObject(UtilMap.getString(taskCheckLink,"metaString")).getString("url");
-        }
-        /// prd 7.17 预检项任务ID与当前任务ID不匹配,判定为非法, 无需重新刷新
-        if (isTask && !nullFlag) {
-            if (!(checkLinkUrl.contains("?taskId=") && !checkLinkUrl.contains(taskId))) {
-                /// prd 预检项持续维护需要重新匹配, 任务号和主数据判定为非法, 无需重新刷新
-                if (AWServer.TASK_CHECK_LINK_OK.equals(checkLinkTitle)) {
-                    return UtilMap.map("result", "链接已加载");
-                }
-            }
-        }
-        // prd 非未完成任务若是未配置预检项状态, 忽略更新
-        if (isTask && !_getWorkFlowStatusList(pCode, AWServer.WORKFLOW_INITIAL).contains(rTask.get("tfsId"))) {
-            return UtilMap.map("result", "任务已执行");
-        }
-        // prd 7.17 creatorId 创建人修改为 执行人 executorId
-        String executorId = UtilMap.getString(rTask, "executorId");
-        String tCode = UtilMap.getString(taskData, AWServer.TASK_CODE);
-
-        String resultTitle = "",resultUrl = "";
-        List<Map> tList = null;
-        Map rProject = null;
-        if (StringUtils.isBlank(tCode)) {
-            resultTitle = "无任务编码";
-        } else {
-            List<Map> pList = ydService.queryDataList_FormData(_matchFormUuid("PROJECT"), UtilMap.map("textField_lqxtykce", pCode));
-            pList = pList.stream().filter(item -> pCode.equals(item.get("textField_lqxtykce"))).collect(Collectors.toList());
-            if (pList.size() == 0) {
-                resultTitle = "未匹配到项目主数据";
-            } else {
-                rProject = pList.get(0);
-                String proType = UtilMap.getString(rProject, "textField_ltwcq7s6");
-                tList = ydService.queryFormData_all(YDParam.builder()
-                        .formUuid(_matchFormUuid("CHECK"))
-                        .searchFieldJson(JSON.toJSONString(UtilMap.map("selectField_lrncf4hk, radioField_lrnddfq6", tCode, "启用")))
-                        .build());
-                // prd 多模板适配: [任务号 + 项目类型]
-                tList = tList.stream().filter(item -> tCode.equals(item.get("selectField_lrncf4hk")) && UtilMap.getList(item, "multiSelectField_ltwjre9s").contains(proType)).collect(Collectors.toList());
-                if (tList.size() == 0) {
-                    resultTitle = "未配置预检项";
-                } else {
-                    resultTitle = AWServer.TASK_CHECK_LINK_OK;
-                    resultUrl = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/workbench/" + _matchFormUuid("RECORD") + "?taskId=" + taskId;
-                }
-            }
-        }
-        log.info("检查项结果, {},{}", resultTitle,resultUrl);
-        if (isTask) {
-            if (!checkLinkTitle.equals(resultTitle)) {
-//                Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_LINK, result);
-//                tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
-                // prd 7.17 技术检查项从未匹配到更新,刷新检查状态为空
-                if (resultTitle.equals(AWServer.TASK_CHECK_LINK_OK) && (checkLinkTitle.equals("未配置预检项")||checkLinkTitle.equals(""))) {
-                    Map body2 = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS);
-                    tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body2);
-                }
-                _updateApproveField(taskId, new ArrayList<>(), resultUrl, resultTitle, "待提交", AWServer.TASK_CHECK_LINK);
-            }
-            // prd 未配置预检项更新为已检查, 避免完成任务完成触发必填校验 [ppExt 避免重复写入, 重复写入TB会重复记录日志
-            if (!resultTitle.equals(AWServer.TASK_CHECK_LINK_OK) && !taskData.get(AWServer.TASK_CHECK_STATUS).equals("已检查")) {
-                Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS, "已检查");
-                tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
-            }
-            return UtilMap.map("result", resultTitle.equals(AWServer.TASK_CHECK_LINK_OK)?resultUrl:resultTitle);
-        }
-        // prd 7.17 添加执行人为空校验
-        McException.assertAccessException(StringUtils.isBlank(executorId), "执行人不能为空!");
-        McException.assertAccessException(!resultTitle.equals("技术检查确认"), resultTitle);
-
-        Map formData = UtilMap.map("selectField_lqxuswzd, textField_lrndwu00, textField_lr3dlwsa, textField_lr3er4qb", tCode, pCode, taskId, executorId);
-        // 项目信息
-        formData.putAll(UtilMap.map("selectField_lqxuswze, textField_lrndwu07, textField_lqxuc9m4", UtilMap.getString(rProject, "textField_lrj7vnxb"), UtilMap.getString(rProject, "textareaField_lrj7vnxl"), UtilMap.getString(rProject, "textField_lqxtykcd")));
-        // 任务信息 [TBManager 操作]
-        Map<String, String> extra = (Map) tbClient.idMapQuery(executorId, "dingTalk-user", ddConf.getCorpId()).get(0).get("extra");
-        // ppExt: TB映射钉钉信息仅返回 userId, 人员组件返回前端展示需要 name. 通过页面公式临时自动加载, 可通过TB接口或钉钉用户接口查询
-        formData.putAll(UtilMap.map("textField_lrndwu09, textField_lrndwu0a, textField_lrndwu0b, employeeField_lrndwu0e, textField_lvbrueqs, textField_lvbrueqt", rTask.get("content"), taskData.get(AWServer.TASK_STAGE), taskData.get(AWServer.TASK_ROLE), Arrays.asList(UtilMap.map("value, name", extra.get("userId"), extra.get("userId"))), taskData.get(AWServer.TASK_PRODUCT), taskData.get(AWServer.TASK_PRODUCT_VERSION)));
-        // 返回检查项
-        formData.put("tableField_lqxxgj4s", tList.stream().map(item -> {
-            item.put("associationFormField_lrrnem5r", YDConf.associationForm(ydConf.getAppType(), _matchFormUuid("CHECK"), UtilMap.getString(item, "instanceId"), UtilMap.getString(item, "textField_lrnd3h0r"), "", false));
-            return item;
-        }).collect(Collectors.toList()));
-        formData.put("userId",extra.get("userId"));// prd 7.17 技术检查项需要匹配userId
-        return formData;
-    }
-
-
-    public void checked(Map data) {
-
-            log.info("检查项回调, {}", data);
-            String title = UtilMap.getString(data, "title");
-            Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS, "已检查");
-            tbClient.updateTaskCustomField(String.valueOf(data.get("taskId")), tbConf.getOperatorId(), body);
-            String result = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/formDetail?formInstId=" + data.get("formInstId");
-            Map body2 = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_LINK, result);
-            tbClient.updateTaskCustomField(String.valueOf(data.get("taskId")), tbConf.getOperatorId(), body2);
-            _updateApproveField(UtilMap.getString(data, "taskId"), new ArrayList<>(), result, title, "已提交", AWServer.TASK_CHECK_LINK);
-        }
-
-
-
-
-
-
-
-public Map doCheck(String taskId, boolean isTask) {
-
-        log.info("检查项check {}, isTask {}", taskId, isTask);
-        Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_CODE, AWServer.TASK_CHECK_LINK, AWServer.TASK_ROLE, AWServer.TASK_STAGE, AWServer.TASK_CHECK_STATUS, AWServer.TASK_PRODUCT, AWServer.TASK_PRODUCT_VERSION);
-        Map rTask = UtilMap.getMap(taskData, "task");
-        String pCode = UtilMap.getString(rTask, "projectId");
-        if (AWServer.PROJECT_IGNORE_ID.contains(pCode)) {
-            return UtilMap.map("result", "模板/复用项目");
-        }
-        String checkLink = UtilMap.getString(taskData, AWServer.TASK_CHECK_LINK);
-        /// prd 7.17 预检项任务ID与当前任务ID不匹配,判定为非法, 无需重新刷新
-        if (isTask) {
-            if (!(checkLink.contains("?taskId=") && !checkLink.contains(taskId))) {
-                /// prd 预检项持续维护需要重新匹配, 任务号和主数据判定为非法, 无需重新刷新
-                if (StringUtils.isNotBlank(checkLink) && !AWServer.TASK_CHECK_LINK_OK.equals(checkLink)) {
-                    return UtilMap.map("result", "链接已加载");
-                }
-            }
-        }
-        // prd 非未完成任务若是未配置预检项状态, 忽略更新
-        if (isTask && !_getWorkFlowStatusList(pCode, AWServer.WORKFLOW_INITIAL).contains(rTask.get("tfsId"))) {
-            return UtilMap.map("result", "任务已执行");
-        }
-        // prd 7.17 creatorId 创建人修改为 执行人 executorId
-        String executorId = UtilMap.getString(rTask, "executorId");
-        String tCode = UtilMap.getString(taskData, AWServer.TASK_CODE);
-
-        String resultTitle = "", resultUrl = "";
-        List<Map> tList = null;
-        Map rProject = null;
-        if (StringUtils.isBlank(tCode)) {
-            resultTitle = "无任务编码";
-        } else {
-            List<Map> pList = ydService.queryDataList_FormData(_matchFormUuid("PROJECT"), UtilMap.map("textField_lqxtykce", pCode));
-            pList = pList.stream().filter(item -> pCode.equals(item.get("textField_lqxtykce"))).collect(Collectors.toList());
-            if (pList.size() == 0) {
-                resultTitle = "未匹配到项目主数据";
-            } else {
-                rProject = pList.get(0);
-                String proType = UtilMap.getString(rProject, "textField_ltwcq7s6");
-                tList = ydService.queryFormData_all(YDParam.builder()
-                        .formUuid(_matchFormUuid("CHECK"))
-                        .searchFieldJson(JSON.toJSONString(UtilMap.map("selectField_lrncf4hk, radioField_lrnddfq6", tCode, "启用")))
-                        .build());
-                // prd 多模板适配: [任务号 + 项目类型]
-                tList = tList.stream().filter(item -> tCode.equals(item.get("selectField_lrncf4hk")) && UtilMap.getList(item, "multiSelectField_ltwjre9s").contains(proType)).collect(Collectors.toList());
-                if (tList.size() == 0) {
-                    resultTitle = "未配置预检项";
-                } else {
-                    resultTitle = AWServer.TASK_CHECK_LINK_OK;
-                    resultUrl = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/workbench/" + _matchFormUuid("RECORD") + "?taskId=" + taskId;
-                }
-            }
-        }
-        log.info("检查项结果, {},{}", resultTitle, resultUrl);
-        String result = resultTitle.equals(AWServer.TASK_CHECK_LINK_OK) ? resultUrl : resultTitle;
-        if (isTask) {
-            if (!result.equals(checkLink)) {
-                Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_LINK, result);
-                tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
-                // prd 7.17 技术检查项从未匹配到更新,刷新检查状态为空
-                if (resultTitle.equals(AWServer.TASK_CHECK_LINK_OK) && (checkLink.equals("未配置预检项") || checkLink.equals(""))) {
-                    Map body2 = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS);
-                    tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body2);
-                }
-//                _updateApproveField(taskId, new ArrayList<>(), resultUrl, resultTitle, "待提交", AWServer.TASK_CHECK_LINK);
-            }
-            // prd 未配置预检项更新为已检查, 避免完成任务完成触发必填校验 [ppExt 避免重复写入, 重复写入TB会重复记录日志
-            if (!resultTitle.equals(AWServer.TASK_CHECK_LINK_OK) && !taskData.get(AWServer.TASK_CHECK_STATUS).equals("已检查")) {
-                Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS, "已检查");
-                tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
-            }
-            return UtilMap.map("result", result);
-        }
-        // prd 7.17 添加执行人为空校验
-        McException.assertAccessException(StringUtils.isBlank(executorId), "执行人不能为空!");
-        McException.assertAccessException(!resultTitle.equals("技术检查确认"), resultTitle);
-
-        Map formData = UtilMap.map("selectField_lqxuswzd, textField_lrndwu00, textField_lr3dlwsa, textField_lr3er4qb", tCode, pCode, taskId, executorId);
-        // 项目信息
-        formData.putAll(UtilMap.map("selectField_lqxuswze, textField_lrndwu07, textField_lqxuc9m4", UtilMap.getString(rProject, "textField_lrj7vnxb"), UtilMap.getString(rProject, "textareaField_lrj7vnxl"), UtilMap.getString(rProject, "textField_lqxtykcd")));
-        // 任务信息 [TBManager 操作]
-        Map<String, String> extra = (Map) tbClient.idMapQuery(executorId, "dingTalk-user", ddConf.getCorpId()).get(0).get("extra");
-        // ppExt: TB映射钉钉信息仅返回 userId, 人员组件返回前端展示需要 name. 通过页面公式临时自动加载, 可通过TB接口或钉钉用户接口查询
-        formData.putAll(UtilMap.map("textField_lrndwu09, textField_lrndwu0a, textField_lrndwu0b, employeeField_lrndwu0e, textField_lvbrueqs, textField_lvbrueqt", rTask.get("content"), taskData.get(AWServer.TASK_STAGE), taskData.get(AWServer.TASK_ROLE), Arrays.asList(UtilMap.map("value, name", extra.get("userId"), extra.get("userId"))), taskData.get(AWServer.TASK_PRODUCT), taskData.get(AWServer.TASK_PRODUCT_VERSION)));
-        // 返回检查项
-        formData.put("tableField_lqxxgj4s", tList.stream().map(item -> {
-            item.put("associationFormField_lrrnem5r", YDConf.associationForm(ydConf.getAppType(), _matchFormUuid("CHECK"), UtilMap.getString(item, "instanceId"), UtilMap.getString(item, "textField_lrnd3h0r"), "", false));
-            return item;
-        }).collect(Collectors.toList()));
-        // prd 7.17 技术检查项需要匹配userId
-        formData.put("userId", extra.get("userId"));
-        return formData;
-    }
-
-    /**
-     * 检查项回调
-     */
-    @Override
-    public void checked(Map data) {
-
-        log.info("检查项回调, {}", data);
-        String title = UtilMap.getString(data, "title");
-        Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_STATUS, "已检查");
-        tbClient.updateTaskCustomField(String.valueOf(data.get("taskId")), tbConf.getOperatorId(), body);
-        String result = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/formDetail?formInstId=" + data.get("formInstId");
-        Map body2 = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_LINK, result);
-        tbClient.updateTaskCustomField(String.valueOf(data.get("taskId")), tbConf.getOperatorId(), body2);
-//        _updateApproveField(UtilMap.getString(data, "taskId"), new ArrayList<>(), result, title, "已提交", AWServer.TASK_CHECK_LINK);
-    }

+ 113 - 49
mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.java

@@ -392,6 +392,12 @@ public class AWImplClient implements AWClint {
         return workFlowStatusList.stream().map(item -> UtilMap.getString(item, "id")).collect(Collectors.toList());
     }
 
+    // 通过id获取工作流名称
+    private String _getWorkFlowStatusName(String projectId, String tfsId) {
+        List<Map> customFlowStatus = tbClient.queryProjectCustomFlowStatus(projectId, UtilMap.map("tfsIds", tfsId));
+        return UtilMap.getString(customFlowStatus.get(0), "name");
+    }
+
     /**
      * 获取知识库附件传递到宜搭
      * -
@@ -485,11 +491,11 @@ public class AWImplClient implements AWClint {
                 tList = tList.stream().filter(item -> tCode.equals(item.get("selectField_lrncf4hk")) && UtilMap.getList(item, "multiSelectField_ltwjre9s").contains(proType)).collect(Collectors.toList());
                 /// prd 8.21 RTO任务关联技术检查项自动化处理
                 if (tCode.contains("RTO-")) {
-                    // 按照技术领域区分, 非必填
+                    // 按照技术领域区分, prd 0910 不改为多选逻辑, 需要查询全量数据匹配, 通过多条查询合并更可控
                     List<Map> rtoList = ydService.queryDataList_FormData(_matchFormUuid("RTO"), UtilMap.map("selectField_lrncf4hk", tCode));
                     rtoList = rtoList.stream().filter(item -> tCode.equals(item.get("selectField_lrncf4hk"))).collect(Collectors.toList());
-                    if (rtoList.size() == 1) {
-                        Map searchMap = UtilMap.map("radioField_lrnddfq6, textField_8n5ib3y", "启用", UtilMap.getString(rtoList.get(0), "textField_8n5ib3y"));
+                    for (Map rto : rtoList) {
+                        Map searchMap = UtilMap.map("radioField_lrnddfq6, textField_8n5ib3y", "启用", UtilMap.getString(rto, "selectField_m0wbhut4"));
                         List<Map> rtoCheck = ydService.queryFormData_all(YDParam.builder()
                                 .formUuid(_matchFormUuid("CHECK"))
                                 .searchFieldJson(JSON.toJSONString(searchMap))
@@ -541,7 +547,9 @@ public class AWImplClient implements AWClint {
         // 任务信息 [TBManager 操作]
         Map<String, String> extra = (Map) tbClient.idMapQuery(executorId, "dingTalk-user", ddConf.getCorpId()).get(0).get("extra");
         // ppExt: TB映射钉钉信息仅返回 userId, 人员组件返回前端展示需要 name. 通过页面公式临时自动加载, 可通过TB接口或钉钉用户接口查询
-        formData.putAll(UtilMap.map("textField_lrndwu09, textField_lrndwu0a, textField_lrndwu0b, employeeField_lrndwu0e, textField_lvbrueqs, textField_lvbrueqt", rTask.get("content"), taskData.get(AWServer.TASK_STAGE), taskData.get(AWServer.TASK_ROLE), Arrays.asList(UtilMap.map("value, name", extra.get("userId"), extra.get("userId"))), taskData.get(AWServer.TASK_PRODUCT), taskData.get(AWServer.TASK_PRODUCT_VERSION)));
+        formData.putAll(UtilMap.map("textField_lrndwu09, textField_lrndwu0a, textField_lrndwu0b, employeeField_lrndwu0e, textField_lvbrueqs, textField_lvbrueqt, textField_m0yo7tsj", rTask.get("content"), taskData.get(AWServer.TASK_STAGE), taskData.get(AWServer.TASK_ROLE), Arrays.asList(UtilMap.map("value, name", extra.get("userId"), extra.get("userId"))), taskData.get(AWServer.TASK_PRODUCT), taskData.get(AWServer.TASK_PRODUCT_VERSION), taskData.get(AWServer.TASK_WAFER)));
+        // prd 9.12 任务状态, 存量数据已更新, 新增数据自动带出任务状态 [ NA的任务也关联到, 新版本技术检查项查询全量数据, 已更新. fixme NA 任务也需要提交检查项, 否则不能关联 ]
+        formData.put("textField_m0yo7tsk", _getWorkFlowStatusName(UtilMap.getString(rTask, "projectId"), UtilMap.getString(rTask, "tfsId")));
         // 返回检查项
         formData.put("tableField_lqxxgj4s", tList.stream().map(item -> {
             item.put("associationFormField_lrrnem5r", YDConf.associationForm(ydConf.getAppType(), _matchFormUuid("CHECK"), UtilMap.getString(item, "instanceId"), UtilMap.getString(item, "textField_lrnd3h0r"), "", false));
@@ -549,9 +557,6 @@ public class AWImplClient implements AWClint {
         }).collect(Collectors.toList()));
         // prd 7.17 技术检查项需要匹配userId
         formData.put("userId", extra.get("userId"));
-        // prd 9.12 添加任务状态字段
-        formData.put("textField_m0yo7tsj",taskData.get(AWServer.TASK_WAFER));
-        formData.put("textField_m0yo7tsk",_getWorkFlowStatusName(UtilMap.getString(rTask,"projectId"),UtilMap.getString(rTask,"tfsId"))); // 任务状态
         return formData;
     }
 
@@ -880,6 +885,14 @@ public class AWImplClient implements AWClint {
                 if (!workFlowStatusList.contains(task.get("tfsId"))) {
                     continue;
                 }
+
+                // prd 0920 下发不阻断,没有执行人也更新,已下达 [ 存量任务兼容未添加字段 ]
+                try {
+                    Map body = TBConf.assembleCustomFieldName(AWServer.TASK_TRANSMIT, "已下达");
+                    tbClient.updateTaskCustomField(UtilMap.getString(task, "id"), tbConf.getOperatorId(), body);
+                } catch (McException e) {
+                    log.error(e.getMessage(), e);
+                }
                 // 获取资源名称对应的项目角色
                 Optional optional = customField.stream().filter(item -> AWServer.TASK_ROLE.equals(item.get("name"))).findAny();
                 if (!optional.isPresent()) {
@@ -988,11 +1001,6 @@ public class AWImplClient implements AWClint {
         });
     }
 
-    private String _getWorkFlowStatusName(String projectId,String tfsId) {
-        List<Map> customFlowStatus = tbClient.queryProjectCustomFlowStatus(projectId, UtilMap.map("tfsIds",tfsId));
-        return UtilMap.getString(customFlowStatus.get(0),"name");
-    }
-
     // TB与宜搭userId转换 fixme 提取 tbService [ ppExt: 若TB未启用人员返回为空 ]
     private String _convertUserId(String userId, boolean isTBID) {
         List<Map> tbMap = tbClient.idMapQuery(userId, isTBID);
@@ -1051,6 +1059,15 @@ public class AWImplClient implements AWClint {
         List<Map> taskList = tbClient.queryProjectTaskList(projectId, UtilMap.map("q", tql), null);
         log.info("项目计划: {}, {}", C_TR5_03_13, taskList.size());
 
+        List<Map> customFlowStatus = tbClient.queryProjectCustomFlowStatus(projectId, null);
+        List<String> nastatus = new ArrayList<>();
+        for (Map flowStatus : customFlowStatus) {
+            if ("NA".equals(flowStatus.get("name").toString()) || "N/A".equals(flowStatus.get("name").toString())) {
+                nastatus.add((String) flowStatus.get("id"));
+            }
+        }
+        log.info("nastatus, {}", nastatus);
+
         List<Map> dataList = new ArrayList<>();
         for (Map task : taskList) {
             String name = _getTaskFieldMap(task, customField, AWServer.TASK_PRODUCT).get(AWServer.TASK_PRODUCT);
@@ -1058,6 +1075,12 @@ public class AWImplClient implements AWClint {
             if (dataList.stream().filter(item -> name.equals(item.get("Name"))).findAny().isPresent()) {
                 continue;
             }
+            // 任务状态为NA不取数据
+            String tfsId = (String) task.get("tfsId");
+            if (nastatus.contains(tfsId)) {
+                log.info("任务状态为NA,跳过!, {}", tfsId);
+                continue;
+            }
             Map tData = UtilMap.map("Name", name);
             // 2. 预计α推广 (AlphaDate): 最新项目计划(非基线)里的ADCPα任务对应的计划完成时间 / TR5-03-13任务号 取最新版本
             String date = UtilMap.getString(task, "dueDate");
@@ -1067,14 +1090,14 @@ public class AWImplClient implements AWClint {
                     tData.put("AlphaDate", UtilDateTime.formatDate(tDate));
                 }
             }
-            // 3. α推广 (AlphaDate2): 【ADCPα】对应的实际完成时间 / TR5-03-13任务号 取最新版本
-            date = UtilMap.getString(task, "accomplishTime");
-            if (UtilString.isNotBlankCompatNull(date)) {
-                Date tDate = UtilDateTime.parse(date, UtilDateTime.DATE_MSEL_ISO);
-                if (tDate.after(dlDate)) {
-                    tData.put("AlphaDate2", UtilDateTime.formatDate(tDate));
-                }
-            }
+            // 3. α推广 (AlphaDate2): 【ADCPα】对应的实际完成时间 / TR5-03-13任务号 取最新版本 [ prd 2024-09-10实际完成时间不传 ]
+//            date = UtilMap.getString(task, "accomplishTime");
+//            if (UtilString.isNotBlankCompatNull(date)) {
+//                Date tDate = UtilDateTime.parse(date, UtilDateTime.DATE_MSEL_ISO);
+//                if (tDate.after(dlDate)) {
+//                    tData.put("AlphaDate2", UtilDateTime.formatDate(tDate));
+//                }
+//            }
             // 1. α推广基线 (AlphaBaseline): PDCP basseline 基线下的ADCPα任务的计划完成时间 / PDCP basseline 基线 TR5-03-13任务号
             Optional optional = baseLineTaskList.stream().filter(item -> task.get("id").equals(item.get("id"))).findAny();
             if (optional.isPresent()) {
@@ -1103,6 +1126,14 @@ public class AWImplClient implements AWClint {
             if (dataList.stream().filter(item -> name.equals(item.get("Name"))).findAny().isPresent()) {
                 continue;
             }
+            log.info("任务tfsId: {}", task.get("tfsId"));
+            String tfsId = (String) task.get("tfsId");
+
+            //任务状态为NA不取数据
+            if (nastatus.contains(tfsId)) {
+                log.info("任务状态为NA,跳过!");
+                continue;
+            }
             Map tData = UtilMap.map("Name", name);
             // 5. 预计正式发布 (AgentSampleDate): 最新项目计划(非基线),取最早那个产品版本的计划完成时间 / TR6-01-11任务号 最早版本
             String date = UtilMap.getString(task, "dueDate");
@@ -1112,14 +1143,14 @@ public class AWImplClient implements AWClint {
                     tData.put("AgentSampleDate", UtilDateTime.formatDate(tDate));
                 }
             }
-            // 6. 正式发布 (ProductSendDate): 【ADCP】对应的取最早那个产品版本的实际完成时间 / TR6-01-11任务号 最早版本
-            date = UtilMap.getString(task, "accomplishTime");
-            if (UtilString.isNotBlankCompatNull(date)) {
-                Date tDate = UtilDateTime.parse(date, UtilDateTime.DATE_MSEL_ISO);
-                if (tDate.after(dlDate)) {
-                    tData.put("ProductSendDate", UtilDateTime.formatDate(tDate));
-                }
-            }
+            // 6. 正式发布 (ProductSendDate): 【ADCP】对应的取最早那个产品版本的实际完成时间 / TR6-01-11任务号 最早版本 [ prd 2024-09-10实际完成时间不传 ]
+//            date = UtilMap.getString(task, "accomplishTime");
+//            if (UtilString.isNotBlankCompatNull(date)) {
+//                Date tDate = UtilDateTime.parse(date, UtilDateTime.DATE_MSEL_ISO);
+//                if (tDate.after(dlDate)) {
+//                    tData.put("ProductSendDate", UtilDateTime.formatDate(tDate));
+//                }
+//            }
             // 4. 正式发布基线 (ReleaseBaseline): 取PDCPbasseline基线里的ADCP的计划完成时间,取最早那个产品版本的计划完成时间 / PDCP basseline 基线 TR6-01-11任务号
             Optional optional = baseLineTaskList.stream().filter(item -> task.get("id").equals(item.get("id"))).findAny();
             if (optional.isPresent()) {
@@ -1163,18 +1194,31 @@ public class AWImplClient implements AWClint {
         // prd 同步CRM: 项目状态为执行 & TB项目号不为空
         YDParam ydParam = YDParam.builder()
                 .formUuid(_matchFormUuid("PROJECT"))
-                .searchFieldJson(JSON.toJSONString(UtilMap.map("textField_llouhiyf", "执行")))
+                .searchFieldJson(JSON.toJSONString(UtilMap.map("textField_ltsdsti7", "执行")))
                 .build();
         List<Map> proList = ydService.queryFormData_all(ydParam);
+        log.info("推送总计数据数量:" + proList.size());
+        int num = 0;
         for (Map pro : proList) {
             String projectId = UtilMap.getString(pro, "textField_lqxtykce");
             if (StringUtils.isBlank(projectId)) {
                 continue;
             }
-            syncBaseLineForCRM(projectId);
+            num++;
+            try {
+                syncBaseLineForCRM(projectId);
+            } catch (Exception e) {
+
+                log.error("项目推送异常:{}", e.getMessage());
+                log.info("项目推送异常:{}", projectId);
+
+            }
         }
+        log.info("推送成功总计数据数量:" + num);
+
     }
 
+
     // 获取项目自定义字段ID, 格式 { 名称: id } fixme 提取
     Map<String, String> getProjectCFID(String projectId, List<String> names) {
         List<Map> customField = tbClient.queryProjectCustomField(projectId, null);
@@ -1251,6 +1295,7 @@ public class AWImplClient implements AWClint {
             Map formData = UtilMap.map(associationCompId, YDConf.associationForm(srcParam.getAppType(), srcParam.getFormUuid(), UtilMap.getString(item, "formInstanceId"), UtilMap.getString(item, codeCompId), "", false));
             formData.put("radioField_lrnddfq6", "启用");
             formData.put("selectField_lrnd3h0s", checkType);
+            formData.put("textField_lrrnqz82", UtilMap.getString(item, "formInstanceId"));
             for (String compId : compIds.keySet()) {
                 formData.put(compId, item.get(compIds.get(compId)));
             }
@@ -1321,7 +1366,7 @@ public class AWImplClient implements AWClint {
                         .formUuid("FORM-5436DC75BC8347D498DFF2617925BA70O066")
                         .build();
                 // 任务号, 描述, ID(标题), 验收标准, 等级, 任务名称, 适用项目类型
-                Map<String, ?> compIds3 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r, textField_cd4f65l, textField_267kho2, textField_ltzl9mpc, multiSelectField_ltwjre9s", "selectField_4gwjfa5, textField_lryetn3g, textField_9x1yps6, textField_cd4f65l, textField_267kho2, textField_ls9xvxvk, multiSelectField_ltwjre9s");
+                Map<String, ?> compIds3 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r, textField_cd4f65l, textField_267kho2, textField_ltzl9mpc, multiSelectField_ltwjre9s, textField_rfwx8lk", "selectField_4gwjfa5, textField_lryetn3g, textField_9x1yps6, textField_cd4f65l, textField_267kho2, textField_ls9xvxvk, multiSelectField_ltwjre9s, textField_lryi6ba6");
                 syncCheckList(ydParam3, compIds3, "selectField_4gwjfa5", "textField_9x1yps6", "TR评审要素表", "associationFormField_lrrn5csa");
                 break;
         }
@@ -1818,41 +1863,59 @@ public class AWImplClient implements AWClint {
     /**
      * 检查项导出, 全部检查项左关联已提交数据 [ ppExt 已提交数据作为查询条件, 只能筛选已提交数据, 因此提供全量导出 ]
      */
+    @Synchronized
     @Override
-    public void exportCheckList(String pCode, String proType) {
+    public List<Map> exportCheckList(String pCode, String proType) {
         List<Map> checkedList = _getCheckedList(pCode);
-
-
         // 查询全部检查项提交数据
         List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
                 .formUuid(_matchFormUuid("CHECK"))
-                .searchFieldJson(JSON.toJSONString(UtilMap.map("multiSelectField_ltwjre9s, radioField_lrnddfq6", Arrays.asList(proType), "启用")))
+                //.searchFieldJson(JSON.toJSONString(UtilMap.map("radioField_lrnddfq6", Arrays.asList(proType), "启用")))
+                .searchFieldJson(JSON.toJSONString(UtilMap.empty()))
                 .dynamicOrder(JSON.toJSONString(UtilMap.map("selectField_lrncf4hk", "+")))
                 .build());
-        // 匹配已提交检查项生数据
         List<Map> unionList = new ArrayList<>();
+        // prd RTO 除自身任务号关联任务外, 还会通过规则匹配 IC 技术评审表, 但任务都是重复因此无需左关联, 匹配已提交数据即可
+        checkedList.stream().filter(item -> UtilMap.getString(item, "selectField_lqxuswzd").contains("RTO-")).forEach(item -> {
+            // RTO- 前缀任务编号会通过左关联匹配, 此处只处理非 RTO 任务且已提交数据
+            Optional optional = dataList.stream().filter(check -> {
+                Map unique = (Map) UtilMap.getList(item, "associationFormField_lrrnem5r").get(0);
+                return !UtilMap.getString(check, "selectField_lrncf4hk").contains("RTO-") && check.get("textField_lrnd3h0r").equals(unique.get("title"));
+            }).findAny();
+            if (optional.isPresent()) {
+                Map check = (Map) optional.get();
+                check.put("no", unionList.size() + 1);
+                item.putAll(check);
+                unionList.add(item);
+            }
+        });
+        // 匹配已提交检查项生数据
         for (Map check : dataList) {
             // 任务编号一致, 检查项唯一标识一致
             List<Map> match = checkedList.stream().filter(item ->
             {
-                Map unique = (Map) UtilMap.getList(UtilMap.getMap(item, "main"), "associationFormField_lrrnem5r_id").get(0);
-                return item.get("selectField_lqxuswzd").equals(check.get("selectField_lrncf4hk")) && check.get("textField_lrnd3h0r").equals(unique.get("title"));
+                // ppExt 可能中途有换任务号, 通过实例ID匹配 [ 历史数据存在关联表单标题为任务编号, 非唯一标题 ]
+                Map unique = (Map) UtilMap.getList(item, "associationFormField_lrrnem5r").get(0);
+                return check.get("instanceId").equals(unique.get("instanceId"));
             }).collect(Collectors.toList());
             // 多条重复提交如何处理
             if (match.size() > 0) {
                 // match 匹配已提交件数据, match.main 为已提交数据主表内容
                 match.forEach(item -> {
+                    check.put("no", unionList.size() + 1);
                     check.put("match", item);
+                    check.putAll(item); // 全量, 用于导出
                     unionList.add(check);
                 });
             } else {
+                check.put("no", unionList.size() + 1);
                 unionList.add(check);
             }
         }
-        log.info("检查项数据: {}", unionList.size());
+        log.info("检查项数据: check = {}, checked = {}, union = {}", dataList.size(), checkedList.size(), unionList.size());
+        return unionList;
     }
 
-
     /// 查询项目已提交全部检查项数据
     List<Map> _getCheckedList(String pCode) {
         List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
@@ -1871,17 +1934,18 @@ public class AWImplClient implements AWClint {
                         .formInstanceId(UtilMap.getString(record, "instanceId"))
                         .tableFieldId("tableField_lqxxgj4s")
                         .build());
-                // 过滤空数据, 无检查项目关联数据
-                details = details.stream().filter(item -> ObjectUtil.isNotNull(item) && StringUtils.isNotBlank(UtilMap.getString(item, "associationFormField_lrrnem5r_id")))
-                        .map(item -> {
-                            // 合并主笔数据
-                            item.put("main", record);
-                            item.put("associationFormField_lrrnem5r", JSON.parse(String.valueOf(JSON.parse(UtilMap.getString(item, "associationFormField_lrrnem5r_id")))));
-                            return item;
-                        }).collect(Collectors.toList());
             }
-            checkedList.addAll(details);
+            // 过滤空数据, 无检查项目关联数据
+            details = details.stream().filter(item -> ObjectUtil.isNotNull(item) && StringUtils.isNotBlank(UtilMap.getString(item, "associationFormField_lrrnem5r_id")))
+                    .map(item -> {
+                        // 合并主表笔数据
+                        item.put("main", record);
+                        item.putAll(record); // 全量, 用于导出
+                        item.put("associationFormField_lrrnem5r", JSON.parse(String.valueOf(JSON.parse(UtilMap.getString(item, "associationFormField_lrrnem5r_id")))));
+                        return item;
+                    }).collect(Collectors.toList());
             log.info("检查项清单, {}", details.size());
+            checkedList.addAll(details);
         }
         return checkedList;
     }

+ 527 - 2
mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWYDImplClient.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.malk.aiwei.service.AWYDClient;
 import com.malk.server.aliwork.YDConf;
 import com.malk.server.aliwork.YDParam;
+import com.malk.server.dingtalk.DDR_New;
 import com.malk.service.aliwork.YDClient;
 import com.malk.service.aliwork.YDService;
 import com.malk.utils.UtilMap;
@@ -16,6 +17,7 @@ import org.springframework.stereotype.Service;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -45,9 +47,532 @@ public class AWYDImplClient implements AWYDClient {
         }).collect(Collectors.toList());
     }
 
+    /// 产品需求
+    String appType_pr = "APP_YQY0OH7953OKBTM57PLL";
+    String systemToken_pr = "PGC66MB1H2RLXMFQ7XKW4BC3BDFJ2UDM909XL18";
+
+    /**
+     * 项目需求导入校验 [0910 更新版本]
+     */
+    @Override
+    public Map checkImportData_projectRequirements(String charter, List<Map> dataList, boolean isSubmit) {
+
+        /// fixme prd 0913 Charter子类不作为匹配条件, 可能存在跨情况. 引用\覆盖核心参数覆盖, 其余作为bom属性. 通过charter则全量载入
+        boolean isSuccess = true;
+        List<String> prList = new ArrayList<>();
+
+        // 提交前再次校验,兼容手动新增情况【手动新增只能选择需求库】
+        if (isSubmit) {
+            for (Map row : dataList) {
+                String code = UtilMap.getString(row, "textField_m0vvv72j");
+                if (StringUtils.isNotBlank(code) && prList.contains(code)) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", code + "需求在当前列表已存在, 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+                prList.add(code);
+            }
+            return UtilMap.map("isSuccess, dataList", isSuccess, dataList);
+        }
+
+        // ppExt 可能存在同时新增一二三级需求, 按照层级导入, 已处理替换新增引用需求编号, 再恢复排序  [ prd 保留原导入排序 ]
+        int index = 0;
+        for (Map row : dataList) {
+            row.put("order", index); // sort 内不仅仅循环
+            index++;
+        }
+        dataList.sort(Comparator.comparingInt(item -> Integer.valueOf(UtilMap.getString(item, "selectField_lr4y2xcj").replace("级", ""))));
+
+        // ‒ 关闭需求入库入口,仅需求编号、名称、描述、大类、子类不可修改,作为唯一条件,其他信息可维护
+        Map condition = UtilMap.map("selectField_lronu2g3, selectField_lvy6fgqe, textField_lt2d2x7b, textareaField_lt2d2x7c", "需求大类不能为空", "需求子类不能为空", "需求名称不能为空", "需求描述不能为空");
+        for (Map row : dataList) {
+            // 清理导入状态标志
+            row.put("selectField_m10k093c", "");
+            row.put("textareaField_m10k093a", "");
+            // 效验需求层级 & 父需求编号逻辑
+            if (UtilMap.isBlankString(row, "selectField_lr4y2xcj")) {
+                row.put("selectField_m10k093c", "失败");
+                row.put("textareaField_m10k093a", "需求层级不能为空, 请检查数据后重新导入");
+                isSuccess = false;
+                continue;
+            }
+            // prd: 0914 需求层级关系不判断, 如1级需求是否有父编号, 3级需求是不是挂到了一级. 后续树形展开时兼容处理
+            String level = UtilMap.getString(row, "selectField_lr4y2xcj");
+            if (!"1级".equals(level)) {
+                if (UtilMap.isBlankString(row, "textField_m0afm8pd")) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", level + "需求对应父需求编号不能为空, 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+                if (UtilMap.getString(row, "textField_m0vvv72j").equals(UtilMap.getString(row, "textField_m0afm8pd"))) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", level + "需求父编号引用了当前编号, 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+                // ppExt 当前导入列表必须存在对应的父需求编号, 且唯一性. [ fixme 已过滤掉引用自身情况, 且添加提交时校验需求编号不能重复, 9.19 按照层级导入, 无需记录原始编号, 已处理替换新增引用需求编号 ]
+                List<Map> mList = dataList.stream().filter(item -> row.get("textField_m0afm8pd").equals(item.get("textField_m0vvv72j"))).collect(Collectors.toList());
+                if (mList.size() != 1) {
+                    String desc = mList.size() == 0 ? "未匹配到" : "不唯一";
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", level + "需求对应父需求编号" + desc + ", 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+            }
+            // 根据需求编号判断需求名称与描述以及大类是否一致,如果是非一级需求需要增加校验父需求编号是否一致
+            String code = UtilMap.getString(row, "textField_m0vvv72j");
+            if (StringUtils.isNotBlank(code)) {
+
+                // 重复需求编号校验
+                if (prList.contains(code)) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", code + "需求在当前列表已存在, 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+                prList.add(code);
+
+                List<Map> searchCondition = Arrays.asList(YDConf.searchCondition_TextFiled("serialNumberField_lr4xs4zi", code, "eq"));
+                List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                        .appType(appType_pr)
+                        .systemToken(systemToken_pr)
+                        .formUuid("FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO")
+                        .searchCondition(JSON.toJSONString(searchCondition))
+                        .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+                if (tList.size() > 0) {
+                    Map formData = UtilMap.getMap(tList.get(0), "formData");
+                    // 校验父需求编号是否一致
+                    if (!UtilMap.getString(formData, "textField_m0afm8pd").equals(UtilMap.getString(row, "textField_m0afm8pd"))) {
+                        row.put("selectField_m10k093c", "失败");
+                        row.put("textareaField_m10k093a", "父需求编号与求库内编号" + formData.get("textField_m0afm8pd") + "不一致, 请检查数据后重新导入");
+                        isSuccess = false;
+                        continue;
+                    }
+                    row.put("textField_m0vvv72j", formData.get("serialNumberField_lr4xs4zi")); // 赋值需求编号
+                    // 匹配需求编号, 条件内数据以原始数据覆盖导入数据
+                    for (Object key : condition.keySet()) {
+                        row.put(key, UtilMap.getString(formData, String.valueOf(key)));
+                    }
+                    row.put("associationFormField_lvy7yjq8", YDConf.associationForm(appType_pr, "FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO", UtilMap.getString(tList.get(0), "formInstanceId"), code, "", false));
+                    row.put("selectField_m10k093c", "引用");
+                    row.put("textareaField_m10k093a", "已匹配需求编号, 条件内数据以原始数据覆盖导入数据");
+                    row.put("attachmentField_lticyied", UtilMap.getString(formData, "attachmentField_lticyied")); // 保留附件
+                    continue;
+                }
+            }
+            // 控制匹配条件 [ prd: 同时兼容, 有编号未匹配到数据, 通过填写内容识别 ppExt: 编号不清空, 可能存在同时新增一二三级需求 ]
+            for (Object prop : condition.keySet()) {
+                if (UtilMap.isBlankString(row, String.valueOf(prop))) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", condition.get(prop) + ", 请检查数据后重新导入");
+                    break;
+                }
+            }
+            if (UtilMap.isNotBlankString(row, "textareaField_m10k093a")) {
+                isSuccess = false;
+                continue;
+            }
+            // 若有,执行替换逻辑, 备注体现原始被替换编号
+            List<Map> searchCondition = new ArrayList<>();
+            for (Object key : condition.keySet()) {
+                searchCondition.add(YDConf.searchCondition_TextFiled(String.valueOf(key), row.get(key), "eq"));
+            }
+            List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formUuid("FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO")
+                    .searchCondition(JSON.toJSONString(searchCondition))
+                    .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+            if (tList.size() > 0) {
+                Map formData = UtilMap.getMap(tList.get(0), "formData");
+                String rCode = UtilMap.getString(formData, "serialNumberField_lr4xs4zi");
+                // 替换新增引用需求编号
+                dataList.stream().filter(item -> StringUtils.isNotBlank(code) &&
+                        !"1级".equals(UtilMap.getString(item, "selectField_lr4y2xcj")) &&
+                        row.get("textField_m0vvv72j").equals(item.get("textField_m0afm8pd"))).forEach(item -> item.put("textField_m0afm8pd", rCode));
+                row.put("selectField_m10k093c", "替换");
+                row.put("textareaField_m10k093a", code + "需求已替换, " + UtilMap.getString(formData, "serialNumberField_lr4xs4zi") + "需求库中相同条件需求");
+                row.put("associationFormField_lvy7yjq8", YDConf.associationForm(appType_pr, "FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO", UtilMap.getString(tList.get(0), "formInstanceId"), rCode, "", false));
+                row.put("textField_m0afm8pd", UtilMap.getString(formData, "textField_m0afm8pd")); // 替换父需求编号
+                row.put("attachmentField_lticyied", UtilMap.getString(formData, "attachmentField_lticyied")); // 保留附件
+                row.put("textField_m0vvv72j", rCode);
+
+            } else {
+                row.put("selectField_m10k093c", "新增");
+                row.put("textareaField_m10k093a", "需求编号不存在, 评审通过后需求库新增");
+            }
+        }
+        dataList.sort(Comparator.comparingInt(item -> UtilMap.getInt(item, "order"))); // 恢复原排序
+        return UtilMap.map("isSuccess, dataList", isSuccess, dataList);
+    }
+
+    /**
+     * 项目需求审批通过后, 回写需求编号与关联表单, ppExt 并覆盖项目需求档案需求列表
+     */
+    @Override
+    public void dealApprovedData_projectRequirements(String pCode, String instanceId, Map data) {
+
+        // 项目需求数据
+        DDR_New ddr_new = ydClient.queryData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formInstanceId(instanceId)
+                .build(), YDConf.FORM_QUERY.retrieve_id);
+        Map formData = ddr_new.getFormData();
+
+        List<Map> details = (List<Map>) formData.get("tableField_lt2d2x78");
+        if (details.size() == YDConf.PAGE_SIZE_DETAILS) {
+            details = ydService.queryDetails(YDParam.builder()
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formUuid("FORM-2326752634AA463EAA8AE81DD8E19E88AJ6K")
+                    .formInstanceId(ddr_new.getFormInstId())
+                    .tableFieldId("tableField_lt2d2x78")
+                    .build());
+        }
+        // ppExt 编号不清空, 可能存在同时新增一二三级需求. 提取新增需求, 排序优先写入一级需求 [ prd 保留原导入排序 ]
+        List<Map> ttList = new ArrayList<>();
+        AtomicInteger index = new AtomicInteger();
+        details = details.stream().filter((item) ->
+        {
+            item.put("sCode", UtilMap.getString(item, "textField_m0vvv72j"));
+            item.put("order", index.intValue()); // 避免对象引用, 保留值
+            index.getAndIncrement();
+            boolean isCreate = "新增".equals(item.get("selectField_m10k093c"));
+            if (UtilMap.isNotBlankString(item, "attachmentField_lticyied")) {
+                item.put("attachmentField_lticyied", JSONObject.parse(UtilMap.getString(item, "attachmentField_lticyied")));
+            }
+            if (!isCreate) {
+                item.put("associationFormField_lvy7yjq8", YDConf.associationForm(UtilMap.getString(item, "associationFormField_lvy7yjq8_id")));
+                ttList.add(item);
+            }
+            return isCreate;
+        }).collect(Collectors.toList());
+        details.sort(Comparator.comparingInt(item -> Integer.valueOf(UtilMap.getString(item, "selectField_lr4y2xcj").replace("级", ""))));
+
+        for (Map detail : details) {
+            // 主表信息需完善
+            detail.putAll(UtilMap.map("textField_lth2h04b, textField_lypq3ees, textField_lypq3eet", "charter, pdt, mpdt", data));
+            detail.putAll(UtilMap.map("radioField_lroozhse, dateField_ly5ewjnf, employeeField_ly5ewjnc", "启用", new Date().getTime(), Arrays.asList(data.get("userId"))));
+            String formInstId = (String) ydClient.operateData(YDParam.builder()
+                    .formUuid("FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO")
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formDataJson(JSONObject.toJSONString(detail)) // 子表组件ID与档案一致
+                    .build(), YDConf.FORM_OPERATION.create);
+            // 查询新增数据后, 需求编号
+            Map crData = ydClient.queryData(YDParam.builder()
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formInstanceId(formInstId)
+                    .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+            String rCode = UtilMap.getString(crData, "serialNumberField_lr4xs4zi");
+            // 若是引用, 导入已匹配父需求编号可过滤 [ 已按照层级导入, 更新当前父需求编号即可 ]
+            if (!"1级".equals(UtilMap.getString(detail, "selectField_lr4y2xcj"))) {
+                Optional optional = details.stream().filter(item -> item.get("sCode").equals(detail.get("textField_m0afm8pd"))).findAny();
+                if (optional.isPresent()) {
+                    detail.put("textField_m0afm8pd", ((Map) optional.get()).get("textField_m0vvv72j"));
+                }
+            }
+            detail.put("associationFormField_lvy7yjq8", YDConf.associationForm(appType_pr, "FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO", formInstId, rCode, "", false));
+            detail.put("textField_m0vvv72j", rCode);
+            detail.put("selectField_m10k093c", "已新增");  // 兼容重复调用
+            ttList.add(detail);
+        }
+        ttList.sort(Comparator.comparingInt(item -> UtilMap.getInt(item, "order"))); // 恢复原排序
+
+        ydClient.operateData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formInstanceId(instanceId)
+                .updateFormDataJson(JSONObject.toJSONString(UtilMap.map("tableField_lt2d2x78", ttList)))
+                .build(), YDConf.FORM_OPERATION.update);
+
+        // 覆盖项目需求档案需求列表 [最新版本]
+        List<Map> searchCondition = Arrays.asList(YDConf.searchCondition_TextFiled("textField_lt6xd8lm", pCode, "eq"));
+        List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formUuid("FORM-480F7D71D1F44B9D92946BE95C1F2656JKAU")
+                .searchCondition(JSON.toJSONString(searchCondition))
+                .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+        if (tList.size() > 0) {
+            ydClient.operateData(YDParam.builder()
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formInstanceId(UtilMap.getString(tList.get(0), "formInstanceId"))
+                    .updateFormDataJson(JSONObject.toJSONString(UtilMap.map("tableField_lt2d2x78, radioField_m11lu528", ttList, "是")))
+                    .build(), YDConf.FORM_OPERATION.update);
+        }
+    }
+
+    /**
+     * 覆盖项目需求档案需求列表 tmp
+     */
+    @Override
+    public void archiveProjectRequirements(String pCode, String instanceId) {
+
+        // 项目需求数据
+        DDR_New ddr_new = ydClient.queryData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formInstanceId(instanceId)
+                .build(), YDConf.FORM_QUERY.retrieve_id);
+        Map formData = ddr_new.getFormData();
+
+        List<Map> details = (List<Map>) formData.get("tableField_lt2d2x78");
+        if (details.size() == YDConf.PAGE_SIZE_DETAILS) {
+            details = ydService.queryDetails(YDParam.builder()
+                    .appType(appType_pr)
+                    .systemToken(systemToken_pr)
+                    .formUuid("FORM-2326752634AA463EAA8AE81DD8E19E88AJ6K")
+                    .formInstanceId(ddr_new.getFormInstId())
+                    .tableFieldId("tableField_lt2d2x78")
+                    .build());
+        }
+        details = details.stream().map((item) ->
+        {
+            if (UtilMap.isNotBlankString(item, "attachmentField_lticyied")) {
+                item.put("attachmentField_lticyied", JSONObject.parse(UtilMap.getString(item, "attachmentField_lticyied")));
+            }
+            item.put("associationFormField_lvy7yjq8", YDConf.associationForm(UtilMap.getString(item, "associationFormField_lvy7yjq8_id")));
+            return item;
+        }).collect(Collectors.toList());
+
+        // 覆盖项目需求档案需求列表 [最新版本]
+        List<Map> searchCondition = Arrays.asList(YDConf.searchCondition_TextFiled("textField_lt6xd8lm", pCode, "eq"));
+        List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formUuid("FORM-480F7D71D1F44B9D92946BE95C1F2656JKAU")
+                .searchCondition(JSON.toJSONString(searchCondition))
+                .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+        ydClient.operateData(YDParam.builder()
+                .appType(appType_pr)
+                .systemToken(systemToken_pr)
+                .formInstanceId(UtilMap.getString(tList.get(0), "formInstanceId"))
+                .updateFormDataJson(JSONObject.toJSONString(UtilMap.map("tableField_lt2d2x78, radioField_m11lu528", details, "是")))
+                .build(), YDConf.FORM_OPERATION.update);
+    }
+
+    /// 产品规格
+//    String appType_ps = "APP_SQB02DVSNJJJ9XE2XEW4";
+//    String systemToken_ps = "Q9966X618IQLEQ166YMEWC3DJYU9310KY1AXLUL";
+
+    String appType_ps = "APP_DINTFSNSIJPX533M3CQK";
+    String systemToken_ps = "PTA66DA1S5QO5MDPBPN8V6CS4Q0O2LYISD91M024";
+
+    /**
+     * 项目规格导入校验 [0919 更新版本] fixme 复制\简化无层级项目需求实现逻辑
+     */
+    @Override
+    public Map checkImportData_projectSpecification(String charter, List<Map> dataList, List<String> prList, boolean isSubmit) {
+
+        boolean isSuccess = true;
+        List<String> psList = new ArrayList<>();
+
+        // ‒ 匹配条件,仅规格编号、分类、类型、子类、描述、条件不可修改,作为唯一条件,其他信息作为bom属性可维护
+        Map condition = UtilMap.map("selectField_lti2e638, textField_lt2vdhtd, textField_lt2vdhte, textField_lt2vdhtf, textField_lt2vdhtg", "规格分类不能为空", "规格类型不能为空", "规格子类不能为空", "规格描述不能为空", "规格条件不能为空");
+        for (Map row : dataList) {
+            // 清理导入状态标志
+            row.put("selectField_m10k093c", "");
+            row.put("textareaField_m10k093a", "");
+
+            // ppExt 需求库校验逻辑: 1. 需求编号不能为空且必须在需求列表中, 2.需求相关数据不修改, 只引用规格内容 [解决前端关联表单导出再导入后丢失问题]
+            if (!prList.contains(UtilMap.getString(row, "textField_m0vvv72j"))) {
+                row.put("selectField_m10k093c", "失败");
+                row.put("textareaField_m10k093a", UtilMap.getString(row, "textField_m0vvv72j") + "需求在列表中不存在, 请检查数据后重新导入");
+                isSuccess = false;
+                continue;
+            }
+            String code = UtilMap.getString(row, "textField_m18x4yqo");
+            if (StringUtils.isNotBlank(code)) {
+                // 重复需求+规格编号校验
+                String uCode = UtilMap.getString(row, "textField_m0vvv72j") + "-" + UtilMap.getString(row, "textField_m18x4yqo");
+                if (psList.contains(uCode)) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", uCode + "需求+规格在当前列表已存在, 请检查数据后重新导入");
+                    isSuccess = false;
+                    continue;
+                }
+                psList.add(uCode);
+
+                if (!isSubmit) {
+                    List<Map> searchCondition = Arrays.asList(YDConf.searchCondition_TextFiled("serialNumberField_lti2e639", code, "eq"));
+                    List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                            .appType(appType_ps)
+                            .systemToken(systemToken_ps)
+//                            .formUuid("FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY")
+                            .formUuid("FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J")
+                            .searchCondition(JSON.toJSONString(searchCondition))
+                            .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+                    if (tList.size() > 0) {
+                        Map formData = UtilMap.getMap(tList.get(0), "formData");
+                        row.put("serialNumberField_lti2e639", formData.get("serialNumberField_lti2e639")); // 赋值规格编号
+                        // 匹配规格编号, 条件内数据以原始数据覆盖导入数据
+                        for (Object key : condition.keySet()) {
+                            row.put(key, UtilMap.getString(formData, String.valueOf(key)));
+                        }
+//                        row.put("associationFormField_m18x4yqq", YDConf.associationForm(appType_pr, "FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY", UtilMap.getString(tList.get(0), "formInstanceId"), code, "", false));
+                        row.put("associationFormField_m18x4yqq", YDConf.associationForm(appType_pr, "FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J", UtilMap.getString(tList.get(0), "formInstanceId"), code, "", false));
+                        row.put("selectField_m10k093c", "引用");
+                        row.put("textareaField_m10k093a", "已匹配规格编号, 条件内数据以原始数据覆盖导入数据");
+                        continue;
+                    }
+                }
+            }
+            // 控制匹配条件 [ prd: 同时兼容, 有编号未匹配到数据, 通过填写内容识别 ]
+            for (Object prop : condition.keySet()) {
+                if (UtilMap.isBlankString(row, String.valueOf(prop))) {
+                    row.put("selectField_m10k093c", "失败");
+                    row.put("textareaField_m10k093a", condition.get(prop) + ", 请检查数据后重新导入");
+                    break;
+                }
+            }
+            if (UtilMap.isNotBlankString(row, "textareaField_m10k093a")) {
+                isSuccess = false;
+                continue;
+            }
+            if (!isSubmit) {
+                // 若有,执行替换逻辑, 备注体现原始被替换编号
+                List<Map> searchCondition = new ArrayList<>();
+                for (Object key : condition.keySet()) {
+                    searchCondition.add(YDConf.searchCondition_TextFiled(String.valueOf(key), row.get(key), "eq"));
+                }
+                List<Map> tList = (List<Map>) ydClient.queryData(YDParam.builder()
+                        .appType(appType_ps)
+                        .systemToken(systemToken_ps)
+//                        .formUuid("FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY")
+                        .formUuid("FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J")
+                        .searchCondition(JSON.toJSONString(searchCondition))
+                        .build(), YDConf.FORM_QUERY.retrieve_list).getData();
+                if (tList.size() > 0) {
+                    Map formData = UtilMap.getMap(tList.get(0), "formData");
+                    String rCode = UtilMap.getString(formData, "serialNumberField_lti2e639");
+                    row.put("selectField_m10k093c", "替换");
+                    row.put("textareaField_m10k093a", code + "规格已替换, " + UtilMap.getString(formData, "serialNumberField_lti2e639") + "规格库中相同条件规格");
+//                    row.put("associationFormField_m18x4yqq", YDConf.associationForm(appType_pr, "FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY", UtilMap.getString(tList.get(0), "formInstanceId"), rCode, "", false));
+                    row.put("associationFormField_m18x4yqq", YDConf.associationForm(appType_pr, "FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J", UtilMap.getString(tList.get(0), "formInstanceId"), rCode, "", false));
+                    row.put("textField_m18x4yqo", rCode);
+                } else {
+                    row.put("selectField_m10k093c", "新增");
+                    row.put("textareaField_m10k093a", "规格编号不存在, 评审通过后规格库新增");
+                }
+            }
+        }
+        return UtilMap.map("isSuccess, dataList", isSuccess, dataList);
+    }
+
+    /**
+     * 项目规格审批通过后, 回写规格编号与关联表单
+     */
+    @Override
+    public void dealApprovedData_projectSpecification(String instanceId, Map data) {
+        // 项目需求数据
+        DDR_New ddr_new = ydClient.queryData(YDParam.builder()
+                .appType(appType_ps)
+                .systemToken(systemToken_ps)
+                .formInstanceId(instanceId)
+                .build(), YDConf.FORM_QUERY.retrieve_id);
+        Map formData = ddr_new.getFormData();
+        List<Map> details = (List<Map>) formData.get("tableField_lt2d2x78");
+        if (details.size() == YDConf.PAGE_SIZE_DETAILS) {
+            details = ydService.queryDetails(YDParam.builder()
+                    .appType(appType_ps)
+                    .systemToken(systemToken_ps)
+//                    .formUuid("FORM-2C5D914DC3974745AA6F55CB916E82FEH5UA")
+                    .formUuid("FORM-A1809FBB82E54CB387B372E3B15457E7IOVV")
+                    .formInstanceId(ddr_new.getFormInstId())
+                    .tableFieldId("tableField_lt2d2x78")
+                    .build());
+        }
+
+        boolean isUpdate = false;
+        for (Map detail : details) {
+            if (UtilMap.isNotBlankString(detail, "attachmentField_lticyied")) {
+                detail.put("attachmentField_lticyied", JSONObject.parse(UtilMap.getString(detail, "attachmentField_lticyied")));
+            }
+            detail.put("associationFormField_lvy7yjq8", YDConf.associationForm(UtilMap.getString(detail, "associationFormField_lvy7yjq8_id")));
+
+            boolean isCreate = "新增".equals(detail.get("selectField_m10k093c"));
+            if (!isCreate) {
+                detail.put("associationFormField_m18x4yqq", YDConf.associationForm(UtilMap.getString(detail, "associationFormField_m18x4yqq_id")));
+                continue;
+            }
+            // 主表信息需完善
+            detail.putAll(UtilMap.map("selectField_lwq7bv4z, selectField_lwq7bv51", "charter, pdt", data));
+            detail.put("radioField_lti2e636", "启用");
+            String formInstId = (String) ydClient.operateData(YDParam.builder()
+//                    .formUuid("FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY")
+                    .formUuid("FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J")
+                    .appType(appType_ps)
+                    .systemToken(systemToken_ps)
+                    .formDataJson(JSONObject.toJSONString(detail)) // 子表组件ID与档案一致
+                    .build(), YDConf.FORM_OPERATION.create);
+            // 查询新增数据后, 规格编号
+            Map crData = ydClient.queryData(YDParam.builder()
+                    .appType(appType_ps)
+                    .systemToken(systemToken_ps)
+                    .formInstanceId(formInstId)
+                    .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
+            String rCode = UtilMap.getString(crData, "serialNumberField_lti2e639");
+//            detail.put("associationFormField_lvy7yjq8", YDConf.associationForm(appType_ps, "FORM-C1BB004F74C1492DB3F568EA8A7A951C7NBY", formInstId, rCode, "", false));
+            detail.put("associationFormField_m18x4yqq", YDConf.associationForm(appType_ps, "FORM-F5FD52E311514AC890D4B308CFA8E8A8BH8J", formInstId, rCode, "", false));
+            detail.put("textField_m18x4yqo", rCode);
+            detail.put("selectField_m10k093c", "已新增");  // 兼容重复调用
+            isUpdate = true;
+        }
+        if (isUpdate) {
+            ydClient.operateData(YDParam.builder()
+                    .appType(appType_ps)
+                    .systemToken(systemToken_ps)
+                    .formInstanceId(instanceId)
+                    .updateFormDataJson(JSONObject.toJSONString(UtilMap.map("tableField_lt2d2x78", details)))
+                    .build(), YDConf.FORM_OPERATION.update);
+        }
+    }
+
+
+    @Override
+    public void test() {
+
+//        this._test0912();
+
+        List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
+                .appType(appType_ps)
+                .systemToken(systemToken_ps)
+                .formUuid("FORM-2C5D914DC3974745AA6F55CB916E82FEH5UA")
+                .build());
+        log.info("dataList, {}", JSON.toJSONString(dataList));
+    }
+
+    // 刷新排序号, 并更新数据版本
+    private void _test0912() {
+        String appType = "APP_YQY0OH7953OKBTM57PLL";
+        String systemToken = "PGC66MB1H2RLXMFQ7XKW4BC3BDFJ2UDM909XL18";
+
+        List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
+                .appType(appType)
+                .systemToken(systemToken)
+                .formUuid("FORM-7A52930D0E834522AD65A4CFE2C0818F1KQO")
+                .build());
+
+        dataList.forEach(item -> {
+            ydClient.operateData(YDParam.builder()
+                    .appType(appType)
+                    .systemToken(systemToken)
+                    .formInstanceId(String.valueOf(item.get("formInstanceId")))
+                    .updateFormDataJson(JSON.toJSONString(UtilMap.empty()))
+                    .useLatestVersion(true)
+                    .build(), YDConf.FORM_OPERATION.update);
+        });
+    }
 
     /************************************* 0906 产品需求导入逻辑变革, 弃用保留 *************************************/
-    
+
     @Override
     public Map checkImportData(Map data) {
         List<Map> dataList = UtilMap.getList(data, "dataList");
@@ -105,7 +630,7 @@ public class AWYDImplClient implements AWYDClient {
                     }
                 }
                 if (fno == no) {
-                    errmsg = errmsg + "需求编号父需求编号不能相同;";
+                    errmsg = errmsg + "需求编号父需求编号不能相同;";
                 }
                 dataList.get(i).put("selectField_lr4y2xcj", cj + "级");
                 if (String.valueOf(item.get("multiSelectField_lw678e43")).length() > 0) {

BIN
mjava-aiwei/src/main/resources/templates/templates_checked_list.xlsx


BIN
mjava-aiwei/src/main/resources/templates/templates_project_requirements.xlsx


BIN
mjava-aiwei/src/main/resources/templates/templates_project_requirements_exception.xlsx


BIN
mjava-aiwei/src/main/resources/templates/templates_project_specification.xlsx


BIN
mjava-aiwei/src/main/resources/templates/templates_project_specification_exception.xlsx


BIN
mjava-aiwei/src/main/resources/templates/templates_xqwh.xlsx


+ 25 - 7
mjava-cloudpure/src/main/java/com/malk/cloudpure/controller/TLYController.java

@@ -5,6 +5,7 @@ import com.malk.server.aliwork.YDConf;
 import com.malk.server.aliwork.YDParam;
 import com.malk.server.common.McR;
 import com.malk.service.aliwork.YDClient;
+import com.malk.service.aliwork.YDService;
 import com.malk.service.dingtalk.DDClient;
 import com.malk.service.dingtalk.DDClient_Workflow;
 import com.malk.utils.UtilDateTime;
@@ -147,7 +148,7 @@ public class TLYController {
         formData.put("textField_lygvvyda", tasks.get(0).get("userId"));
         formData.put("textField_lyh4y3th", processInstanceId);
         formData.put("selectField_lyo1uao4", "否"); // 出差是否报销, 否
-        formData.put("selectField_lyo1zprd", ""); // 同步存量数据, 否
+        formData.put("selectField_lyo1zprd", ""); // 同步存量数据, 否
 
         log.info("审批数据, {}", JSON.toJSONString(formData));
         ydClient.operateData(YDParam.builder()
@@ -182,18 +183,35 @@ public class TLYController {
         return McR.success();
     }
 
+
+    @Autowired
+    private YDService ydService;
+
     @GetMapping("test")
     McR test() {
 
 
-        long beginTime = UtilDateTime.parseDateTime("2024-06-01 00:00:00").getTime();
-        long finishTime = UtilDateTime.parseDateTime("2024-07-17 00:00:00").getTime();
-
-        Map extInfo = UtilMap.map("statuses", Arrays.asList("COMPLETED"));
-        List<String> dataList = ddClient_workflow.getInstanceIds_all(ddClient.getAccessToken(), "PROC-6E40CB5E-F2AE-4CE6-9864-F5C3848D0C7E", beginTime, finishTime, extInfo);
+//        long beginTime = UtilDateTime.parseDateTime("2024-06-01 00:00:00").getTime();
+//        long finishTime = UtilDateTime.parseDateTime("2024-07-17 00:00:00").getTime();
+//
+//        Map extInfo = UtilMap.map("statuses", Arrays.asList("COMPLETED"));
+//        List<String> dataList = ddClient_workflow.getInstanceIds_all(ddClient.getAccessToken(), "PROC-6E40CB5E-F2AE-4CE6-9864-F5C3848D0C7E", beginTime, finishTime, extInfo);
 
 
-        log.info("xxxx, {}", dataList.size());
+        List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
+                .formUuid("FORM-210DA087671044F8A5CD72F0E9E89060SZ8Q")
+                .systemToken(SYSTRM_TOKEN)
+                .appType(APP_TYPE)
+                .build());
+        for (Map map : dataList) {
+
+            if (UtilMap.isBlankString(map, "selectField_lyo1uao4")) {
+                ydClient.operateData(YDParam.builder()
+                        .formInstanceId(UtilMap.getString(map, "instanceId"))
+                        .updateFormDataJson(JSON.toJSONString(UtilMap.map("selectField_lyo1uao4, selectField_lyo1zprd", "否", "否")))
+                        .build(), YDConf.FORM_OPERATION.update);
+            }
+        }
         return McR.success();
     }
 }

+ 19 - 2
mjava-ruisi/src/main/java/com/malk/ruisi/controller/CosController.java

@@ -4,17 +4,24 @@ package com.malk.ruisi.controller;
  * 错误抛出与拦截详见 CatchException
  */
 
+import com.alibaba.fastjson.JSONObject;
 import com.malk.ruisi.service.RSService;
+import com.malk.server.aliwork.YDConf;
+import com.malk.server.aliwork.YDParam;
 import com.malk.server.common.McException;
 import com.malk.server.common.McR;
+import com.malk.service.aliwork.YDClient;
+import com.malk.utils.UtilMap;
 import com.malk.utils.UtilServlet;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
 import java.util.Map;
 
 @Slf4j
@@ -30,7 +37,7 @@ public class CosController {
      */
     @PostMapping("user/sync")
     McR syncUser() {
-        
+
         log.info("syncUser");
         rsService.syncDingTalk_exclusive();
         return McR.success();
@@ -50,9 +57,19 @@ public class CosController {
         return McR.success();
     }
 
-    @PostMapping("test")
+
+    @Autowired
+    private YDClient ydClient;
+
+    @GetMapping("test")
     McR test() {
 
+        ydClient.operateData(YDParam.builder()
+                .formInstanceId("cc8b3153-3950-4fa5-b089-b28d1025ea7e")
+                .updateFormDataJson(JSONObject.toJSONString(UtilMap.map("departmentSelectField_lys0k6qb", Arrays.asList("155254332"))))
+                .useLatestVersion(true)
+                .build(), YDConf.FORM_OPERATION.update);
+
         return McR.success();
     }
 }

+ 1 - 1
mjava/src/main/java/com/malk/server/aliwork/YDConf.java

@@ -111,7 +111,7 @@ public class YDConf {
      * 若是同一个应用,appType与formUuid为空(不指定下formUuid可为错误,但appType只能为空),点击页面链接会自动匹配;
      * 若是跨应用实例,则不能通过导入,需要通过接口指定appType
      */
-    public List<Map> associationForm(String associations) {
+    public static List<Map> associationForm(String associations) {
         if (UtilString.isBlankCompatNull(associations)) {
             return new ArrayList<>();
         }

+ 1 - 1
mjava/src/main/java/com/malk/service/aliwork/YDService.java

@@ -24,7 +24,7 @@ public interface YDService {
     Object operateData2(Map data, Map update, YDParam param, YDConf.FORM_OPERATION type);
 
     /**
-     * 查询数据 [子表] todo 参数控制
+     * 查询数据 [子表] todo 参数控制, todo 提取合并到 queryAll, 带出子表全部数据
      * <p>
      * .formUuid("FORM-TD966Z81I9ODTCY66GH345S03VW03JJF6EQLL5")
      * .formInstanceId(data.get("formInstanceId").toString())

+ 6 - 0
mjava/src/main/java/com/malk/utils/UtilList.java

@@ -96,4 +96,10 @@ public abstract class UtilList {
         }
         return false;
     }
+
+    // 判断集合中是否存在重复对象
+    public static boolean isDistinct(List<Map> dataList, String prop) {
+        List dList = dataList.stream().map(item -> item.get(prop)).collect(Collectors.toList());
+        return dList.stream().distinct().count() != dList.size();
+    }
 }

+ 10 - 1
mjava/src/main/java/com/malk/utils/UtilMap.java

@@ -64,6 +64,8 @@ public abstract class UtilMap {
         return map;
     }
 
+    // todo 字段Equal, 添加首个参数判空
+
     /**
      * 快速创建map [保留skeys, 取值ckeys] todo: 新版本更新, 字段覆盖
      */
@@ -309,7 +311,10 @@ public abstract class UtilMap {
      */
     public static List getList(Map data, String key) {
         if (data.containsKey(key)) {
-            return (List) data.get(key);
+            Object val = data.get(key);
+            if (val instanceof List) {
+                return (List) val;
+            }
         }
         return new ArrayList();
     }
@@ -319,6 +324,10 @@ public abstract class UtilMap {
      */
     public static Map getMap(Map data, String key) {
         if (data.containsKey(key)) {
+            Object val = data.get(key);
+            if (val instanceof Map) {
+                return (Map) val;
+            }
             return (Map) data.get(key);
         }
         return new HashedMap();