Browse Source

艾为更新

pruple_boy 1 year ago
parent
commit
fddfe9201c

File diff suppressed because it is too large
+ 18 - 1
mjava-aiwei/src/main/java/com/malk/aiwei/controller/TBxYDController.java


+ 108 - 36
mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.java

@@ -44,8 +44,8 @@ public class AWImplClient implements AWClint {
     @Autowired
     private AwDingService awDingService;
 
-    // 项目主数据表
-    String _matchFormUuid(String code) {
+    // 单据环境映射
+    private String _matchFormUuid(String code) {
         Map<String, String> formUuid = UtilMap.empty();
         if (UtilEnv.getActiveProfile().equals(UtilEnv.ENV_PROD)) {
             formUuid.put("DENTRY", "FORM-BD73A57B62EA4153B896C9BB3EA14D28GWSQ");                // 文件夹/文件版本记录
@@ -108,7 +108,7 @@ public class AWImplClient implements AWClint {
             type = "回调";
         } else {
             // prd 发起评审先修改工作流,成功后再执行相关逻辑,避免前后置依赖与子任务未完成导致不能关闭
-            String workFlowId_c = _getWorkFlowStatus(pCode, "审批通过");
+            String workFlowId_c = _getWorkFlowStatus(pCode, "完成");
             if (!isChange) {
                 McException.assertAccessException(StringUtils.isBlank(UtilMap.getString(taskData, AWServer.TASK_CHECK_STATUS)), "请核对, 技术检查项检查状态!");
                 // prd: 不过滤审批中, 避免撤销后重新填写交付件情况
@@ -119,8 +119,16 @@ public class AWImplClient implements AWClint {
                 McException.assertAccessException(!rTask.get("tfsId").equals(workFlowId_c), "非已完成任务, 不允许提交变更!");
             }
         }
-        // ppExt: TB有卡控, 校验交付物不能为空, 同时兼容非交付件任务类型
         List<Map> docs = _getDocs(taskData, AWServer.TASK_APPROVE_ATTACHMENT);
+        // prd 7.10 交付件描述为空,即非交付件审批任务;且未传交付件内容,直接更新为审批通过。否则走交付物审批升版
+        if (UtilMap.isBlankString(taskData, AWServer.TASK_APPROVE_DESC) && docs.size() == 0) {
+            String workFlowId = _getWorkFlowStatus(pCode, "完成");
+            tbClient.updateTaskFlowStatus(taskId, tbConf.getOperatorId(), workFlowId, "完成");
+            Map body = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_LINK, "非交付件任务");
+            tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
+            return null;
+        }
+        // ppExt: TB有卡控, 校验交付物不能为空, 同时兼容非交付件任务类型
         McException.assertAccessException(!isCallback && docs.size() == 0, "任务交付件不能为空, 请填写后再操作!");
         String executorId = UtilMap.getString(rTask, "executorId");
         if (!isCallback) {
@@ -159,8 +167,11 @@ public class AWImplClient implements AWClint {
                     result = "未配置交付物审批";
                 }
             }
+            log.info("交付物结果, {}", result);
             /// 发起评审 prd 630 若无交付物审批, 则配置发起人自动通过, 以兼容全部任务类型可提交交付件评审
-            if (StringUtils.isBlank(result) || "未配置交付物审批".equals(result)) {
+            boolean isIgnore = "未配置交付物审批".equals(result);
+            if (StringUtils.isBlank(result) || isIgnore) {
+                result = ""; // 重置返回错误信息
                 List<Map> roles = (List<Map>) rProject.get("tableField_lqxtykcf");
                 Map formData = UtilMap.map("selectField_lqxuswzd, textField_lrncs2fu", tCode, pCode);
                 formData.put("attachmentField_lqxtebtq", docs);
@@ -172,35 +183,37 @@ public class AWImplClient implements AWClint {
                 formData.put("textareaField_lw0nmvko", UtilMap.getString(taskData, AWServer.TASK_APPROVE_DESC));         // 交付件描述
                 formData.put("textField_lvbrueqs", UtilMap.getString(taskData, AWServer.TASK_PRODUCT));                  // 产品型号
                 formData.put("textField_lvbrueqt", UtilMap.getString(taskData, AWServer.TASK_PRODUCT_VERSION));          // 产品版本
-                formData.put("radioField_ly2o9rxn", "未配置交付物审批".equals(result) ? "是" : "否");                        // 审批分支
+                formData.put("radioField_ly2o9rxn", isIgnore ? "是" : "否");                        // 审批分支
                 // 匹配任务编码与项目角色
                 List<Map<String, String>> compIds = Arrays.asList(  // 任务表角色, 交付物评审表: 角色, 审批人
                         UtilMap.map("tsRole, prRole, prEmp", "multiSelectField_lrokzlo7, multiSelectField_lrokzlo7, employeeField_lqxtebtw"),
                         UtilMap.map("tsRole, prRole, prEmp", "multiSelectField_lrokzlo8, multiSelectField_lrokzlo8, employeeField_lqxtebtx"),
                         UtilMap.map("tsRole, prRole, prEmp", "multiSelectField_lrokzlo9, multiSelectField_lrokzlo9, employeeField_lqxtebty"));
-                Map rCondition = tList.get(0);
-                for (Map compId : compIds) {
-                    // ppExt: 宜搭多选若无值则不返回字段 [prd 角色多选, 会签逻辑]
-                    if (!rCondition.containsKey(compId.get("tsRole"))) {
-                        continue;
-                    }
-                    List<String> namesCondition = (List<String>) rCondition.get(compId.get("tsRole"));
-                    List<String> namesApproveRole = new ArrayList<>();
-                    List<String> namesApprover = new ArrayList<>();
-                    for (String name : namesCondition) {
-                        Optional optional = roles.stream().filter(item -> UtilString.equal(name, item.get("selectField_lqxu6bgf"))).findAny();
-                        if (optional.isPresent()) {
-                            Map detail = (Map) optional.get();
-                            if (ObjectUtil.isNotNull(detail.get("selectField_lqxu6bgf"))) {
-                                namesApproveRole.add(String.valueOf(detail.get("selectField_lqxu6bgf")));
-                            }
-                            if (ObjectUtil.isNotNull(detail.get("employeeField_lqxtykch_id"))) {
-                                namesApprover.addAll((List) detail.get("employeeField_lqxtykch_id"));
+                if (!isIgnore) {
+                    Map rCondition = tList.get(0);
+                    for (Map compId : compIds) {
+                        // ppExt: 宜搭多选若无值则不返回字段 [prd 角色多选, 会签逻辑]
+                        if (!rCondition.containsKey(compId.get("tsRole"))) {
+                            continue;
+                        }
+                        List<String> namesCondition = (List<String>) rCondition.get(compId.get("tsRole"));
+                        List<String> namesApproveRole = new ArrayList<>();
+                        List<String> namesApprover = new ArrayList<>();
+                        for (String name : namesCondition) {
+                            Optional optional = roles.stream().filter(item -> UtilString.equal(name, item.get("selectField_lqxu6bgf"))).findAny();
+                            if (optional.isPresent()) {
+                                Map detail = (Map) optional.get();
+                                if (ObjectUtil.isNotNull(detail.get("selectField_lqxu6bgf"))) {
+                                    namesApproveRole.add(String.valueOf(detail.get("selectField_lqxu6bgf")));
+                                }
+                                if (ObjectUtil.isNotNull(detail.get("employeeField_lqxtykch_id"))) {
+                                    namesApprover.addAll((List) detail.get("employeeField_lqxtykch_id"));
+                                }
                             }
                         }
+                        formData.put(compId.get("prRole"), namesApproveRole);
+                        formData.put(compId.get("prEmp"), namesApprover);
                     }
-                    formData.put(compId.get("prRole"), namesApproveRole);
-                    formData.put(compId.get("prEmp"), namesApprover);
                 }
                 // 组装数据
                 Map<String, String> extra = (Map) tbClient.idMapQuery(executorId, "dingTalk-user", ddConf.getCorpId()).get(0).get("extra");
@@ -229,11 +242,11 @@ public class AWImplClient implements AWClint {
                 }
             }
         }
-        log.info("交付物结果, {}", result);
         // prd 618 通过任务扩展按钮, 提交后再触发审批回写, 直接提交场景下直接更新审批链接
         if (!isChange) {
             Map body = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_LINK, result);
             tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
+            //_updateApproveField(taskId, new ArrayList<>(), result, title, "审批中", AWServer.TASK_APPROVE_LINK);
         }
         return UtilMap.map("result, userId", result, userId);
     }
@@ -246,6 +259,12 @@ public class AWImplClient implements AWClint {
         String result = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/processDetail?procInsId=" + instanceId;
         Map body = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_LINK, result);
         tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
+
+        // todo tb回写多的审批记录仅显示第一个, 建议保存最后一个, 审批回调时更新, 避免匹配对应数据问题
+//        Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_APPROVE_LINK);
+//        List<Map> approves = _getCustomFieldList(taskData, AWServer.TASK_APPROVE_LINK);
+//        _updateApproveField(taskId, new ArrayList<>(), result, title, "审批中", AWServer.TASK_APPROVE_LINK);
+
         Map body2 = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_STATE, "变更中");
         tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body2);
     }
@@ -260,7 +279,7 @@ public class AWImplClient implements AWClint {
         String type = UtilMap.getString(data, "type");
         String projectId = UtilMap.getString(data, "projectId");
         String taskId = UtilMap.getString(data, "taskId");
-        String result = UtilMap.getString(data, "approve");
+        String result = UtilMap.getString(data, "approve").contains("通过") ? "完成" : "拒绝";
         if ("变更".equals(type)) {
             Map body = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_STATE, "已变更");
             tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
@@ -274,6 +293,28 @@ public class AWImplClient implements AWClint {
             String workFlowId = _getWorkFlowStatus(projectId, result);
             tbClient.updateTaskFlowStatus(taskId, tbConf.getOperatorId(), workFlowId, result);
         }
+//        String url = _matchFormUuid("DOMAIN") + ydConf.getAppType() + "/processDetail?procInsId=" + instanceId;
+//        _updateApproveField(taskId, new ArrayList<>(), url, title, result, AWServer.TASK_APPROVE_LINK);
+    }
+
+    /**
+     * 更新任务自定义审批字段值
+     *
+     * @apiNote https://open.teambition.com/docs/documents/664dad5aba1c5ca0e1576f8f#4%230%23%E5%AE%A1%E6%89%B9%E5%8A%9F%E8%83%BD%E9%85%8D%E7%BD%AE
+     * [ ppExt ]
+     * 1. 更新需要为 metaString, 若是 meta 自定义字段不生效. 目前发现仅自定义字段有此问题
+     * 2. tb 内弹出框打开可用 handler, 但宜搭链接会被拦击, 直接赋值url新标签页打开
+     * 3. 标题字段直接赋值, 不能添加自定义值, 如标记颜色, 否则点击连接诶后任务上该字段会消失: meta.put("title", UtilMap.map("color, value", "BLUE", "xxx"))
+     */
+    private void _updateApproveField(String taskId, List<Map> approves, String url, String title, String subTitle, String fieldName) {
+
+        Map colors = UtilMap.map("待提交, 已提交, 审批中, 已通过, 已拒绝, 已终止", "ORANGE, BLUE, BLUE, GREEN, RED, RED");
+        Map meta = UtilMap.map("instanceId, url", taskId, url);
+        meta.put("icon", UtilMap.map("value, color, bgColor", "Icon_Approve, WHITE, ORANGE"));
+        meta.put("subTitle", UtilMap.map("value, color", subTitle, colors.get(subTitle)));
+        approves.add(UtilMap.map("title, metaString", title, JSON.toJSONString(meta)));
+        log.info("更新任务自定义审批字段值, {}, {}", taskId, JSON.toJSONString(approves));
+        tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), UtilMap.map("customfieldName, value", fieldName, approves));
     }
 
     @Autowired
@@ -316,6 +357,8 @@ public class AWImplClient implements AWClint {
     String _getWorkFlowStatus(String projectId, String workFlowStatusName) {
 
         List<Map> customFlowStatus = tbClient.queryProjectCustomFlowStatus(projectId, UtilMap.map("q", workFlowStatusName));
+        // 7.10 避免模糊匹配
+        customFlowStatus = customFlowStatus.stream().filter(item -> workFlowStatusName.equals(UtilMap.getString(item, "name"))).collect(Collectors.toList());
         McException.assertAccessException(customFlowStatus.isEmpty(), "工作流名称未匹配");
         return String.valueOf(customFlowStatus.get(0).get("id"));
     }
@@ -325,6 +368,8 @@ public class AWImplClient implements AWClint {
         List<String> workFlowStatusList = new ArrayList<>();
         for (String name : workFlowStatusNames) {
             List<Map> customFlowStatus = tbClient.queryProjectCustomFlowStatus(projectId, UtilMap.map("q", name));
+            // 7.10 避免模糊匹配
+            customFlowStatus = customFlowStatus.stream().filter(item -> name.equals(UtilMap.getString(item, "name"))).collect(Collectors.toList());
             workFlowStatusList.addAll(customFlowStatus.stream().map(item -> UtilMap.getString(item, "id")).collect(Collectors.toList()));
         }
         return workFlowStatusList;
@@ -351,6 +396,10 @@ public class AWImplClient implements AWClint {
         List<Map> customfields = UtilMap.getList(rTask, "customfields");
         String deliverable = String.valueOf(taskData.get(fieldName + "_id")); // 交付物任务字段ID
         List<Map> attachments = TBConf.getTaskFieldValue(customfields, deliverable).stream().map(item -> {
+            // 7.8 兼容知识库版本: 无编辑权限, 复制失败 任务更新
+            if (UtilMap.isBlankString(item, "metaString")) {
+                return item;
+            }
             Map link = (Map) JSON.parse(String.valueOf(item.get("metaString")));
             Map row = UtilMap.map("downloadUrl, name", link.get("url"), link.get("title") + ".docx");
             row.putAll(link); // 保留原数据作为知识库版本管理相关参数
@@ -359,6 +408,15 @@ public class AWImplClient implements AWClint {
         return attachments;
     }
 
+
+    /// 获取自定义字段数据列表
+    List<Map> _getCustomFieldList(Map taskData, String fieldName) {
+        Map rTask = UtilMap.getMap(taskData, "task");
+        List<Map> customfields = UtilMap.getList(rTask, "customfields");
+        String deliverable = String.valueOf(taskData.get(fieldName + "_id")); // 自定义组件ID
+        return TBConf.getTaskFieldValue(customfields, deliverable);
+    }
+
     /**
      * 检查项check
      *
@@ -417,6 +475,7 @@ public class AWImplClient implements AWClint {
             if (!checkLink.equals(result)) {
                 Map body = TBConf.assembleCustomFieldName(AWServer.TASK_CHECK_LINK, result);
                 tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
+                //_updateApproveField(taskId, new ArrayList<>(), result, "技术检查确认", "待提交", AWServer.TASK_CHECK_LINK);
             }
             // prd 未配置预检项更新为已检查, 避免完成任务完成触发必填校验 [ppExt 避免重复写入, 重复写入TB会重复记录日志
             if (!result.startsWith("http") && !taskData.get(AWServer.TASK_CHECK_STATUS).equals("已检查")) {
@@ -454,6 +513,7 @@ public class AWImplClient implements AWClint {
         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);
     }
 
     /**
@@ -638,8 +698,8 @@ public class AWImplClient implements AWClint {
         List<String> representativeIds = new ArrayList<>();
 
         String representative = _getProjectRoleId(projectId, "项目各代表");
-        // prd 6.26 L类项目使用 项目各代表_L, TB配置拥有邀请成员权限
-        if ("生命周期维护项目".equals(formData.get("textField_ltwcq7s6"))) {
+        // prd 6.26 L类\C类项目使用 项目各代表_L, TB配置拥有邀请成员权限
+        if (Arrays.asList("生命周期维护项目", "Charter开发项目").contains(formData.get("textField_ltwcq7s6"))) {
             representative = _getProjectRoleId(projectId, "项目各代表_L");
         }
         String pmRoleId = _getProjectRoleId(projectId, AWServer.PROJECT_PM_ROLE);
@@ -653,7 +713,9 @@ public class AWImplClient implements AWClint {
                 continue;
             }
             String roleName = UtilMap.getString(row, "selectField_lqxu6bgf");
-            if (AWServer.PROJECT_PM_NAME.equals(roleName)) {
+            // prd 7.3 C类项目管理员权限, 分配为产品代表
+            String tbRole = "Charter开发项目".equals(formData.get("textField_ltwcq7s6")) ? "产品代表" : AWServer.PROJECT_PM_NAME;
+            if (tbRole.equals(roleName)) {
                 pmUserId.addAll(userIds);
             }
             // prd 项目各代表, 可独立N/A任务, 添加角色控制权限
@@ -1376,7 +1438,7 @@ public class AWImplClient implements AWClint {
                 String title = UtilMap.getString(doc, "title");
                 fileName = fileName + "/" + title;
                 Map version = _upsertFileAbsolutePath("文件", fileName, pCode, UtilMap.getString(rTask, "projectId"), workspaceId, rootNodeId, unionId);
-                // 通过提供 spaceId + dentryId 查询创建人, 再由创建人为公共账号添加编辑权限, 再通过公共账号复制 [ppExt: 知识库可为其他成员添加不高于自身的权限]
+                // 通过提供 spaceId + dentryId 查询创建人, 再由创建人为公共账号添加编辑权限, 再通过公共账号复制 [ ppExt: 知识库可为其他成员添加不高于自身的权限 ]
                 DDR_New ddr_dentry = ddClient_storage.getSpaceDentryDetail(ddClient.getAccessToken(), executorIdUnionId, UtilMap.getString(doc, "instanceId"), UtilMap.getString(doc, "instanceId"), false);
                 try {
                     String creatorId_t = UtilMap.getString(ddr_dentry.getCreator(), "unionId");
@@ -1393,7 +1455,7 @@ public class AWImplClient implements AWClint {
                 DDR_New ddr_new;
                 try {
                     ddr_new = ddClient_storage.copyDentries(ddClient.getAccessToken(), unionId, workspaceId, UtilMap.getString(doc, "instanceId"), workspaceId, rootNodeId, docName);
-                    _updatePermission(ddr_new.getDentryUuid(), unionId, tName, subList); // 设置文件权限矩阵
+                    //_updatePermission(ddr_new.getDentryUuid(), unionId, tName, subList); // 设置文件权限矩阵
                 } catch (McException e) {
                     // ppExt: 权限异常可能原因是重复授权, 依然执行复制操作, 再跳过
                     log.error(e.getMessage(), e);
@@ -1548,9 +1610,9 @@ public class AWImplClient implements AWClint {
 
     @Override
     public void test() {
-        approveVersion("659a681d44ade3345fdc0d39", "99999");
-
-        String pCode = "99999";
+//        approveVersion("659a681d44ade3345fdc0d39", "99999");
+//
+//        String pCode = "99999";
 //        String pCode = "A240407DryRun";
 //
 //        List<Map> pList = ydService.queryDataList_FormData(_matchFormUuid("PROJECT"), UtilMap.map("textField_lrj7vnxb", pCode));
@@ -1560,6 +1622,16 @@ public class AWImplClient implements AWClint {
 //        String creatorUnionId = String.valueOf(ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), "095358016629044412").get("unionid"));
 //        List<Map> allMembers = ddClient_storage.queryDentryPermissions(ddClient.getAccessToken(), "14lgGw3P8vMDNPvMC7aw11LzJ5daZ90D", creatorUnionId, null);
 //        log.info("xxxx, {}", allMembers);
+
+        String taskId = "65addc57055591ee7d17d807";
+        Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_APPROVE_LINK, AWServer.TASK_APPROVE_VERSION, AWServer.TASK_STAGE, AWServer.TASK_ROLE);
+        List<Map> checkList = _getCustomFieldList(taskData, AWServer.TASK_APPROVE_LINK);
+
+//        checkList = new ArrayList<>();
+//        String url = "https://kabom7.aliwork.com/APP_H7WUJTKB448F9IBDC6C4/processDetail?formUuid=FORM-FBC1A390B4C348089020C763938A6F54RUNY&procInsId=5bd917c4-b23c-4f2f-ba4b-cef533118cdf&isAdmin=true&navConfig.layout=1180";
+        String url = "https://kabom7.aliwork.com/APP_H7WUJTKB448F9IBDC6C4/submission/FORM-7B63BB056145452F8BC0A2C52492DE00QVBH?taskId=65addc57055591ee7d17d807";
+        _updateApproveField(taskId, checkList, url, "预检项确认", "待提交", AWServer.TASK_APPROVE_LINK);
+
     }
 
     @Override

+ 183 - 0
mjava-cloudpure/src/main/java/com/malk/cloudpure/controller/TLYController.java

@@ -0,0 +1,183 @@
+package com.malk.cloudpure.controller;
+
+import com.alibaba.fastjson.JSON;
+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.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.utils.UtilDateTime;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.*;
+
+/**
+ * 错误抛出与拦截详见 CatchException
+ */
+@Slf4j
+@RestController
+@RequestMapping("lanyun")
+public class TLYController {
+
+    @Autowired
+    private DDClient ddClient;
+
+    @Autowired
+    private YDClient ydClient;
+
+    @Autowired
+    private DDClient_Workflow ddClient_workflow;
+
+    /**
+     * 推送审批
+     */
+    @PostMapping("do-approve")
+    public McR doApprove(String processInstanceId, String type) {
+
+        log.info("推送审批, {} {}", type, processInstanceId);
+        // OA组件name, 匹配宜搭组件ID
+        if ("出差".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("出差类型, 预算, 出差天数, 出差事由, 出差备注", "textField_lygo4abp, numberField_lygo9owt, numberField_lygo4abs, textareaField_lygo9owv, textareaField_lygo9ox1");
+            Map<String, String> compsId_itinerary = UtilMap.map("交通工具, 单程往返, 出发城市, 目的城市, 开始时间, 结束时间, 时长", "textField_lygo9oww, textField_lygo9owx, textField_lygo9owy, textField_lygo9owz, textField_lygvu31i, textField_lygvu31j, numberField_lygo9ox0");
+            compsId_main.put("出差明细", "tableField_lygo9owu"); // 子表组件
+            syncYD(processInstanceId, "FORM-210DA087671044F8A5CD72F0E9E89060SZ8Q", compsId_main, compsId_itinerary, "itinerary");
+        }
+        if ("加班".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("加班人, 加班类型, 开始时间, 结束时间, 时长, 甲方付费, 加班原因", "employeeField_lygnp948, textField_lygnetwa, textField_lygwcloa, textField_lygwclob, numberField_lygnetwe, textField_lygnetwf, textareaField_lygnetwg");
+            Map<String, String> compsId_itinerary = UtilMap.map("加班时间, overtimeDuration", "textField_lygwclo8, numberField_lygnetwd");
+            compsId_main.put("明细", "tableField_lygnetwc"); // 子表组件
+            syncYD(processInstanceId, "FORM-B33388047A42479BB6387F9E0B453588EZ4P", compsId_main, compsId_itinerary, "everyDayDuration");
+        }
+        if ("请假".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("DDHolidayField, 请假事由, 代班人", "textField_lyh0xukm / textField_lyh0xukn / numberField_lygo4abs / radioField_lyh0xuko / textField_lygo4abp, textareaField_lygo4abt, employeeField_lygo4abu");
+            syncYD(processInstanceId, "FORM-FB29F35DF0A04DC2A5F2860E9593C9C6XCMT", compsId_main, null, "");
+        }
+        if ("调班".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("申请类型, 原班次名称, 原考勤时间, 新班次考勤时间, 新班次有效时长, 处理状态", "selectField_lygnsggb, textField_lygnsgg6, textField_lygnsgg7, textField_lygnsgg8, textField_lygnsgg9, selectField_lygnsgga");
+            syncYD(processInstanceId, "FORM-777EB143B87D45F2ACB228A024290E1A9GSF", compsId_main, null, "");
+
+        }
+        return McR.success();
+    }
+
+    /// dingtalk
+    final String APP_EKY = "dingfwn4kpmb4g3dy4fj";
+    final String APP_SECRET = "OwdkUc9nvBivpwsg0AiaEcyWLtZ678fskqBJwP7B5CQNrycIoyKuWslFTCOOaZG4";
+    /// aliwork
+    final String APP_TYPE = "APP_ERBDTFS82HOVBPL3NFH0";
+    final String SYSTRM_TOKEN = "RRB66F91T97H1WN89QZYC47PKLZO2ZQOUMOQLP";
+
+    /// 同步OA单据到宜搭
+    public void syncYD(String processInstanceId, String formUuid, Map<String, String> compsId_main, Map<String, String> compsId_itinerary, String compId_sub_oa) {
+
+        String token = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+
+        Map processData = ddClient_workflow.getProcessInstanceId(token, processInstanceId);
+        List<Map> formComponentValues = (List<Map>) processData.get("formComponentValues");
+
+        String userId = String.valueOf(processData.get("originatorUserId"));
+        long cDate = UtilDateTime.parse(UtilMap.getString(processData, "createTime"), "yyyy-MM-dd'T'HH:mm").getTime();
+        Map formData = UtilMap.map("employeeField_ltxqs53k, departmentSelectField_lu20ayky, dateField_ltxqs53j, textField_lygnetw9", Arrays.asList(userId), Arrays.asList(processData.get("originatorDeptId")), cDate, UtilMap.getString(processData, "businessId"));
+
+        for (String name : compsId_main.keySet()) {
+            String compId = compsId_main.get(name);
+            // 判定是否子表 [宜搭]
+            if (compId.startsWith("tableField_")) {
+                List<Map> details = new ArrayList<>();
+                // 兼容明细组件, 存在多条情况 [加班跨天才有有明细]
+                Optional optional = formComponentValues.stream().filter(item -> compId_sub_oa.equals(item.get("bizAlias"))).findAny();
+                if (!optional.isPresent()) {
+                    continue;
+                }
+                String schedule = UtilMap.getString((Map) optional.get(), "value");
+                List<Map> itineraryList = ((List<Map>) JSON.parse(schedule));
+                // 循环明细数据
+                for (Map itinerary : itineraryList) {
+                    List<Map> rowValue = (List<Map>) itinerary.get("rowValue");
+                    Map rowData = new HashedMap();
+                    // 循环子表组件
+                    for (String subName : compsId_itinerary.keySet()) {
+                        log.info("子表字段, {}", subName);
+                        //  加班单跨天 [子表label为空]
+                        rowData.put(compsId_itinerary.get(subName), rowValue.stream().filter(item -> subName.equals(item.get("bizAlias")) || subName.equals(item.get("label"))).findAny().get().get("value"));
+                    }
+                    details.add(rowData);
+                }
+                formData.put(compId, details);
+                continue;
+            }
+            log.info("主表字段, {}", name);
+            // 请假套件: 开始时间 / 结束时间 / 时长 / 单位 / 请假类型
+            if ("DDHolidayField".equals(name)) {
+                Optional optional = formComponentValues.stream().filter(item -> "DDHolidayField".equals(item.get("componentType"))).findAny();
+                if (optional.isPresent()) {
+                    String[] ids = compId.split(" / ");
+                    List vas = (List) JSON.parse(UtilMap.getString((Map) optional.get(), "value"));
+                    for (int i = 0; i < ids.length; i++) {
+                        formData.put(ids[i], vas.get(i));
+                    }
+                }
+                continue;
+            }
+            Map formComp = formComponentValues.stream().filter(item -> name.equals(item.get("name"))).findAny().get();
+            Object value = formComp.get("value");
+            // 成员组件, 数据处理
+            if ("InnerContactField".equals(formComp.get("componentType")) && formComp.containsKey("value")) {
+                List<Map> empInfos = (List<Map>) JSON.parse(String.valueOf(formComp.get("extValue")));
+                List<String> emplsId = new ArrayList<>();
+                for (Map empInfo : empInfos) {
+                    emplsId.add(String.valueOf(empInfo.get("emplId")));
+                }
+                value = emplsId; // 成员多选
+            }
+            formData.put(compId, value);
+        }
+
+        // 用于审批回传
+        List<Map> tasks = UtilMap.getList(processData, "tasks");
+        formData.put("textField_lygvvyd9", tasks.get(0).get("taskId"));
+        formData.put("textField_lygvvyda", tasks.get(0).get("userId"));
+        formData.put("textField_lyh4y3th", processInstanceId);
+
+        log.info("审批数据, {}", JSON.toJSONString(formData));
+        ydClient.operateData(YDParam.builder()
+                .appType(APP_TYPE)
+                .systemToken(SYSTRM_TOKEN)
+                .formUuid(formUuid)
+                .formDataJson(JSON.toJSONString(formData))
+                .userId(userId)
+                .build(), YDConf.FORM_OPERATION.start);
+    }
+
+    /**
+     * 审批回调
+     */
+    @PostMapping("approved")
+    public McR approved(String processInstanceId, String userId, String taskId, String result) {
+        log.info("审批回调, {} {}", processInstanceId, result);
+        String accessToken = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+        ddClient_workflow.executeRunningApprove(accessToken, processInstanceId, userId, taskId, result, "", null);
+        return McR.success();
+    }
+
+    /**
+     * 删除/撤销
+     */
+    @PostMapping("terminate")
+    public McR terminate(String processInstanceId, String userId) {
+        log.info("删除/撤销, {} {}", processInstanceId, userId);
+
+        String accessToken = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+        ddClient_workflow.terminateRunningApprove(accessToken, processInstanceId, true, "发起人撤销", userId);
+        return McR.success();
+    }
+}
+
+

+ 12 - 1
mjava-cloudpure/src/main/java/com/malk/cloudpure/controller/TSController.java

@@ -9,6 +9,7 @@ import com.malk.service.aliwork.YDClient;
 import com.malk.service.aliwork.YDService;
 import com.malk.service.dingtalk.DDClient;
 import com.malk.service.dingtalk.DDClient_Contacts;
+import com.malk.service.dingtalk.DDClient_Workflow;
 import com.malk.utils.UtilDateTime;
 import com.malk.utils.UtilMap;
 import lombok.SneakyThrows;
@@ -61,6 +62,17 @@ public class TSController {
         return McR.success();
     }
 
+    @Autowired
+    private DDClient_Workflow ddClient_workflow;
+
+    @PostMapping("tmp")
+    public void tmp() {
+        String token = ddClient.getAccessToken("dingfwn4kpmb4g3dy4fj", "OwdkUc9nvBivpwsg0AiaEcyWLtZ678fskqBJwP7B5CQNrycIoyKuWslFTCOOaZG4");
+
+//        ddClient_workflow.getProcessInstanceId(token, "QVxpoRomSfOqOeuUqzwXvQ04851720676197");
+//        ddClient_workflow.executeRunningApprove(token, "QVxpoRomSfOqOeuUqzwXvQ04851720676197", "396511732", "87755078200", "agree", "", null);
+
+    }
 
     // 蓝云
     private void updateVersion(String formInstId) {
@@ -163,7 +175,6 @@ public class TSController {
                     .formInstanceId(UtilMap.getString(item, "formInstanceId"))
                     .build(), YDConf.FORM_OPERATION.delete);
         });
-
     }
 
     private void updatePosition(String userId) {

+ 2 - 5
mjava/src/main/java/com/malk/server/teambition/TBConf.java

@@ -6,10 +6,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
 
 /**
  * 读取配置文件参考FilePah
@@ -58,7 +55,7 @@ public class TBConf {
         if (optional.isPresent()) {
             return UtilMap.getList((Map) optional.get(), "value");
         }
-        return Arrays.asList();
+        return new ArrayList<>();
     }
 
     /// 获取任务字段集合第一个值

+ 2 - 1
mjava/src/main/java/com/malk/service/dingtalk/DDClient_Storage.java

@@ -75,6 +75,7 @@ public interface DDClient_Storage {
      * 1.知识库文件/文件夹复制接口需要传递来源 workplaceId, 但测试下来传递目标 workplaceId 也可以, 包括从我的文档内拷贝均验证通过
      * 2.知识库复制需要同时有文件与\复制目标文件夹下编辑权限. 目前外部使用不能提供公共操作账号, 只能通过 dentryUuid 给来源文件添加编辑权限, 再进行复制操作
      * 3.高密知识库访问限制: 同一个知识库内可以复制, 产品答复通过接口形态也与实际操作一致的表现, 均有控制不能往外进行复制. 写入是被允许的
+     * 4. ppExt 设置打断权限后, 若是没有父文件编辑以上权限, 复制异常
      */
     DDR_New copyDentries(String accessToken, String operatorId, String workspaceId, String fileDentryId, String targetSpaceId, String toParentDentryId, String fileName);
 
@@ -131,7 +132,7 @@ public interface DDClient_Storage {
     boolean deleteDentryPermissions(String accessToken, String dentryUuid, String unionId, String roleId, List<Map> members, Map option);
 
     /**
-     * 设置权限继承模式
+     * 设置权限继承模式 [ fixme: 设置打断权限后, 若是没有父文件编辑以上权限, 复制异常 ]
      * ppExt: 设置为打断模式, 与手动修改权限效果一致. 后续知识库成员调整, 非管理者与所有者人员不会传递到当前路径下, 即该文件若要可见需要重新对成员赋权
      *
      * @param inheritance 不支持OWNER和MANAGER的打断, 默认权限继承模式

+ 5 - 0
mjava/src/main/java/com/malk/service/dingtalk/DDClient_Workflow.java

@@ -33,6 +33,11 @@ public interface DDClient_Workflow {
      */
     boolean terminateRunningApprove(String access_token, String processInstanceId, boolean isSystem, String remark, String operatingUserId);
 
+    /**
+     * 同意或拒绝审批任务
+     */
+    boolean executeRunningApprove(String access_token, String processInstanceId, String actionerUserId, String taskId, String result, String remark, Map file);
+
     /**
      * 通知审批撤销
      */

+ 25 - 4
mjava/src/main/java/com/malk/service/dingtalk/impl/DDImplClient_Workflow.java

@@ -69,6 +69,21 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
         return (Boolean) r.getResult();
     }
 
+    /**
+     * 同意或拒绝审批任务
+     *
+     * @apiNote https://open.dingtalk.com/document/orgapp/approve-or-reject-the-approval-task
+     */
+    @Override
+    public boolean executeRunningApprove(String access_token, String processInstanceId, String actionerUserId, String taskId, String result, String remark, Map file) {
+
+        Map header = DDConf.initTokenHeader(access_token);
+        Map body = UtilMap.map("processInstanceId, result, actionerUserId, taskId, file", processInstanceId, result, actionerUserId, taskId, file);
+        UtilMap.putNotEmpty(body, "remark", remark);
+        DDR_New r = DDR_New.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/execute", header, null, body);
+        return (Boolean) r.getResult();
+    }
+
     /**
      * 通知审批撤销
      *
@@ -82,13 +97,15 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
         return (Boolean) r.getResult();
     }
 
+
     /**
      * 添加审批评论
      *
      * @apiNote https://open.dingtalk.com/document/orgapp-service/add-an-approval-comment
      */
     @Override
-    public boolean commentApproveInstance(String access_token, String processInstanceId, File file, String text, String commentUserid) {
+    public boolean commentApproveInstance(String access_token, String processInstanceId, File file, String
+            text, String commentUserid) {
         Map header = DDConf.initTokenParams(access_token);
         Map body = UtilMap.map("process_instance_id, file, text, comment_userid", processInstanceId, file, text, commentUserid);
         Map req = UtilMap.map("request", body);
@@ -122,12 +139,14 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
      * 获取审批实例ID列表_全部
      */
     @Override
-    public List<String> getInstanceIds_all(String access_token, String processCode, long startTime, long endTime, Map extInfo) {
+    public List<String> getInstanceIds_all(String access_token, String processCode, long startTime,
+                                           long endTime, Map extInfo) {
         return _getInstanceIds_all(access_token, processCode, startTime, endTime, extInfo, new ArrayList());
     }
 
     // 递归查询
-    private List<String> _getInstanceIds_all(String access_token, String processCode, long startTime, long endTime, Map extInfo, List dataList) {
+    private List<String> _getInstanceIds_all(String access_token, String processCode, long startTime,
+                                             long endTime, Map extInfo, List dataList) {
         Map rsp = getInstanceIds(access_token, processCode, startTime, endTime, extInfo);
         dataList.addAll(((List) rsp.get("list")));
         if (rsp.containsKey("nextToken")) {
@@ -146,7 +165,9 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
      * @apiNote https://open.dingtalk.com/document/isvapp/add-dingtalk-to-do-task
      */
     @Override
-    public DDR_New createTBTask(String access_token, String createUserId, String subject, String description, long dueTime, List<String> executorIds, List<String> participantIds, Map detailUrl, boolean isOnlyShowExecutor, int priority, Map notifyConfigs) {
+    public DDR_New createTBTask(String access_token, String createUserId, String subject, String description,
+                                long dueTime, List<String> executorIds, List<String> participantIds, Map detailUrl,
+                                boolean isOnlyShowExecutor, int priority, Map notifyConfigs) {
         String unionId = String.valueOf(ddClient_contacts.getUserInfoById(access_token, createUserId).get("unionid"));
         Map param = new HashMap();
         param.put("operatorId", unionId);