RZServiceImpl.java 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package com.malk.rongzhi.service.impl;
  2. import com.malk.Util.UtilDateTime;
  3. import com.malk.Util.UtilHttp;
  4. import com.malk.Util.UtilMap;
  5. import com.malk.rongzhi.repository.dao.RzEkbRecordDao;
  6. import com.malk.rongzhi.repository.entity.RzEkbRecordPo;
  7. import com.malk.rongzhi.server.RZConf;
  8. import com.malk.rongzhi.server.RZR;
  9. import com.malk.rongzhi.service.RZService;
  10. import com.malk.server.dingtalk.DDConf;
  11. import com.malk.server.ekuaibao.EKBR;
  12. import com.malk.service.dingtalk.DDClient_Attendance;
  13. import com.malk.service.dingtalk.DDClient_Contacts;
  14. import com.malk.service.ekuaibao.EKBClient;
  15. import lombok.extern.slf4j.Slf4j;
  16. import org.apache.commons.codec.digest.DigestUtils;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.stereotype.Service;
  19. import java.time.LocalDateTime;
  20. import java.util.*;
  21. import java.util.stream.Collectors;
  22. @Slf4j
  23. @Service
  24. public class RZServiceImpl implements RZService {
  25. @Autowired
  26. private DDClient_Contacts ddClientImpl_contacts;
  27. @Autowired
  28. private DDClient_Attendance ddClient_attendance;
  29. @Autowired
  30. private EKBClient ekbClient;
  31. @Autowired
  32. private RzEkbRecordDao rzEkbRecordDao;
  33. /// 获取调用授权
  34. @Override
  35. public String getAccessToken(RZConf.TYPE type) {
  36. String timestamp = String.valueOf(new Date().getTime());
  37. String signature = DigestUtils.md5Hex("Max" + timestamp + RZConf.NONCE);
  38. Map header = new HashMap();
  39. header.put("timestamp", timestamp);
  40. header.put("nonce", RZConf.NONCE);
  41. header.put("signature", signature);
  42. RZR rsp = (RZR) UtilHttp.doGet("https://one-api.max-digital.cn/v1/token" + type.getPath(), header, null, RZR.class);
  43. return rsp.getData();
  44. }
  45. // 同步考勤数据到e快报
  46. @Override
  47. public void uploadTravelData() {
  48. // 获取用户数据
  49. log.info("###### [DD]开始同步组织架构 ######");
  50. List<String> userIds = new ArrayList<>();
  51. syncOrganizationalStructure(DDConf.TOP_DEPARTMENT, userIds);
  52. log.info("###### [DD]同步组织架构结束 ######");
  53. // 获取用户考勤
  54. for (int i = 0; i < userIds.size(); i += 50) {
  55. int to = (i + 50) > userIds.size() ? userIds.size() : i + 50;
  56. // 考勤范围从当日 7.30 到次日 7.30
  57. Date now = new Date();
  58. String from = UtilDateTime.formatDate(now) + " 07:30:00";
  59. String end = UtilDateTime.formatDate(now) + " 23:59:59";
  60. // fixme: [凌晨以后同步新的考勤时段]
  61. if (LocalDateTime.now().getHour() <= 7) {
  62. from = UtilDateTime.formatDate(now) + " 00:00:00";
  63. end = UtilDateTime.formatDate(now) + " 07:30:00";
  64. }
  65. // String from = "2023-04-18 07:30:00";
  66. // String end = "2023-04-19 07:30:00";
  67. List<String> users = userIds.subList(i, to);
  68. List<Map> list = ddClient_attendance.listAttendanceRecord(getAccessToken(RZConf.TYPE.dingtalk), users, from, end);
  69. // 匹配数据考勤
  70. users.forEach(user -> {
  71. List<Map> records = list.stream().filter(item -> user.equals(item.get("userId"))).collect(Collectors.toList());
  72. // 有下班打卡
  73. if (records.size() > 1) {
  74. long checkIn = (long) records.get(0).get("userCheckTime");
  75. long checkOut = (long) records.get(records.size() - 1).get("userCheckTime");
  76. String userId = String.valueOf(records.get(0).get("userId"));
  77. LocalDateTime begin = UtilDateTime.getLocalDateTimeFromTimestamp(checkIn);
  78. LocalDateTime finish = UtilDateTime.getLocalDateTimeFromTimestamp(checkOut);
  79. // 下班超过22点, 且当天考勤超过11小时
  80. float hour = UtilDateTime.betweenHour(begin, finish);
  81. if (hour > 11.0f && finish.getHour() >= 22) {
  82. String checkDate = UtilDateTime.formatLocalDate(begin.toLocalDate());
  83. if (!rzEkbRecordDao.existsByUserIdAndCheckDate(userId, checkDate)) {
  84. log.info("匹配符合数据, {}, {}, {}", userId, UtilDateTime.formatLocalDateTime(begin), UtilDateTime.formatLocalDateTime(finish));
  85. // 易快报同步默认是打卡结束时间, 若是23点以后下班, 第二天可打车, 结束传递+1天
  86. if (finish.getHour() == 23) {
  87. checkOut += 24 * 60 * 60 * 1000L;
  88. }
  89. pushTemplateByRecord(userId, checkIn, checkOut, checkDate);
  90. } else {
  91. log.info("数据已经推送, {}, {}, {}", userId, UtilDateTime.formatLocalDateTime(begin), UtilDateTime.formatLocalDateTime(finish));
  92. }
  93. }
  94. }
  95. });
  96. }
  97. }
  98. // 匹配userId, 以最新数据为准
  99. private void syncOrganizationalStructure(Number deptId, List<String> userIds) {
  100. // 同步一级部门用户
  101. if (deptId.equals(DDConf.TOP_DEPARTMENT)) {
  102. userIds.addAll(ddClientImpl_contacts.listDepartmentUserId(getAccessToken(RZConf.TYPE.dingtalk), DDConf.TOP_DEPARTMENT));
  103. }
  104. // 同步除一级部门外所有部门用户
  105. ddClientImpl_contacts.listSubDepartmentId(getAccessToken(RZConf.TYPE.dingtalk), deptId).forEach(dept -> {
  106. List<String> ids = ddClientImpl_contacts.listDepartmentUserId(getAccessToken(RZConf.TYPE.dingtalk), dept);
  107. userIds.addAll(ids);
  108. syncOrganizationalStructure(dept, userIds);
  109. });
  110. }
  111. // 推送易快报
  112. private void pushTemplateByRecord(String userId, long start, long end, String checkDate) {
  113. // 获取用户Id [易快报corpId:钉钉userId]
  114. Map userInfo = ekbClient.getStaffInfo(getAccessToken(RZConf.TYPE.ekuaibao), "USERID", Arrays.asList(userId)).get(0);
  115. // 获取自定义模板Id [通过list接口查询到固定模板ID, 再实时换取单据版本ID]
  116. Map template = ekbClient.getTemplateIdById(getAccessToken(RZConf.TYPE.ekuaibao), Arrays.asList(RZConf.REQUEST_TEMPLATE_ID)).get(0);
  117. // 组装自定义模板数据 [手动创建一条记录查询比对模板字段]
  118. Map formData = UtilMap.map("submitterId, specificationId", RZConf.SUBMITTED_ID, template.get("id")); // 固定提交人, 查看权限
  119. formData.put("travelers", Arrays.asList(userInfo.get("id"))); // 同行人, 即打车人
  120. formData.put("expenseDepartment", userInfo.get("defaultDepartment"));
  121. Map travel = UtilMap.map("E_e9101f64b75c7b57a3c0_name, E_e9101f64b75c7b57a3c0_住宿地", "上海市/上海市区", "[{\"key\":\"858\",\"label\":\"上海市/上海市区\"}]"); // 固定打车行程地点
  122. travel.put("E_e9101f64b75c7b57a3c0_入住日期", start); // 上班打卡日期
  123. travel.put("E_e9101f64b75c7b57a3c0_离店日期", end); // 下班打卡日期
  124. formData.put("u_行程规划", Arrays.asList(UtilMap.map("dataLinkForm, dataLinkTemplateId", travel, "ID01nORyvKjEPJ"))); // 行程ID通过查询记录复制
  125. // 创建单据数据, 推送易快报审批作为打车依据
  126. EKBR ekbr = ekbClient.createFormInstance(getAccessToken(RZConf.TYPE.ekuaibao), true, false, formData, null);
  127. // 记录当天的推送结果
  128. rzEkbRecordDao.save(RzEkbRecordPo.builder()
  129. .userId(userId)
  130. .checkDate(checkDate).build());
  131. log.info("易快报推送结果, {}", ekbr.getFlow());
  132. }
  133. }