|
@@ -9,9 +9,13 @@ import com.malk.server.aliwork.YDConf;
|
|
|
import com.malk.server.aliwork.YDParam;
|
|
|
import com.malk.server.common.McException;
|
|
|
import com.malk.server.dingtalk.DDConf;
|
|
|
+import com.malk.server.dingtalk.DDR_New;
|
|
|
import com.malk.server.teambition.TBConf;
|
|
|
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_Storage;
|
|
|
import com.malk.service.teambition.TBClient;
|
|
|
import com.malk.utils.*;
|
|
|
import lombok.Synchronized;
|
|
@@ -43,6 +47,7 @@ public class AWImplClient implements AWClint {
|
|
|
String _matchFormUuid(String code) {
|
|
|
Map<String, String> formUuid = UtilMap.empty();
|
|
|
if (true || UtilEnv.getActiveProfile().equals(UtilEnv.ENV_PROD)) {
|
|
|
+ formUuid.put("DENTRY", "FORM-BD73A57B62EA4153B896C9BB3EA14D28GWSQ"); // 文件夹/文件版本记录
|
|
|
formUuid.put("REVIEW", "FORM-812FD46AF391449A8F206EDB3221B38840UQ"); // 交付物审批记录
|
|
|
formUuid.put("REVIEW_PROCESS", "TPROC--RJC66SC1NEFHXJ0H770K0CF4WN1K21HQ706RL5"); // 交付物审批记录
|
|
|
formUuid.put("PROJECT", "FORM-141E21DF183846028E21727CE43CD1C75CLZ"); // 项目主数据
|
|
@@ -252,9 +257,11 @@ public class AWImplClient implements AWClint {
|
|
|
|
|
|
/**
|
|
|
* 获取知识库附件传递到宜搭
|
|
|
- * - ppExt -
|
|
|
+ * -
|
|
|
* 1. 宜搭附件传递 downloadUrl 和 name 即可实现在线预览
|
|
|
* 2. 知识库绑定没有文件后缀, 宜搭识别目前仅能点击下载跳转预览, 添加统一docx后缀, 文档会自行区分
|
|
|
+ * -
|
|
|
+ * ppExt: 获取任务字段需要传递 AWServer.TASK_APPROVE_ATTACHMENT 以获取ID, 另外添加 .doc 只是重定向若是文件夹也不影响访问
|
|
|
*/
|
|
|
List<Map> _getDocs(Map taskData) {
|
|
|
Map rTask = UtilMap.getMap(taskData, "task");
|
|
@@ -262,7 +269,9 @@ public class AWImplClient implements AWClint {
|
|
|
String deliverable = String.valueOf(taskData.get(AWServer.TASK_APPROVE_ATTACHMENT + "_id")); // 交付物任务字段ID
|
|
|
List<Map> attachments = TBConf.getTaskFieldValue(customfields, deliverable).stream().map(item -> {
|
|
|
Map link = (Map) JSON.parse(String.valueOf(item.get("metaString")));
|
|
|
- return UtilMap.map("downloadUrl, name", link.get("url"), link.get("title") + ".docx");
|
|
|
+ Map row = UtilMap.map("downloadUrl, name", link.get("url"), link.get("title") + ".docx");
|
|
|
+ row.putAll(link); // 保留原数据作为知识库版本管理相关参数
|
|
|
+ return row;
|
|
|
}).collect(Collectors.toList());
|
|
|
return attachments;
|
|
|
}
|
|
@@ -605,12 +614,12 @@ public class AWImplClient implements AWClint {
|
|
|
tbClient.updateProjectStatusField(projectId, tbConf.getOperatorId(), TBConf.assembleCustomFieldName("项目重要等级", UtilMap.getString(formData, "textField_lwj1r7n6")));
|
|
|
}
|
|
|
// 项目添加到项目集
|
|
|
- List<Map> groupList = tbClient.queryProgramList_all();
|
|
|
- Optional optional = groupList.stream().filter(item -> groupName.equals(item.get("name"))).findAny();
|
|
|
- if (optional.isPresent()) {
|
|
|
- tbClient.upsertProgramProject(UtilMap.getString((Map) optional.get(), "id"), Arrays.asList(projectId), tbConf.getOperatorId());
|
|
|
- }
|
|
|
-
|
|
|
+// List<Map> groupList = tbClient.queryProgramList_all();
|
|
|
+// Optional optional = groupList.stream().filter(item -> groupName.equals(item.get("name"))).findAny();
|
|
|
+// if (optional.isPresent()) {
|
|
|
+// tbClient.upsertProgramProject(UtilMap.getString((Map) optional.get(), "id"), Arrays.asList(projectId), tbConf.getOperatorId());
|
|
|
+// }
|
|
|
+
|
|
|
log.info("TB项目信息, {}, {}", projectId, roleIds.size());
|
|
|
}
|
|
|
|
|
@@ -1078,6 +1087,225 @@ public class AWImplClient implements AWClint {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private DDClient ddClient;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DDClient_Contacts ddClient_contacts;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DDClient_Storage ddClient_storage;
|
|
|
+
|
|
|
+ /// fixme: 文件/文件夹名称若重复会自动添加(1)\(2)\..., 目前通过自建文件夹/文件数据表控制 [fileType - 文件/文件夹]
|
|
|
+ private Map _upsertFileAbsolutePath(String fileType, String fileName, String pCode, String pId, String workspaceId, String rootNodeId, String unionId) {
|
|
|
+
|
|
|
+ // 避免模糊匹配, 知识库ID + 文件/文件夹名称全路径
|
|
|
+ List<Map> conditions = new ArrayList<>();
|
|
|
+ Map work = new HashMap();
|
|
|
+ work.put("key", "textField_lx2yekqj");
|
|
|
+ work.put("value", workspaceId);
|
|
|
+ work.put("type", "TEXT");
|
|
|
+ work.put("operator", "eq");
|
|
|
+ work.put("componentName", "TextField");
|
|
|
+ conditions.add(work);
|
|
|
+ Map file = new HashMap();
|
|
|
+ file.put("key", "textField_lx36gdpp");
|
|
|
+ file.put("value", fileName);
|
|
|
+ file.put("type", "TEXT");
|
|
|
+ file.put("operator", "eq");
|
|
|
+ file.put("componentName", "TextField");
|
|
|
+ conditions.add(file);
|
|
|
+
|
|
|
+ List<Map> dataList = (List<Map>) ydClient.queryData(YDParam.builder()
|
|
|
+ .formUuid(_matchFormUuid("DENTRY"))
|
|
|
+ .searchCondition(JSON.toJSONString(conditions))
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_list).getData();
|
|
|
+ String formInstId = "";
|
|
|
+ int version = 0;
|
|
|
+ if (dataList.isEmpty()) {
|
|
|
+ Map formData = UtilMap.map("radioField_lx36gdpm, textField_lx36gdpp, textField_lrj7vnxb, textField_lqxtykce", fileType, fileName, pCode, pId);
|
|
|
+ formData.put("textField_lx2yekqj", workspaceId);
|
|
|
+ if ("文件夹".equals(fileType)) {
|
|
|
+ if (fileName.contains("/")) {
|
|
|
+ fileName = String.valueOf(UtilList.getLast(fileName.split("/")));
|
|
|
+ }
|
|
|
+ DDR_New ddr_new = ddClient_storage.createFolder(ddClient.getAccessToken(), unionId, workspaceId, fileName, "FOLDER", rootNodeId, null);
|
|
|
+ rootNodeId = ddr_new.getNodeId(); // ppExt: 文件夹创后, 更新 rootNodeId, 用于创建下级目录
|
|
|
+ formData.put("textField_lx36gdpn", ddr_new.getDentryUuid());
|
|
|
+ formData.put("textField_lx36gdpo", ddr_new.getUrl());
|
|
|
+ formData.put("textField_lx342bvd", rootNodeId);
|
|
|
+ } else {
|
|
|
+ formData.put("numberField_lx36gdpz", version);
|
|
|
+ }
|
|
|
+ formInstId = (String) ydClient.operateData(YDParam.builder()
|
|
|
+ .formUuid(_matchFormUuid("DENTRY"))
|
|
|
+ .formDataJson(JSON.toJSONString(formData))
|
|
|
+ .build(), YDConf.FORM_OPERATION.create);
|
|
|
+ } else {
|
|
|
+ formInstId = UtilMap.getString(dataList.get(0), "formInstanceId");
|
|
|
+ rootNodeId = UtilMap.getString(UtilMap.getMap(dataList.get(0), "formData"), "textField_lx342bvd");
|
|
|
+ version = UtilMap.getInt(UtilMap.getMap(dataList.get(0), "formData"), "numberField_lx36gdpz");
|
|
|
+ }
|
|
|
+ // 返回 formInstId 用于记录文件版本号, 以及复制后文件访问链接
|
|
|
+ return UtilMap.map("rootNodeId, formInstId, version", rootNodeId, formInstId, version);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 知识库复制操作 sop [ fixme: 文件复制后, 变更记录不会保存 ]
|
|
|
+ * -
|
|
|
+ * 1. TB 任务上未返回真实 workspaceId
|
|
|
+ * - 现状: 返回的 WorkspaceId 其实是 spaceId, instanceId 其实是 dentryId.
|
|
|
+ * - 临时: 知识库文件/文件夹复制接口需要传递来源 workplaceId, 但测试下来传递目标 workplaceId 也可以, 包括从我的文档内拷贝均验证通过
|
|
|
+ * - 解法: 官方已做了兼容
|
|
|
+ * -
|
|
|
+ * 2. TB 任务上返回链接格式不一致
|
|
|
+ * - 原因: 知识库复制需要同时有文件与\复制目标文件夹下编辑权限. 目前外部使用不能提供公共操作账号, 只能通过 dentryUuid 给来源文件添加编辑权限, 再进行复制操作
|
|
|
+ * - 现状: 从最近浏览选择的文件, 地址会被转换, 点击以后会重定向到知识库真实地址, 地址带有 node 与 dentryUuid
|
|
|
+ * - 官方: 文档选择器 有最近浏览列表 以及 选知识库后的目录树,背后都是对接的钉钉文档开放API,不过前者是V1版本的,后者是V2版本的,文档他们好像又出了V3版本,总之不同版本接口返回的链接可能是不一样的
|
|
|
+ * - 临时: 目前控制用户不能选择最近浏览
|
|
|
+ * - 建议: 权限控制需要通过 dentryUuid, TB是否可反回, 或可提供通过 dentry 查询 dentryUuid接口; 或者使用现有链接反查接口, 是否确保链接格式内包含 dentryUuid [URL下node后字符串]
|
|
|
+ * - 解法: 提供通过 spaceId + dentryId 查询详情接口, 返回有创建人\dentryUuid信息
|
|
|
+ * -
|
|
|
+ * 3. 通过接口添加知识库权限操作人需要有权限
|
|
|
+ * 说明: 详见2 [fixme: 公共账号拥有所有项目知识库的编辑以上权限]
|
|
|
+ * 解法: 通过提供通过 spaceId + dentryId 查询创建人, 再由创建人为公共账号添加编辑权限, 再通过公共账号复制 [ppExt: 知识库可为其他成员添加不高于自身的权限]
|
|
|
+ * -
|
|
|
+ * 4. 需要通过模板自动创建知识库, TB 134版本已支持
|
|
|
+ * 1. 调整: fixme 知识库列表接口, 查询受到 operatorId 权限控制, 仅会返回操作者有权限知识库
|
|
|
+ * 2. 方案: 依赖同3, 公共账号 TBManager 默认是项目拥有者, 会默认同步到知识库成员, 查询权限后通过知识库创建者添加编辑, 用于复制文件 [ppExt: 直接添加知识库根节点 rootNodeId 编辑权限]
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void approveVersion(String taskId, String pCode) {
|
|
|
+
|
|
|
+ if (!"A240407DryRun".equals(pCode)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String result = "";
|
|
|
+
|
|
|
+ // 通用账户unionId
|
|
|
+ String unionId = String.valueOf(ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), ddConf.getOperator()).get("unionid"));
|
|
|
+
|
|
|
+ // 知识库 & 项目信息处理
|
|
|
+ String workspaceId = "", rootNodeId = "", creatorId = "";
|
|
|
+ List<Map> pList = ydService.queryDataList_FormData(_matchFormUuid("PROJECT"), UtilMap.map("textField_lrj7vnxb", pCode));
|
|
|
+ pList = pList.stream().filter(item -> pCode.equals(item.get("textField_lrj7vnxb"))).collect(Collectors.toList());
|
|
|
+ if (pList.isEmpty()) {
|
|
|
+ result = "未匹配到项目主数据";
|
|
|
+ } else {
|
|
|
+ workspaceId = UtilMap.getString(pList.get(0), "textField_lx2yekqj");
|
|
|
+ rootNodeId = UtilMap.getString(pList.get(0), "textField_lx342bvd");
|
|
|
+ creatorId = UtilMap.getString(pList.get(0), "textField_lx8ra1bs");
|
|
|
+ if (StringUtils.isBlank(workspaceId)) {
|
|
|
+ // 获取知识库空间Id [ ppExt: 知识库列表接口, 查询受到 operatorId 权限控制, 仅会返回操作者有权限知识库 ]
|
|
|
+ List<Map> workspaces = ddClient_storage.searchWorkspaces(ddClient.getAccessToken(), unionId, pCode, null);
|
|
|
+ if (workspaces.isEmpty()) {
|
|
|
+ result = "项目知识库未找到";
|
|
|
+ } else {
|
|
|
+ workspaceId = UtilMap.getString(workspaces.get(0), "workspaceId");
|
|
|
+ Map space = ddClient_storage.getWorkspaceDetail(ddClient.getAccessToken(), unionId, workspaceId, false);
|
|
|
+ rootNodeId = UtilMap.getString(space, "rootNodeId"); // 知识库返回根节点 rootNodeId, 即根目录的 dentryUuid
|
|
|
+ creatorId = UtilMap.getString(space, "creatorId");
|
|
|
+ // 更新知识库信息到项目档案
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .formInstanceId(UtilMap.getString(pList.get(0), "instanceId"))
|
|
|
+ .updateFormDataJson(JSON.toJSONString(UtilMap.map("textField_lx2yekqj, textField_lx342bvd, textField_lx8ra1bs, textField_lx8ra1bt", workspaceId, rootNodeId, creatorId, space.get("url"))))
|
|
|
+ .build(), YDConf.FORM_OPERATION.update);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 权限 & 文件复制 & 版本管理
|
|
|
+ if (StringUtils.isBlank(result)) {
|
|
|
+ try {
|
|
|
+ // 知识库返回根节点 rootNodeId, 即根目录的 dentryUuid, 可添加整个知识库编辑权限, 通过知识库创建者赋权
|
|
|
+ List<Map> members = Arrays.asList(UtilMap.map("type, id", "USER", ddConf.getOperator()));
|
|
|
+ creatorId = String.valueOf(ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), creatorId).get("unionid"));
|
|
|
+ ddClient_storage.updateDentryPermissions(ddClient.getAccessToken(), rootNodeId, creatorId, "EDITOR", members, null);
|
|
|
+ } catch (McException e) {
|
|
|
+ // ppExt: 若是创建人赋值编辑权限, 也会是500异常
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ // 知识库文件夹 & 拷贝处理 [prd 文件目录结构 TR评审节点/资源名称]
|
|
|
+ Map taskData = _getTaskFieldMap(taskId, AWServer.TASK_APPROVE_ATTACHMENT, AWServer.TASK_STAGE, AWServer.TASK_ROLE);
|
|
|
+ Map rTask = UtilMap.getMap(taskData, "task");
|
|
|
+
|
|
|
+ // 文件/文件夹版本控制: fileType, fileName, pCode, pId, workspaceId, rootNodeId, unionId
|
|
|
+ String fileName = "项目交付件";
|
|
|
+ Map record = _upsertFileAbsolutePath("文件夹", fileName, pCode, UtilMap.getString(rTask, "projectId"), workspaceId, rootNodeId, unionId);
|
|
|
+ rootNodeId = UtilMap.getString(record, "rootNodeId"); // 最新文件路径下ID
|
|
|
+
|
|
|
+ String rName = UtilMap.getString(taskData, AWServer.TASK_STAGE);
|
|
|
+ if (StringUtils.isBlank(rName)) {
|
|
|
+ rName = "其他";
|
|
|
+ }
|
|
|
+ fileName = fileName + "/" + rName;
|
|
|
+ record = _upsertFileAbsolutePath("文件夹", fileName, pCode, UtilMap.getString(rTask, "projectId"), workspaceId, rootNodeId, unionId);
|
|
|
+ rootNodeId = UtilMap.getString(record, "rootNodeId"); // 最新文件路径下ID
|
|
|
+
|
|
|
+ String tName = UtilMap.getString(taskData, AWServer.TASK_ROLE);
|
|
|
+ if (StringUtils.isBlank(tName)) {
|
|
|
+ tName = "其他";
|
|
|
+ }
|
|
|
+ fileName = fileName + "/" + tName;
|
|
|
+ record = _upsertFileAbsolutePath("文件夹", fileName, pCode, UtilMap.getString(rTask, "projectId"), workspaceId, rootNodeId, unionId);
|
|
|
+ rootNodeId = UtilMap.getString(record, "rootNodeId"); // 最新文件路径下ID
|
|
|
+
|
|
|
+ List<Map> docs = _getDocs(taskData);
|
|
|
+ log.info("docs, {}", docs);
|
|
|
+ List<Map> attas = new ArrayList<>();
|
|
|
+ for (Map doc : docs) {
|
|
|
+
|
|
|
+ // 文件/文件夹版本控制: fileType, fileName, pCode, pId, workspaceId, rootNodeId, unionId
|
|
|
+ String title = UtilMap.getString(doc, "title");
|
|
|
+ fileName = fileName + "/" + title;
|
|
|
+ Map version = _upsertFileAbsolutePath("文件", fileName, pCode, UtilMap.getString(rTask, "projectId"), workspaceId, rootNodeId, unionId);
|
|
|
+
|
|
|
+ // 通过提供通过 spaceId + dentryId 查询创建人, 再由创建人为公共账号添加编辑权限, 再通过公共账号复制 [ppExt: 知识库可为其他成员添加不高于自身的权限]
|
|
|
+ DDR_New ddr_dentry = ddClient_storage.getSpaceDentryDetail(ddClient.getAccessToken(), unionId, UtilMap.getString(doc, "instanceId"), UtilMap.getString(doc, "instanceId"), false);
|
|
|
+ try {
|
|
|
+ String creatorId_t = UtilMap.getString(ddr_dentry.getUpdater(), "unionId");
|
|
|
+ List<Map> members_t = Arrays.asList(UtilMap.map("type, id", "USER", ddConf.getOperator()));
|
|
|
+ ddClient_storage.updateDentryPermissions(ddClient.getAccessToken(), ddr_dentry.getDentryUuid(), creatorId_t, "EDITOR", members_t, null);
|
|
|
+ } catch (McException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ // ppExt: 若是创建人权限被管理员拿走, 会导致500异常, 需要兼容
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制文件/文件夹, 知识库文件添加版本标识
|
|
|
+ int order = UtilMap.getInt(version, "version");
|
|
|
+ order += 1;
|
|
|
+ String docName = title + " v" + order + ".0";
|
|
|
+ DDR_New ddr_new = ddClient_storage.copyDentries(ddClient.getAccessToken(), unionId, workspaceId, UtilMap.getString(doc, "instanceId"), workspaceId, rootNodeId, docName);
|
|
|
+
|
|
|
+ // 更新最新版本记录, 叠加版本号
|
|
|
+ Map formData = UtilMap.map("textField_lx36gdpp, numberField_lx36gdpz", fileName, order);
|
|
|
+ formData.put("textField_lx342bvd", ddr_new.getDentryId());
|
|
|
+ formData.put("textField_lx36gdpn", ddr_new.getDentryUuid());
|
|
|
+ formData.put("textField_lx36gdpo", ddr_new.getUrl());
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .formInstanceId(UtilMap.getString(version, "formInstId"))
|
|
|
+ .updateFormDataJson(JSON.toJSONString(formData))
|
|
|
+ .build(), YDConf.FORM_OPERATION.update);
|
|
|
+ // 更新版本字段
|
|
|
+ Map meta = UtilMap.map("title, url, instanceId, workspaceId, image", docName, ddr_new.getUrl(), ddr_new.getDentryId(), ddr_new.getSpaceId(), doc.get("image"));
|
|
|
+ attas.add(UtilMap.map("title, metaString", docName, JSON.toJSONString(meta)));
|
|
|
+ }
|
|
|
+ if (attas.isEmpty()) {
|
|
|
+ result = "无编辑权限, 复制失败";
|
|
|
+ } else {
|
|
|
+ // 多文件更新回写 [ fixme: 更新任务自定义字段值 ]
|
|
|
+ tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), UtilMap.map("customfieldName, value", AWServer.TASK_APPROVE_VERSION, attas));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(result)) {
|
|
|
+ Map body = TBConf.assembleCustomFieldName(AWServer.TASK_APPROVE_VERSION, result);
|
|
|
+ tbClient.updateTaskCustomField(taskId, tbConf.getOperatorId(), body);
|
|
|
+ }
|
|
|
+ // todo: 业务待定, 若变更有单独BPM流程, TBx宜搭只是作为归档管理, 不存在变更版本审批拒绝场景
|
|
|
+ log.info("知识库版本管理, {}", result);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public void test() {
|
|
|
|
|
@@ -1123,7 +1351,7 @@ public class AWImplClient implements AWClint {
|
|
|
.build(), YDConf.FORM_OPERATION.update);
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
|
|
|
+
|