KYNTController.java 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package com.malk.kaiyue.controller;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.malk.controller.DDCallbackController;
  5. import com.malk.kaiyue.service.KYNTService;
  6. import com.malk.server.common.McR;
  7. import com.malk.server.dingtalk.DDConf;
  8. import com.malk.server.dingtalk.crypto.DingCallbackCrypto;
  9. import lombok.SneakyThrows;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.scheduling.annotation.Scheduled;
  14. import org.springframework.web.bind.annotation.*;
  15. import java.util.*;
  16. import java.util.concurrent.TimeUnit;
  17. @Slf4j
  18. @RestController
  19. @RequestMapping("/nt")
  20. public class KYNTController extends DDCallbackController {
  21. @Autowired
  22. private KYNTService kyntService;
  23. @Autowired
  24. private DDConf ddConf;
  25. @Value("${dingtalk_nt.token}")
  26. private String token;
  27. @Value("${dingtalk_nt.aesKey}")
  28. private String aesKey;
  29. @Value("${dingtalk_nt.appKey}")
  30. private String appKey;
  31. @GetMapping("/test")
  32. public McR test() {
  33. log.info("11111111");
  34. return McR.success();
  35. }
  36. //获取在职员工userId列表
  37. @GetMapping("/getEmployeeUserId")
  38. McR getEmployeeUserId() {
  39. List<String> result = kyntService.getEmployeeUserId();
  40. return McR.success(result);
  41. }
  42. //获取员工花名册信息
  43. @PostMapping("/getEmployeeRosterInfo")
  44. McR getEmployeeRosterInfo(@RequestBody Map<String, Object> map) {
  45. List<Map> result = kyntService.getEmployeeRosterInfo(map);
  46. return McR.success(result);
  47. }
  48. //计算并设置员工年假数
  49. @PostMapping("/getEmployeeAnnualLeaveNum")
  50. McR getEmployeeAnnualLeaveNum(@RequestBody Map<String, Object> map) {
  51. kyntService.getEmployeeAnnualLeaveNum(map);
  52. return McR.success();
  53. }
  54. //每年1月1日 01:00定时更新员工年假数
  55. @Scheduled(cron = "0 0 1 1 1 ? ")
  56. @GetMapping("/cronUpdateEmployeeAnnualLeaveNum")
  57. McR cronUpdateEmployeeAnnualLeaveNum(){
  58. System.out.println("定时更新员工年假数开始执行"+new Date());
  59. return kyntService.updateEmployeeAnnualLeaveNum();
  60. }
  61. @PostMapping("/getUserLeaveInfo")
  62. McR getUserLeaveInfo(@RequestBody Map map) {
  63. String userId = map.get("userId").toString();
  64. Map result = kyntService.getUserLeaveInfo(userId);
  65. return McR.success(result);
  66. }
  67. //保存10s内已处理的回调事件
  68. private Map<String, Long> eventList = new HashMap<>();
  69. //钉钉事件回调
  70. @SneakyThrows
  71. @RequestMapping(value = "/callback", method = RequestMethod.POST)
  72. public synchronized Map<String, String> invokeCallback(@RequestParam(value = "signature", required = false) String signature,
  73. @RequestParam(value = "timestamp", required = false) String timestamp,
  74. @RequestParam(value = "nonce", required = false) String nonce,
  75. @RequestBody(required = false) JSONObject json) {
  76. DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, aesKey, appKey);
  77. // 处理回调消息,得到回调事件decryptMsg...
  78. final String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, json.getString("encrypt"));
  79. JSONObject eventJson = JSON.parseObject(decryptMsg);
  80. Map success = callbackCrypto.getEncryptedMap(DDConf.CALLBACK_RESPONSE, System.currentTimeMillis(), DingCallbackCrypto.Utils.getRandomStr(8));
  81. // 检查回调事件是否已经处理过,如果是,则忽略该回调
  82. if (isCallbackProcessed(decryptMsg)) {
  83. log.info("----- [DD]该回调事件已处理过 忽略该回调 -----");
  84. return success;
  85. }
  86. // 业务处理代码...
  87. String eventType = eventJson.getString("EventType");
  88. if (DDConf.CALLBACK_CHECK.equals(eventType)) {
  89. log.info("----- [DD]验证注册 -----");
  90. return success;
  91. }
  92. // [回调任务执行逻辑: 异步] 钉钉超时3s未返回会被记录为失败, 可通过失败接口获取记录
  93. if (Arrays.asList(DDConf.HRM_USER_RECORD_CHANGE).contains(eventType)) {
  94. log.info("[DD]人事档案变动回调, eventType:{}, eventJson:{}",eventType, eventJson);
  95. //获取员工userId
  96. String userId = "";
  97. if (Objects.nonNull(eventJson.get("staffId"))){
  98. //人事档案返回的userId
  99. userId = eventJson.get("staffId").toString();
  100. }else if (Objects.nonNull(eventJson.get("userid"))){
  101. //通讯录事件返回的userId
  102. userId = eventJson.get("userid").toString();
  103. }else {
  104. log.error("[DD]人事档案变动回调, 未获取到userId");
  105. return success;
  106. }
  107. log.info("员工userId:"+userId);
  108. Map<String, Object> map = new HashMap();
  109. map.put("userid_list", userId);
  110. //更新员工年假余额
  111. log.info("----- [DD]更新员工年假余额 -----");
  112. kyntService.getEmployeeAnnualLeaveNum(map);
  113. // 将回调事件和当前时间戳添加到已处理集合中
  114. long currentTime = System.currentTimeMillis();
  115. eventList.put(decryptMsg, currentTime);
  116. return success;
  117. }
  118. log.info("----- [DD]已注册, 未处理的其它回调 -----, eventType:{}, eventJson:{}",eventType, eventJson);
  119. return success;
  120. }
  121. /**
  122. * 检查该回调事件在10s内是否处理过,应对钉钉瞬间重复回调
  123. *
  124. * @param decryptMsg 回调事件
  125. * @return 是否处理过
  126. */
  127. private boolean isCallbackProcessed(String decryptMsg) {
  128. // 清理超过十分钟的回调事件
  129. long currentTime = System.currentTimeMillis();
  130. long expirationTime = currentTime - TimeUnit.MINUTES.toMillis(10);
  131. eventList.entrySet().removeIf(entry -> entry.getValue() < expirationTime);
  132. return eventList.containsKey(decryptMsg);
  133. }
  134. }