DDImplService.java 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package com.malk.service.dingtalk.impl;
  2. import cn.hutool.core.util.ObjectUtil;
  3. import com.malk.server.common.FilePath;
  4. import com.malk.server.dingtalk.DDConf;
  5. import com.malk.server.dingtalk.DDConfigSign;
  6. import com.malk.server.dingtalk.DDR_New;
  7. import com.malk.service.dingtalk.*;
  8. import com.malk.utils.UtilFile;
  9. import com.malk.utils.UtilHttp;
  10. import com.malk.utils.UtilList;
  11. import com.malk.utils.UtilMap;
  12. import lombok.SneakyThrows;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.scheduling.annotation.Async;
  16. import org.springframework.stereotype.Service;
  17. import java.io.File;
  18. import java.util.*;
  19. import java.util.concurrent.atomic.AtomicInteger;
  20. import java.util.concurrent.atomic.AtomicReference;
  21. import java.util.stream.Collectors;
  22. @Service
  23. @Slf4j
  24. public class DDImplService implements DDService {
  25. @Autowired
  26. private DDClient_Workflow ddClient_workflow;
  27. @Autowired
  28. private DDClient_Attendance ddClient_attendance;
  29. @Autowired
  30. private DDClient_Contacts ddClient_contacts;
  31. @Autowired
  32. private DDClient_Storage ddClient_storage;
  33. @Autowired
  34. private FilePath filePath;
  35. @Autowired
  36. private DDConf ddConf;
  37. @Autowired
  38. private DDClient ddClient;
  39. /**
  40. * 新发起审批15s内不允许撤销, 异步执行 [审批同意/拒绝只能通过节点操作, 系统无法直接介入] -- 异步需要中转一层进行触发, client为原子接口
  41. */
  42. @Async
  43. @Override
  44. @SneakyThrows
  45. public void terminateRunningApprove_async(String access_token, String processInstanceId, boolean isSystem, String subject, String description, String operatingUserId, String noteUserId) {
  46. ddClient_workflow.createTBTask(access_token, noteUserId, subject, description, new Date().getTime() + 16000, Arrays.asList(noteUserId), null, null, false, 20, null);
  47. Thread.sleep(16000);
  48. ddClient_workflow.terminateRunningApprove(access_token, processInstanceId, isSystem, description, operatingUserId);
  49. }
  50. /**
  51. * 钉钉查询假期余额返回是记录, 按照消失/天单位计算该假期类型下的余额
  52. */
  53. @Override
  54. public Map queryVacationQuota_balance(String access_token, String op_userid, String leave_code, String userids, int offset, int size) {
  55. List<Map> records = ddClient_attendance.queryVacationQuota_all(access_token, op_userid, leave_code, userids, offset, size);
  56. if (ObjectUtil.isNull(records)) {
  57. return null;
  58. }
  59. AtomicInteger balance = new AtomicInteger();
  60. AtomicReference<String> unit = new AtomicReference<>("");
  61. records.forEach(item -> {
  62. if (ObjectUtil.isNotNull(item.get("quota_num_per_hour"))) {
  63. balance.set(balance.get() + Integer.valueOf(String.valueOf(item.get("quota_num_per_hour"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_hour"))));
  64. unit.set("小时");
  65. } else {
  66. balance.set(balance.get() + (Integer.valueOf(String.valueOf(item.get("quota_num_per_day"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_day")))));
  67. unit.set("天");
  68. }
  69. });
  70. Map result = new HashMap();
  71. result.put("balance", balance.get() / 100f);
  72. result.put("unit", unit);
  73. return result;
  74. }
  75. /**
  76. * 上传审批附件
  77. *
  78. * @param urlFile 远程文件地址
  79. * @param fileNamePure 文件名称[后缀自动通过地址获取]
  80. */
  81. @Override
  82. public Map uploadFileFormUrl(String accessToken, String userId, String urlFile, String fileNamePure) {
  83. // 下载到本地
  84. String fileName = fileNamePure + urlFile.substring(urlFile.lastIndexOf(".")).toLowerCase();
  85. File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getFile());
  86. UtilHttp.doDownload(urlFile, file);
  87. return uploadFileFormUrl(accessToken, userId, file.getAbsolutePath());
  88. }
  89. /**
  90. * 上传审批附件
  91. *
  92. * @param filePath 已存在文件, 本地file绝对路径
  93. */
  94. @Override
  95. public Map uploadFileFormUrl(String accessToken, String userId, String filePath) {
  96. // 获取用户unionId
  97. String unionId = String.valueOf(ddClient_contacts.getUserInfoById(accessToken, userId).get("unionid"));
  98. // 获取储存空间
  99. String spaceId = ddClient_storage.getSpacesInfos(accessToken, userId, null);
  100. // 添加空间用户权限
  101. List<Map> members = Arrays.asList(UtilMap.map("type, id, corpId", "USER", unionId, ddConf.getCorpId()));
  102. ddClient_storage.addSpacePermissions(accessToken, spaceId, "0", unionId, "EDITOR", members, null);
  103. // 获取空间上传信息
  104. DDR_New ddr_new = ddClient_storage.getUploadInfos(accessToken, spaceId, unionId, null);
  105. // 执行文件上传
  106. String resourceUrl = ((List<String>) ddr_new.getHeaderSignatureInfo().get("resourceUrls")).get(0);
  107. ddClient_storage.uploadFiles(resourceUrl, (Map<String, String>) ddr_new.getHeaderSignatureInfo().get("headers"), filePath);
  108. // 提交文件
  109. return ddClient_storage.commitFiles(accessToken, spaceId, unionId, ddr_new.getUploadKey(), String.valueOf(UtilList.getLast(filePath.split("/"))), "0", null);
  110. }
  111. /**
  112. * 判断员工是否在指定部门
  113. */
  114. @Override
  115. public boolean matchDepartment(String access_token, String userId, List<Long> deptIds) {
  116. List<Number> deptIdList = (List<Number>) ddClient_contacts.getUserInfoById(access_token, userId).get("dept_id_list");
  117. boolean isMatch = false;
  118. // 兼容多部门场景
  119. for (Number deptId : deptIdList) {
  120. // ppExt: 不要直接使用 Number 作为类型, 不同基本类型比较值时会有偏差 [可以作为父类接受数据, 避免直接强制类型转换错误, 再进行基本类型处理]
  121. isMatch = _matchDepartment(access_token, deptId.longValue(), deptIds);
  122. if (isMatch) {
  123. break;
  124. }
  125. }
  126. return isMatch;
  127. }
  128. /// 递归: 判断员工是否在指定部门
  129. boolean _matchDepartment(String access_token, long dept_id, List<Long> deptIds) {
  130. boolean isMatch = false;
  131. // 判断入参 [同样作为递归出口]
  132. if (dept_id == DDConf.TOP_DEPARTMENT) {
  133. return false;
  134. }
  135. if (deptIds.contains(dept_id)) {
  136. isMatch = true;
  137. }
  138. // 递归出口 [查询上级部门匹配]
  139. if (!isMatch) {
  140. Map deptInfo = ddClient_contacts.getDepartmentInfo(access_token, dept_id);
  141. long parentId = UtilMap.getLong(deptInfo, "parent_id");
  142. return _matchDepartment(access_token, parentId, deptIds);
  143. }
  144. return isMatch;
  145. }
  146. /**
  147. * 获取员工所属部门全路径
  148. */
  149. @Override
  150. public List<Map> getUserDepartmentHierarchy(String access_token, String userId) {
  151. // PRD: 一个人存在多个部门默认取第一个
  152. List<Number> deptIdList = (List<Number>) ((List<Map>) ddClient_contacts.listParentByUser(access_token, userId).get("parent_list")).get(0).get("parent_dept_id_list");
  153. List<Map> deptInfo = new ArrayList();
  154. // Number 仅仅作为数据类型声明, 避免比较类型不一致导致判定问题
  155. for (Number deptId : deptIdList) {
  156. if (deptId.longValue() == DDConf.TOP_DEPARTMENT) {
  157. continue;
  158. }
  159. deptInfo.add(UtilMap.map("id, name", deptId, ddClient_contacts.getDepartmentInfo(access_token, deptId.longValue()).get("name")));
  160. }
  161. Collections.reverse(deptInfo);
  162. return deptInfo;
  163. }
  164. /**
  165. * 获取员工所属部门路径层级 [名称拼接]
  166. */
  167. @Override
  168. public String getUserDepartmentHierarchyJoin(String access_token, String userId, String delimiter) {
  169. return String.join(delimiter, getUserDepartmentHierarchy(access_token, userId).stream().map(dept -> dept.get("name").toString()).collect(Collectors.toList()));
  170. }
  171. /**
  172. * jsApi 注册
  173. */
  174. @Override
  175. public Map registerJsApi(String url, String nonceStr) {
  176. String jsTicket = ddClient.getJsApiTicket(ddClient.getAccessToken());
  177. long timeStamp = new Date().getTime();
  178. String signature = DDConfigSign.sign(jsTicket, nonceStr, timeStamp, url);
  179. return UtilMap.map("nonceStr, agentId, timeStamp, corpId, signature", nonceStr, ddConf.getAgentId(), timeStamp, ddConf.getCorpId(), signature);
  180. }
  181. /**
  182. * 免登code获取用户信息
  183. */
  184. @Override
  185. public Map getUserInfoByCode(String code) {
  186. Map rsp = ddClient.getUserInfoByCode(ddClient.getAccessToken(), code);
  187. return ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), rsp.get("userid").toString());
  188. }
  189. }