package com.malk.service.dingtalk.impl; import cn.hutool.core.util.ObjectUtil; import com.malk.server.common.FilePath; import com.malk.server.dingtalk.DDConf; import com.malk.server.dingtalk.DDConfigSign; import com.malk.server.dingtalk.DDR_New; import com.malk.service.dingtalk.*; import com.malk.utils.UtilFile; import com.malk.utils.UtilHttp; import com.malk.utils.UtilList; import com.malk.utils.UtilMap; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.io.File; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @Service @Slf4j public class DDImplService implements DDService { @Autowired private DDClient_Workflow ddClient_workflow; @Autowired private DDClient_Attendance ddClient_attendance; @Autowired private DDClient_Contacts ddClient_contacts; @Autowired private DDClient_Storage ddClient_storage; @Autowired private FilePath filePath; @Autowired private DDConf ddConf; @Autowired private DDClient ddClient; /** * 新发起审批15s内不允许撤销, 异步执行 [审批同意/拒绝只能通过节点操作, 系统无法直接介入] -- 异步需要中转一层进行触发, client为原子接口 */ @Async @Override @SneakyThrows public void terminateRunningApprove_async(String access_token, String processInstanceId, boolean isSystem, String subject, String description, String operatingUserId, String noteUserId) { ddClient_workflow.createTBTask(access_token, noteUserId, subject, description, new Date().getTime() + 16000, Arrays.asList(noteUserId), null, null, false, 20, null); Thread.sleep(16000); ddClient_workflow.terminateRunningApprove(access_token, processInstanceId, isSystem, description, operatingUserId); } /** * 钉钉查询假期余额返回是记录, 按照消失/天单位计算该假期类型下的余额 */ @Override public Map queryVacationQuota_balance(String access_token, String op_userid, String leave_code, String userids, int offset, int size) { List records = ddClient_attendance.queryVacationQuota_all(access_token, op_userid, leave_code, userids, offset, size); if (ObjectUtil.isNull(records)) { return null; } AtomicInteger balance = new AtomicInteger(); AtomicReference unit = new AtomicReference<>(""); records.forEach(item -> { if (ObjectUtil.isNotNull(item.get("quota_num_per_hour"))) { balance.set(balance.get() + Integer.valueOf(String.valueOf(item.get("quota_num_per_hour"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_hour")))); unit.set("小时"); } else { balance.set(balance.get() + (Integer.valueOf(String.valueOf(item.get("quota_num_per_day"))) - Integer.valueOf(String.valueOf(item.get("used_num_per_day"))))); unit.set("天"); } }); Map result = new HashMap(); result.put("balance", balance.get() / 100f); result.put("unit", unit); return result; } /** * 上传审批附件 * * @param urlFile 远程文件地址 * @param fileNamePure 文件名称[后缀自动通过地址获取] */ @Override public Map uploadFileFormUrl(String accessToken, String userId, String urlFile, String fileNamePure) { // 下载到本地 String fileName = fileNamePure + urlFile.substring(urlFile.lastIndexOf(".")).toLowerCase(); File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getFile()); UtilHttp.doDownload(urlFile, file); return uploadFileFormUrl(accessToken, userId, file.getAbsolutePath()); } /** * 上传审批附件 * * @param filePath 已存在文件, 本地file绝对路径 */ @Override public Map uploadFileFormUrl(String accessToken, String userId, String filePath) { // 获取用户unionId String unionId = String.valueOf(ddClient_contacts.getUserInfoById(accessToken, userId).get("unionid")); // 获取储存空间 String spaceId = ddClient_storage.getSpacesInfos(accessToken, userId, null); // 添加空间用户权限 List members = Arrays.asList(UtilMap.map("type, id, corpId", "USER", unionId, ddConf.getCorpId())); ddClient_storage.addSpacePermissions(accessToken, spaceId, "0", unionId, "EDITOR", members, null); // 获取空间上传信息 DDR_New ddr_new = ddClient_storage.getUploadInfos(accessToken, spaceId, unionId, null); // 执行文件上传 String resourceUrl = ((List) ddr_new.getHeaderSignatureInfo().get("resourceUrls")).get(0); ddClient_storage.uploadFiles(resourceUrl, (Map) ddr_new.getHeaderSignatureInfo().get("headers"), filePath); // 提交文件 return ddClient_storage.commitFiles(accessToken, spaceId, unionId, ddr_new.getUploadKey(), String.valueOf(UtilList.getLast(filePath.split("/"))), "0", null); } /** * 判断员工是否在指定部门 */ @Override public boolean matchDepartment(String access_token, String userId, List deptIds) { List deptIdList = (List) ddClient_contacts.getUserInfoById(access_token, userId).get("dept_id_list"); boolean isMatch = false; // 兼容多部门场景 for (Number deptId : deptIdList) { // ppExt: 不要直接使用 Number 作为类型, 不同基本类型比较值时会有偏差 [可以作为父类接受数据, 避免直接强制类型转换错误, 再进行基本类型处理] isMatch = _matchDepartment(access_token, deptId.longValue(), deptIds); if (isMatch) { break; } } return isMatch; } /// 递归: 判断员工是否在指定部门 boolean _matchDepartment(String access_token, long dept_id, List deptIds) { boolean isMatch = false; // 判断入参 [同样作为递归出口] if (dept_id == DDConf.TOP_DEPARTMENT) { return false; } if (deptIds.contains(dept_id)) { isMatch = true; } // 递归出口 [查询上级部门匹配] if (!isMatch) { Map deptInfo = ddClient_contacts.getDepartmentInfo(access_token, dept_id); long parentId = UtilMap.getLong(deptInfo, "parent_id"); return _matchDepartment(access_token, parentId, deptIds); } return isMatch; } /** * 获取员工所属部门全路径 */ @Override public List getUserDepartmentHierarchy(String access_token, String userId) { // PRD: 一个人存在多个部门默认取第一个 List deptIdList = (List) ((List) ddClient_contacts.listParentByUser(access_token, userId).get("parent_list")).get(0).get("parent_dept_id_list"); List deptInfo = new ArrayList(); // Number 仅仅作为数据类型声明, 避免比较类型不一致导致判定问题 for (Number deptId : deptIdList) { if (deptId.longValue() == DDConf.TOP_DEPARTMENT) { continue; } deptInfo.add(UtilMap.map("id, name", deptId, ddClient_contacts.getDepartmentInfo(access_token, deptId.longValue()).get("name"))); } Collections.reverse(deptInfo); return deptInfo; } /** * 获取员工所属部门路径层级 [名称拼接] */ @Override public String getUserDepartmentHierarchyJoin(String access_token, String userId, String delimiter) { return String.join(delimiter, getUserDepartmentHierarchy(access_token, userId).stream().map(dept -> dept.get("name").toString()).collect(Collectors.toList())); } /** * jsApi 注册 */ @Override public Map registerJsApi(String url, String nonceStr) { String jsTicket = ddClient.getJsApiTicket(ddClient.getAccessToken()); long timeStamp = new Date().getTime(); String signature = DDConfigSign.sign(jsTicket, nonceStr, timeStamp, url); return UtilMap.map("nonceStr, agentId, timeStamp, corpId, signature", nonceStr, ddConf.getAgentId(), timeStamp, ddConf.getCorpId(), signature); } /** * 免登code获取用户信息 */ @Override public Map getUserInfoByCode(String code) { Map rsp = ddClient.getUserInfoByCode(ddClient.getAccessToken(), code); return ddClient_contacts.getUserInfoById(ddClient.getAccessToken(), rsp.get("userid").toString()); } }