pruple_boy 1 year ago
parent
commit
b3bef8ac2f

+ 20 - 12
mjava-aiwei/src/main/java/com/malk/aiwei/controller/TBxYDController.java

@@ -16,7 +16,6 @@ import com.malk.service.teambition.TBClient;
 import com.malk.utils.UtilMap;
 import com.malk.utils.UtilServlet;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -24,7 +23,6 @@ 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.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -99,7 +97,26 @@ public class TBxYDController {
         log.info("通过模板创建项目, {}", data);
         McException.assertParamException_Null(data, "projectCode, templateId");
 
-        awClint.createProject(UtilMap.getString(data, "projectCode"), UtilMap.getString(data, "templateId"));
+        // todo 多模板适配
+        String templateId = UtilMap.getString(data, "templateId");
+        awClint.createProject(UtilMap.getString(data, "projectCode"), templateId);
+        return McR.success();
+    }
+
+    /**
+     * 项目主数据增量更新
+     */
+    @PostMapping("project/update")
+    McR updateProject(HttpServletRequest request) {
+
+        Map<String, ?> data = UtilServlet.getParamMap(request);
+        log.info("项目主数据增量更新, {}", data);
+        McException.assertParamException_Null(data, "projectCode");
+
+        List<String> pIdList = (List<String>) JSON.parse(UtilMap.getString(data, "projectCode"));
+        for (String projectCode : pIdList) {
+            awClint.updateProject(projectCode);
+        }
         return McR.success();
     }
 
@@ -214,15 +231,6 @@ public class TBxYDController {
 //        tbClient.queryProjectMember(projectId, null);
 
 //        awClint.test();
-
-
-        String groupName = "LQ";
-        if (StringUtils.isNotBlank(groupName)) {
-            List<Map> tagList = tbClient.queryTagList(UtilMap.map("q", groupName));
-            if (tagList.size() > 0) {
-                tbClient.updateProjectTag(projectId, Arrays.asList(UtilMap.getString(tagList.get(0), "id")), tbConf.getOperatorId());
-            }
-        }
         return McR.success();
     }
 }

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

@@ -17,6 +17,7 @@ public class AWServer {
     public static final String TASK_ROLE = "资源名称";
     public static final String TASK_CODE = "任务编号";
     public static final String TASK_STAGE = "TR评审节点";
+    public static final String TASK_STAGE_BLANK = "无TR评审节点";
 
     // prd 23.02.29 字段从 预检项 变更为 技术检查项
     public static final String TASK_CHECK_LINK = "技术检查项";
@@ -32,6 +33,14 @@ public class AWServer {
     public static final String PROJECT_PM_ROLE = "项目管理员";
     public static final String PROJECT_PM_NAME = "项目经理";
 
+    // 预检项不加载白名单 [项目模板/供复制项目]
+    public static final List<String> PROJECT_IGNORE_ID = Arrays.asList(
+            "65fac2fc386eb113f1adac83", // ak类正式模板
+            "65fac3af386eb113f1adacc0", // ak类迁移模板
+            "65f269605a2065ad7ad65d18"  // ak类复制项目
+    );
+
+
     /**
      * 艾为网关接口
      */

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

@@ -37,6 +37,11 @@ public interface AWClint {
      */
     void createProject(String projectCode, String templateId);
 
+    /**
+     * 项目主数据增量更新
+     */
+    void updateProject(String projectCode);
+
     /**
      * 分配项目角色 prd 若是一人直接指定, 多人情况下忽略
      */

+ 72 - 26
mjava-aiwei/src/main/java/com/malk/aiwei/service/impl/AWImplClient.java

@@ -50,7 +50,7 @@ public class AWImplClient implements AWClint {
             formUuid.put("ROLE", "FORM-3C7396A12ADB48A8833EBD90089C93833R21"); // 项目角色
             formUuid.put("DOMAIN", "https://yida.awinic.com/"); // 宜搭域名
         } else {
-            // [ ppExt: 测试环境添加主数据, 匹配不同架构下企业成员 ]
+            // [ fixme: 测试环境添加主数据, 匹配不同架构下企业成员 ]
             formUuid.put("REVIEW", "FORM-FBC1A390B4C348089020C763938A6F54RUNY");
             formUuid.put("REVIEW_PROCESS", "TPROC--YU966T91PIDH4XDR82OJC8GVOP7Z19PODTXQL6");
             formUuid.put("PROJECT", "FORM-84EF78C7DBA047E58A8C8511106F91D5WNVI");
@@ -69,12 +69,12 @@ public class AWImplClient implements AWClint {
     @Override
     public void doApprove(Map data) {
 
-        log.info("交付物审批, {}", data);
         String projectId = UtilMap.getString(data, "projectId");
         String workFlowApprove = _getWorkFlowStatus(projectId, AWServer.WORKFLOW_APPROVE);
         if (!data.get("tfsId").equals(workFlowApprove)) {
             return;
         }
+        log.info("交付物审批, {}", data);
         String pCode = UtilMap.getString(data, "projectId");
         String creatorId = UtilMap.getString(data, "creatorId");
         String taskId = UtilMap.getString(data, "taskId");
@@ -258,19 +258,22 @@ public class AWImplClient implements AWClint {
 
         log.info("检查项check, {}", taskId);
         Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_CODE, AWServer.TASK_CHECK_LINK, AWServer.TASK_ROLE, AWServer.TASK_STAGE, AWServer.TASK_CHECK_STATUS);
+        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 预检项持续维护需要重新匹配, 任务号和主数据判定为非法, 无需重新刷新
         if (isTask && (StringUtils.isNotBlank(checkLink) && !"未配置预检项".equals(checkLink))) {
             return UtilMap.map("result", "链接已加载");
         }
         // prd 非未完成任务若是未配置预检项状态, 忽略更新
-        Map rTask = UtilMap.getMap(taskData, "task");
-        if (isTask && !_getWorkFlowStatusList(UtilMap.getString(rTask, "projectId"), AWServer.WORKFLOW_INITIAL).contains(rTask.get("tfsId"))) {
+        if (isTask && !_getWorkFlowStatusList(pCode, AWServer.WORKFLOW_INITIAL).contains(rTask.get("tfsId"))) {
             return UtilMap.map("result", "任务已执行");
         }
         String creatorId = UtilMap.getString(rTask, "creatorId");
         String tCode = UtilMap.getString(taskData, AWServer.TASK_CODE);
-        String pCode = UtilMap.getString(rTask, "projectId");
 
         String result = "";
         List<Map> tList = null;
@@ -384,17 +387,23 @@ public class AWImplClient implements AWClint {
             formData.put("textField_ltsdsti7", baseFormData.get("textField_llouhiyf"));      // 项目状态
             formData.put("textField_ltsdsti8", baseFormData.get("textField_lo2c1f0r"));      // mPDT
             formData.put("textField_ltsdsti9", baseFormData.get("textField_lki3egzn"));      // Charter子类
+            formData.put("textField_ltwcq7s5", baseFormData.get("textField_lki3egzm"));      // Charter
+            formData.put("textField_ltwcq7s6", baseFormData.get("textField_lnjqpeie"));      // 项目类型 (TB项目模板)
             // 项目角色详情
             List<Map> details = new ArrayList<>();
-            mapRool.forEach(root -> {
+            for (Map root : mapRool) {
                 Map rootFormData = (Map) root.get("formData");
                 // 创建详情的角色名称和成员组件Map
                 Object rootItems = baseFormData.get(rootFormData.get("textField_lr7bgi76"));
                 Object projectRoot = rootFormData.get("textField_lqxu439g");
                 if (rootItems != null && projectRoot != null) {
                     details.add(UtilMap.map("employeeField_lqxtykch, selectField_lqxu6bgf", rootItems, projectRoot));
+                    // prd 添加项目经理, 用于数据权限控制
+                    if (AWServer.PROJECT_PM_NAME.equals(projectRoot)) {
+                        formData.put("employeeField_ltzn872j", rootItems);
+                    }
                 }
-            });
+            }
             formData.put("tableField_lqxtykcf", details);
             // 通过项目号获取项目是否已存在
             List<Map> projectMaps = ydService.queryDataList_FormData(_matchFormUuid("PROJECT"), UtilMap.map("textField_lrj7vnxb", baseFormData.get("textField_le6o88w0")));
@@ -415,7 +424,7 @@ public class AWImplClient implements AWClint {
                 String formInstId = (String) ydClient.operateData(ydParam2, YDConf.FORM_OPERATION.create);
                 formData.put("formInstanceId", formInstId);
             }
-            // tb项目成员更新 [兼容存量创建项目, 被删除后执行异常]
+            // 全量兼容: tb项目成员更新 [兼容存量创建项目, 被删除后执行异常]
             String projectId = UtilMap.getString(formData, "textField_lqxtykce");
             if (StringUtils.isNotBlank(projectId) && StringUtils.isBlank(projectCode)) {
                 try {
@@ -444,9 +453,8 @@ public class AWImplClient implements AWClint {
 
         // 虽然先创建TB, 可以一次性同步项目数据, 但避免执行异常, 因此成功创建TB项目再回写
         Map formData = syncProject(projectCode);
-        log.info("项目主数据, {}", formData);
-
         String projectId = UtilMap.getString(formData, "textField_lqxtykce");
+        log.info("项目主数据, {}, {}", projectId, formData);
         if (StringUtils.isBlank(projectId)) {
             // 通过模板创建项目, 创建项目并更新项目TB项目ID
             Map result = tbClient.projectCreateWithTemplate(projectCode, templateId, tbConf.getOperatorId());
@@ -459,11 +467,31 @@ public class AWImplClient implements AWClint {
         _syncProjectRole(formData, projectId);
     }
 
+    /**
+     * 项目主数据增量更新
+     */
+    @Override
+    public void updateProject(String projectCode) {
+        Map formData = syncProject(projectCode);
+        log.info("项目主数据增量更新, {}", formData);
+        String projectId = UtilMap.getString(formData, "textField_lqxtykce");
+        if (StringUtils.isNotBlank(projectId)) { // 屏蔽未创建项目, 出现新增
+            _syncProjectRole(formData, projectId);
+        }
+    }
+
     // 同步TB项目成员/项目分组 todo 群成员
     void _syncProjectRole(Map formData, String projectId) {
+
         // prd TB项目成员, 项目经理设置为管理者
         List<String> pmUserId = new ArrayList<>();
         List<String> roleIds = new ArrayList<>();
+        List<String> representativeIds = new ArrayList<>();
+
+        String representative = _getProjectRoleId(projectId, "项目各代表");
+        String pmRoleId = _getProjectRoleId(projectId, AWServer.PROJECT_PM_ROLE);
+        String staffRoleId = _getProjectRoleId(projectId, "项目成员");
+
         List<Map> details = (List<Map>) formData.get("tableField_lqxtykcf");
         for (Map row : details) {
             // 同步项目主数据, 会重新匹配项目成员, 因此无需 employeeField_xxx_id 取值
@@ -471,23 +499,29 @@ public class AWImplClient implements AWClint {
             if (userIds.isEmpty()) {
                 continue;
             }
-            if (AWServer.PROJECT_PM_NAME.equals(row.get("selectField_lqxu6bgf"))) {
+            String roleName = UtilMap.getString(row, "selectField_lqxu6bgf");
+            if (AWServer.PROJECT_PM_NAME.equals(roleName)) {
                 pmUserId.addAll(userIds);
             }
+            // prd 项目各代表, 可独立N/A任务, 添加角色控制权限
+            if (roleName.endsWith("代表")) {
+                representativeIds.addAll(userIds);
+            }
             roleIds.addAll(userIds);
         }
         // 钉钉人员ID转TB人员ID
         roleIds = _convertUserId(roleIds, false);
         tbClient.createProjectMember(projectId, roleIds, tbConf.getOperatorId());
-
+        if (!representativeIds.isEmpty()) {
+            tbClient.updateProjectMember(_convertUserId(representativeIds, false), Arrays.asList(staffRoleId, representative), projectId);
+        }
         // prd 不做删除, 离职自动退出, 避免临时交接不可见的问题. 但需要移除项目经理权限
-        String pmRoleId = _getProjectRoleId(projectId, AWServer.PROJECT_PM_ROLE);
         List<Map> roleList = tbClient.queryProjectMember(projectId, UtilMap.map("projectRoleId", pmRoleId));
         for (Map role : roleList) {
             List<String> tRoleIds = UtilMap.getList(role, "roleIds");
             tRoleIds.remove(pmRoleId);
             if (tRoleIds.isEmpty()) {
-                tRoleIds.add(_getProjectRoleId(projectId, "项目成员"));  // 极少情况, 无需冗余
+                tRoleIds.add(staffRoleId);
             }
             tbClient.updateProjectMember(Arrays.asList(UtilMap.getString(role, "userId")), tRoleIds, projectId);
         }
@@ -500,7 +534,7 @@ public class AWImplClient implements AWClint {
         if (StringUtils.isNotBlank(groupName)) {
             List<Map> tagList = tbClient.queryTagList(UtilMap.map("q", groupName));
             if (tagList.size() > 0) {
-                tbClient.updateProjectTag(projectId, Arrays.asList(UtilMap.getString(tagList.get(0), "id")), tbConf.getOperatorId());
+                tbClient.updateProjectTag(projectId, Arrays.asList(UtilMap.getString(tagList.get(0), "id"), staffRoleId), tbConf.getOperatorId());
             }
         }
         log.info("TB项目信息, {}, {}", projectId, roleIds.size());
@@ -550,11 +584,19 @@ public class AWImplClient implements AWClint {
                     if (!optional2.isPresent()) {
                         continue;
                     }
+
+                    // prd 传递无TR评审节点值, 匹配空值, 作为分配条件
                     String trName = TBConf.getTaskFieldValue_First(customfields, UtilMap.getString((Map) optional2.get(), "id"));
-                    if (!trNode.contains(trName)) {
-                        continue;
+                    if (StringUtils.isBlank(trName)) {
+                        if (!trNode.contains(AWServer.TASK_STAGE_BLANK)) {
+                            continue;
+                        }
+                    } else {
+                        if (!trNode.contains(trName)) {
+                            continue;
+                        }
                     }
-                    log.info("trName = {}", trName);
+                    log.info("tr节点控制, {}, {}", trName, trNode);
                 }
                 String roleName = TBConf.getTaskFieldValue_First(customfields, UtilMap.getString((Map) optional.get(), "id"));
                 // 获取角色在项目主数据对应成员
@@ -562,7 +604,7 @@ public class AWImplClient implements AWClint {
                 if (!optional.isPresent()) {
                     continue;
                 }
-                log.info("分配项目角色 taskId = {}, trNode = {}, 资源名称 = {}, 项目角色 = {}", UtilMap.getString(task, "id"), roleName, optional.get());
+                log.info("分配项目角色 taskId = {}, 资源名称 = {}, 角色信息 = {}", UtilMap.getString(task, "id"), roleName, optional.get());
                 List<String> roleIds = (List<String>) UtilMap.getList((Map) optional.get(), "employeeField_lqxtykch_id");
                 if (roleIds.size() == 1) {
                     String tbUserId = _convertUserId(roleIds.get(0), false);
@@ -593,7 +635,8 @@ public class AWImplClient implements AWClint {
                 }
                 List<Map> customfields = (List<Map>) task.get("customfields");
                 String trName = TBConf.getTaskFieldValue_First(customfields, UtilMap.getString((Map) optional.get(), "id"));
-                if (trNode.contains(trName)) {
+                // prd 传递无TR评审节点值, 匹配空值, 作为删除条件
+                if (trNode.contains(trName) || (StringUtils.isBlank(trName) && trNode.contains(AWServer.TASK_STAGE_BLANK))) {
                     String taskId = UtilMap.getString(task, "id");
                     List<Map> dependencies = tbClient.queryTaskDependency(taskId, null);
                     log.info("taskID = {}, trNode, {}, 依赖数量: {}", taskId, trName, dependencies.size());
@@ -645,6 +688,7 @@ public class AWImplClient implements AWClint {
                 .formUuid(curFormUuid)
                 .searchFieldJson(JSON.toJSONString(UtilMap.map("selectField_lrnd3h0s", checkType)))
                 .build());
+        log.info("全量同步 ##, srcList = {}, curList = {}", srcList.size(), curList.size());
 
         for (Map item : srcList) {
             if (StringUtils.isBlank(UtilMap.getString(item, taskCompId))) {
@@ -680,6 +724,7 @@ public class AWImplClient implements AWClint {
     @Override
     public void syncCheckList(int type) {
 
+        log.info("## 同步预检项, {}", type);
         switch (type) {
             case 0:
                 // 经验库
@@ -688,8 +733,8 @@ public class AWImplClient implements AWClint {
                         .systemToken("6J8668A1B59FA7SAFTAF89PTKKGP2HY3FP3OLV1")
                         .formUuid("FORM-D47CCA076DA54F969DF7442D81E4B5A8PC6Q")
                         .build();
-                // 任务号, 描述, ID(标题)
-                Map<String, ?> compIds1 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r", "selectField_ls9yctsd", "textareaField_lo5eakcv", "serialNumberField_lo5dmyfb");
+                // 任务号, 描述, ID(标题), 任务名称, 适用项目类型
+                Map<String, ?> compIds1 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r, textField_ltzl9mpc, multiSelectField_ltwjre9s", "selectField_ls9yctsd, textareaField_lo5eakcv, serialNumberField_lo5dmyfb, textField_ls9xvxvk, multiSelectField_ltwjre9s");
                 syncCheckList(ydParam1, compIds1, "selectField_ls9yctsd", "serialNumberField_lo5dmyfb", "经验库", "associationFormField_lrrn5csf");
                 break;
 
@@ -700,8 +745,8 @@ public class AWImplClient implements AWClint {
                         .systemToken("UM6660D1BFTFLFAP7NPJDBJEEAB737FAR1SOLD4")
                         .formUuid("FORM-B40420AFCDB84AD69FE78D82D6D5CFD2C2D2")
                         .build();
-                // 任务号, 描述, ID(标题)
-                Map<String, ?> compIds2 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r", "selectField_bclg9y5", "textField_k1e08ji", "textField_96ikaoh");
+                // 任务号, 描述, ID(标题), 任务名称, 适用项目类型
+                Map<String, ?> compIds2 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r, textField_ltzl9mpc, multiSelectField_ltwjre9s", "selectField_bclg9y5, textField_k1e08ji, textField_96ikaoh, textField_lsa0d856, multiSelectField_ltwjre9s");
                 syncCheckList(ydParam2, compIds2, "selectField_bclg9y5", "textField_96ikaoh", "技术评审要素表", "associationFormField_lrrn5csg");
                 break;
 
@@ -710,11 +755,12 @@ public class AWImplClient implements AWClint {
                 YDParam ydParam3 = YDParam.builder()
                         .formUuid("FORM-5436DC75BC8347D498DFF2617925BA70O066")
                         .build();
-                // 任务号, 描述, ID(标题), 验收标准, 等级
-                Map<String, ?> compIds3 = UtilMap.map("selectField_lrncf4hk, textField_lrrnqz7w, textField_lrnd3h0r, textField_cd4f65l, textField_267kho2", "selectField_4gwjfa5, textField_lryetn3g, textField_9x1yps6, textField_cd4f65l, textField_267kho2");
+                // 任务号, 描述, 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");
                 syncCheckList(ydParam3, compIds3, "selectField_4gwjfa5", "textField_9x1yps6", "TR评审要素表", "associationFormField_lrrn5csa");
                 break;
         }
+        log.info("同步预检项 ##, {}", type);
     }
 
     @Override

+ 13 - 0
mjava-hake/src/main/java/com/malk/hake/controller/HKController.java

@@ -2,9 +2,13 @@ package com.malk.hake.controller;
 
 import com.alibaba.fastjson.JSON;
 import com.malk.hake.service.HKClient;
+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.server.common.McREnum;
+import com.malk.service.aliwork.YDClient;
+import com.malk.utils.UtilMap;
 import com.malk.utils.UtilServlet;
 import lombok.SneakyThrows;
 import lombok.Synchronized;
@@ -85,8 +89,17 @@ public class HKController {
         return McR.success();
     }
 
+
+    @Autowired
+    private YDClient ydClient;
+
     @GetMapping("test")
     McR test() {
+
+        ydClient.operateData(YDParam.builder()
+                .formInstanceId("125e59bb-5f1f-4307-b809-79d0add5e28e")
+                .updateFormDataJson(JSON.toJSONString(UtilMap.map("textareaField_lntocd8u", "管理员拒绝")))
+                .build(), YDConf.FORM_OPERATION.update);
         return McR.success();
     }
 }

+ 2 - 1
mjava-hake/src/main/resources/static/json/form.json

@@ -29,7 +29,8 @@
         "numberField_lt60a87j": "standardPrice",
         "numberField_lnmyiqb4": "",
         "numberField_lnmyiqb5": "",
-        "textField_lnmyiqb6": "deliveryDate"
+        "textField_lnmyiqb6": "deliveryDate",
+        "textField_lu12hl9n": "circleRate"
       }
     }
   },

+ 1 - 1
mjava-kuaikeli/src/main/java/com/malk/kuaikeli/service/impl/KKLImplService.java

@@ -125,7 +125,7 @@ public class KKLImplService implements KKLService {
 
             List<Map> materials = supperMaterials(supperCode, types.get(i));
             List<Map> records = projectMaterials(supperCode, types.get(i));
-
+            log.info("定价单同步, {}, {}", materials.size(), records.size());
             for (Map material : materials) {
 
                 // 数据处理: 服务费率与供货价, 项目信息

+ 9 - 8
mjava/src/main/java/com/malk/service/teambition/TBClient.java

@@ -7,6 +7,9 @@ public interface TBClient {
 
     /**
      * 查询企业项目模板
+     * - ppExt:
+     * 1. 编辑模板时, 点击左下角"模板信息", 可在请求内看到其ID. 若是需要绑定固定模板ID, 无需通过后端接口获取
+     * 2. 另外若是扩展了任务详情加载回调, 通过任务查询 taskId 查询到 project 实际为 editStateProjectId [该字段接口文档有但未返回]
      *
      * @param ptIds 项目模版ID集合,逗号组合
      * @param q     模糊查询名字
@@ -94,7 +97,7 @@ public interface TBClient {
     List<Map> queryProjectRoles(String projectId);
 
     /**
-     * 修改项目成员的角色
+     * 修改项目成员的角色 ppExt: 新增场景下 userId 可以为钉钉用户ID, 但修改场景下, 需要传递TB用户ID
      *
      * @param roleIds 角色ID列表 [通过 获取项目角色列表 获取]
      * @return { result:  { role } }   项目角色等级: 0=项目成员, 1=项目管理员, 2=项目拥有者, -3:任务访客
@@ -152,9 +155,8 @@ public interface TBClient {
 
     /**
      * 更新任务自定义字段值
-     * -
-     * ppExt:
-     * 1. 自定义字段传入name会自动匹配
+     * - ppExt:
+     * 1. 自定义字段传入name会自动匹配, 且通过接口更新单行文本也可放入链接
      * 2. 未匹配到返回400; 若是下拉框不在下拉选项也可赋值, 修改后选项消失. 注意name是必填
      *
      * @apiNote https://open.teambition.com/docs/apis/6321c6d2912d20d3b5a4a579
@@ -163,9 +165,8 @@ public interface TBClient {
 
     /**
      * 更新任务自定义字段值
-     * -
-     * ppExt:
-     * 1. 自定义字段传入name会自动匹配
+     * - ppExt:
+     * 1. 自定义字段传入name会自动匹配, 且通过接口更新单行文本也可放入链接
      * 2. 未匹配到返回400; 若是下拉框不在下拉选项也可赋值, 修改后选项消失. 注意name是必填
      *
      * @apiNote https://open.teambition.com/docs/apis/6321c6d2912d20d3b5a4a579
@@ -231,7 +232,7 @@ public interface TBClient {
 
     /**
      * 查询全部数据 [函数回调]
-     * - ppExt -
+     * - ppExt
      * 1. 若只需查询单页数据, lambda 传入 null 即可
      * 2. 在回调内独立处理每批数据场景, 若是需要等待全部数据查询完再处理场景, 可以定义集合接收
      * 2. 若是在回调内已拿到了符合预期的数据, 可返回 null, 触发递归出口 [布尔值 isAll 状态]