TimeCardSummaryServiceTest.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package com.malk.service.workhours;
  2. import com.alibaba.fastjson.JSON;
  3. import com.malk.server.aliwork.YDConf;
  4. import com.malk.server.aliwork.YDParam;
  5. import com.malk.server.dingtalk.DDR_New;
  6. import com.malk.server.common.McException;
  7. import com.malk.server.workhours.TimeCardDuplicateCleanupRequest;
  8. import com.malk.server.workhours.TimeCardDuplicateCleanupResult;
  9. import com.malk.server.workhours.TimeCardSummaryUpsertRequest;
  10. import com.malk.server.workhours.WHConf;
  11. import com.malk.service.aliwork.YDClient;
  12. import org.junit.jupiter.api.Test;
  13. import org.mockito.ArgumentCaptor;
  14. import org.springframework.test.util.ReflectionTestUtils;
  15. import java.util.ArrayList;
  16. import java.util.Arrays;
  17. import java.util.Collections;
  18. import java.util.LinkedHashMap;
  19. import java.util.Map;
  20. import static org.junit.jupiter.api.Assertions.assertEquals;
  21. import static org.junit.jupiter.api.Assertions.assertThrows;
  22. import static org.junit.jupiter.api.Assertions.assertTrue;
  23. import static org.mockito.ArgumentMatchers.any;
  24. import static org.mockito.ArgumentMatchers.eq;
  25. import static org.mockito.Mockito.mock;
  26. import static org.mockito.Mockito.never;
  27. import static org.mockito.Mockito.verify;
  28. import static org.mockito.Mockito.when;
  29. class TimeCardSummaryServiceTest {
  30. @Test
  31. void shouldUpsertBySubmitterIdAndDayText() {
  32. YDClient ydClient = mock(YDClient.class);
  33. WHConf whConf = new WHConf();
  34. whConf.setYidaAppType("APP");
  35. whConf.setYidaSystemToken("TOKEN");
  36. whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY");
  37. TimeCardSummaryService service = new TimeCardSummaryService();
  38. ReflectionTestUtils.setField(service, "ydClient", ydClient);
  39. ReflectionTestUtils.setField(service, "whConf", whConf);
  40. TimeCardSummaryUpsertRequest request = new TimeCardSummaryUpsertRequest();
  41. request.setSubmitterUserId("user-1");
  42. request.setDayText("20260716");
  43. request.setFormData(Collections.singletonMap("numberField_hours", 8));
  44. DDR_New<Object> empty = new DDR_New<>();
  45. empty.setData(Collections.emptyList());
  46. when(ydClient.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
  47. .thenReturn(empty);
  48. assertTrue(service.upsert(request));
  49. ArgumentCaptor<YDParam> captor = ArgumentCaptor.forClass(YDParam.class);
  50. verify(ydClient).operateData(captor.capture(), eq(YDConf.FORM_OPERATION.upsert));
  51. YDParam param = captor.getValue();
  52. Map<String, Object> condition = JSON.parseObject(param.getSearchCondition(), Map.class);
  53. Map<String, Object> formData = JSON.parseObject(param.getFormDataJson(), Map.class);
  54. assertEquals("user-1", condition.get("textField_mmbffvd9"));
  55. assertEquals("20260716", condition.get("textField_mmbffvdb"));
  56. assertEquals("user-1", formData.get("textField_mmbffvd9"));
  57. assertEquals("20260716", formData.get("textField_mmbffvdb"));
  58. assertEquals(8, ((Number) formData.get("numberField_hours")).intValue());
  59. }
  60. @Test
  61. void shouldNotOverwriteApprovedSummaryEvenWhenDailyTotalIsZero() {
  62. YDClient ydClient = mock(YDClient.class);
  63. WHConf whConf = new WHConf();
  64. whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY");
  65. TimeCardSummaryService service = new TimeCardSummaryService();
  66. ReflectionTestUtils.setField(service, "ydClient", ydClient);
  67. ReflectionTestUtils.setField(service, "whConf", whConf);
  68. Map<String, Object> item = summaryItem("APPROVED-ID", "user-1", "20260716");
  69. Map<String, Object> data = (Map<String, Object>) item.get("formData");
  70. data.put("numberField_mmeakgi4", 0);
  71. Map<String, Object> child = new LinkedHashMap<>();
  72. child.put("numberField_mmczo637", 8);
  73. data.put("tableField_mmczo634", Collections.singletonList(child));
  74. DDR_New<Object> existing = new DDR_New<>();
  75. existing.setData(Collections.singletonList(item));
  76. when(ydClient.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
  77. .thenReturn(existing);
  78. TimeCardSummaryUpsertRequest request = new TimeCardSummaryUpsertRequest();
  79. request.setSubmitterUserId("user-1");
  80. request.setDayText("20260716");
  81. request.setFormData(Collections.singletonMap("numberField_mmd1smel", 8));
  82. assertThrows(McException.class, () -> service.upsert(request));
  83. verify(ydClient, never()).operateData(any(YDParam.class), eq(YDConf.FORM_OPERATION.upsert));
  84. }
  85. @Test
  86. void shouldNotOverwriteApprovedRepairFromNormalSummary() {
  87. YDClient ydClient = mock(YDClient.class);
  88. WHConf whConf = new WHConf();
  89. whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY");
  90. whConf.setFormUuidRepairWorkHoursSummary("FORM-REPAIR");
  91. whConf.setRepairSummarySyncStatusField("repair-status");
  92. TimeCardSummaryService service = new TimeCardSummaryService();
  93. ReflectionTestUtils.setField(service, "ydClient", ydClient);
  94. ReflectionTestUtils.setField(service, "whConf", whConf);
  95. DDR_New<Object> empty = new DDR_New<>();
  96. empty.setData(Collections.emptyList());
  97. Map<String, Object> repair = summaryItem("REPAIR-ID", "user-1", "20260716");
  98. ((Map<String, Object>) repair.get("formData")).put("repair-status", "已审批");
  99. DDR_New<Object> approved = new DDR_New<>();
  100. approved.setData(Collections.singletonList(repair));
  101. when(ydClient.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
  102. .thenReturn(empty, approved);
  103. TimeCardSummaryUpsertRequest request = new TimeCardSummaryUpsertRequest();
  104. request.setSubmitterUserId("user-1");
  105. request.setDayText("20260716");
  106. request.setFormData(Collections.singletonMap("numberField_mmd1smel", 8));
  107. assertThrows(McException.class, () -> service.upsert(request));
  108. verify(ydClient, never()).operateData(any(YDParam.class), eq(YDConf.FORM_OPERATION.upsert));
  109. }
  110. @Test
  111. void shouldLockWholeFormalDayWhenOneChildIsSubmitted() {
  112. Map<String, Object> record = summaryItem("SUBMITTED-ID", "user-1", "20260716");
  113. Map<String, Object> child = new LinkedHashMap<>();
  114. child.put("numberField_mmczo636", 8);
  115. ((Map<String, Object>) record.get("formData"))
  116. .put("tableField_mmczo634", Collections.singletonList(child));
  117. assertTrue(TimeCardSummaryService.isFormalRecordLocked(record));
  118. }
  119. @Test
  120. void shouldValidateDuplicateCleanupWithoutDeletingInDryRun() {
  121. YDClient ydClient = mock(YDClient.class);
  122. WHConf whConf = new WHConf();
  123. whConf.setYidaAppType("APP");
  124. whConf.setYidaSystemToken("TOKEN");
  125. whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY");
  126. TimeCardSummaryService service = new TimeCardSummaryService();
  127. ReflectionTestUtils.setField(service, "ydClient", ydClient);
  128. ReflectionTestUtils.setField(service, "whConf", whConf);
  129. DDR_New queryResult = mock(DDR_New.class);
  130. when(queryResult.getData()).thenReturn(Arrays.asList(
  131. summaryItem("DELETE-ID", "user-1", "20260507"),
  132. summaryItem("KEEP-ID", "user-1", "20260507")));
  133. when(ydClient.queryData(
  134. any(YDParam.class),
  135. eq(YDConf.FORM_QUERY.retrieve_search_form)))
  136. .thenReturn(queryResult);
  137. TimeCardDuplicateCleanupResult result =
  138. service.cleanupDuplicates(cleanupRequest(true));
  139. assertEquals(1, result.getValidated());
  140. assertEquals(0, result.getDeleted());
  141. verify(ydClient, never()).operateData(
  142. any(YDParam.class),
  143. eq(YDConf.FORM_OPERATION.delete_batch));
  144. }
  145. @Test
  146. void shouldRejectCleanupWhenCurrentDuplicateSetChanged() {
  147. TimeCardDuplicateCleanupRequest.Target target =
  148. cleanupRequest(true).getTargets().get(0);
  149. assertThrows(
  150. RuntimeException.class,
  151. () -> TimeCardSummaryService.validateCleanupTarget(
  152. target, Collections.singletonList("KEEP-ID")));
  153. }
  154. @Test
  155. void shouldValidateDuplicateGroupWithThreeDeleteTargets() {
  156. TimeCardDuplicateCleanupRequest request = cleanupRequest(true);
  157. request.setTargets(new ArrayList<>(request.getTargets()));
  158. for (int index = 2; index <= 3; index++) {
  159. TimeCardDuplicateCleanupRequest.Target target =
  160. new TimeCardDuplicateCleanupRequest.Target();
  161. target.setUserId("user-1");
  162. target.setDayText("20260507");
  163. target.setDeleteInstanceId("DELETE-ID-" + index);
  164. target.setKeepInstanceId("KEEP-ID");
  165. request.getTargets().add(target);
  166. }
  167. TimeCardSummaryService.validateCleanupTargets(
  168. request.getTargets(),
  169. Arrays.asList("KEEP-ID", "DELETE-ID", "DELETE-ID-2", "DELETE-ID-3"));
  170. }
  171. @Test
  172. void shouldDeleteValidatedTargetAndVerifyKeeper() {
  173. YDClient ydClient = mock(YDClient.class);
  174. WHConf whConf = new WHConf();
  175. whConf.setYidaAppType("APP");
  176. whConf.setYidaSystemToken("TOKEN");
  177. whConf.setFormUuidWorkHoursSummary("FORM-SUMMARY");
  178. TimeCardSummaryService service = new TimeCardSummaryService();
  179. ReflectionTestUtils.setField(service, "ydClient", ydClient);
  180. ReflectionTestUtils.setField(service, "whConf", whConf);
  181. DDR_New duplicateResult = mock(DDR_New.class);
  182. when(duplicateResult.getData()).thenReturn(Arrays.asList(
  183. summaryItem("DELETE-ID", "user-1", "20260507"),
  184. summaryItem("KEEP-ID", "user-1", "20260507")));
  185. DDR_New keeperResult = mock(DDR_New.class);
  186. when(keeperResult.getData()).thenReturn(Collections.singletonList(
  187. summaryItem("KEEP-ID", "user-1", "20260507")));
  188. when(ydClient.queryData(
  189. any(YDParam.class),
  190. eq(YDConf.FORM_QUERY.retrieve_search_form)))
  191. .thenReturn(duplicateResult, keeperResult);
  192. TimeCardDuplicateCleanupResult result =
  193. service.cleanupDuplicates(cleanupRequest(false));
  194. ArgumentCaptor<YDParam> captor = ArgumentCaptor.forClass(YDParam.class);
  195. verify(ydClient).operateData(
  196. captor.capture(),
  197. eq(YDConf.FORM_OPERATION.delete_batch));
  198. assertEquals(Collections.singletonList("DELETE-ID"),
  199. captor.getValue().getFormInstanceIdList());
  200. assertEquals(1, result.getDeleted());
  201. assertEquals(1, result.getVerified());
  202. }
  203. private static TimeCardDuplicateCleanupRequest cleanupRequest(boolean dryRun) {
  204. TimeCardDuplicateCleanupRequest.Target target =
  205. new TimeCardDuplicateCleanupRequest.Target();
  206. target.setUserId("user-1");
  207. target.setDayText("20260507");
  208. target.setDeleteInstanceId("DELETE-ID");
  209. target.setKeepInstanceId("KEEP-ID");
  210. TimeCardDuplicateCleanupRequest request =
  211. new TimeCardDuplicateCleanupRequest();
  212. request.setDryRun(dryRun);
  213. request.setTargets(Collections.singletonList(target));
  214. return request;
  215. }
  216. private static Map<String, Object> summaryItem(String instanceId,
  217. String userId,
  218. String dayText) {
  219. Map<String, Object> formData = new LinkedHashMap<>();
  220. formData.put("textField_mmbffvd9", userId);
  221. formData.put("textField_mmbffvdb", dayText);
  222. Map<String, Object> item = new LinkedHashMap<>();
  223. item.put("formInstanceId", instanceId);
  224. item.put("formData", formData);
  225. return item;
  226. }
  227. }