| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- package com.malk.service.workhours;
- import com.alibaba.fastjson.JSON;
- import com.malk.server.aliwork.YDConf;
- import com.malk.server.aliwork.YDParam;
- import com.malk.server.common.McException;
- import com.malk.server.dingtalk.DDR_New;
- import com.malk.server.workhours.MonthClosingCheckRequest;
- import com.malk.server.workhours.MonthClosingStatusResult;
- import com.malk.server.workhours.TimeCardApprovalStartRequest;
- import com.malk.server.workhours.TimeCardRepairSubmitRequest;
- import com.malk.server.workhours.TimeCardRepairSummaryUpsertRequest;
- import com.malk.server.workhours.WHConf;
- import com.malk.service.aliwork.YDClient;
- import org.junit.jupiter.api.Test;
- import org.mockito.ArgumentCaptor;
- import org.springframework.test.util.ReflectionTestUtils;
- import java.time.YearMonth;
- import java.time.ZoneId;
- import java.time.format.DateTimeFormatter;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertFalse;
- import static org.junit.jupiter.api.Assertions.assertThrows;
- import static org.junit.jupiter.api.Assertions.assertTrue;
- import static org.mockito.ArgumentMatchers.any;
- import static org.mockito.ArgumentMatchers.eq;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.never;
- import static org.mockito.Mockito.times;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.verifyNoInteractions;
- import static org.mockito.Mockito.when;
- class TimeCardRepairServiceTest {
- @Test
- void shouldBlockRepairSubmissionWhenCurrentMonthClosingIsRunning() {
- assertCurrentMonthBlocksSubmit(false, 1);
- }
- @Test
- void shouldBlockRepairSubmissionWhenCurrentMonthIsClosed() {
- assertCurrentMonthBlocksSubmit(true, 0);
- }
- @Test
- void shouldAllowRepairSubmissionPastClosingCheckWhenCurrentMonthIsOpen() {
- MonthClosingService closingService = mock(MonthClosingService.class);
- YDClient client = mock(YDClient.class);
- TimeCardRepairSubmitRequest request = repairSubmitRequest();
- String currentMonth = YearMonth.now(ZoneId.of("Asia/Shanghai"))
- .format(DateTimeFormatter.ofPattern("yyyyMM"));
- when(closingService.isClosed(request.getSourceMonthText())).thenReturn(true);
- when(closingService.status(any(MonthClosingCheckRequest.class)))
- .thenReturn(MonthClosingStatusResult.builder().monthText(currentMonth).build());
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "monthClosingService", closingService);
- ReflectionTestUtils.setField(service, "ydClient", client);
- McException error = assertThrows(McException.class, () -> service.submit(request));
- assertEquals("没有需要提交的补填工时", error.getMessage());
- verify(closingService).status(org.mockito.ArgumentMatchers.argThat(
- value -> currentMonth.equals(value.getMonthText())));
- verifyNoInteractions(client);
- }
- private static void assertCurrentMonthBlocksSubmit(boolean closed, int runningCount) {
- MonthClosingService closingService = mock(MonthClosingService.class);
- YDClient client = mock(YDClient.class);
- TimeCardRepairSubmitRequest request = repairSubmitRequest();
- when(closingService.isClosed(request.getSourceMonthText())).thenReturn(true);
- when(closingService.status(any(MonthClosingCheckRequest.class)))
- .thenReturn(MonthClosingStatusResult.builder()
- .closed(closed).runningClosingCount(runningCount).build());
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "monthClosingService", closingService);
- ReflectionTestUtils.setField(service, "ydClient", client);
- McException error = assertThrows(McException.class, () -> service.submit(request));
- assertEquals("当前月份已封账或封账流程审批中,不可提交补填", error.getMessage());
- verifyNoInteractions(client);
- }
- private static TimeCardRepairSubmitRequest repairSubmitRequest() {
- TimeCardRepairSubmitRequest request = new TimeCardRepairSubmitRequest();
- request.setSubmitterUserId("user-1");
- request.setSourceMonthText(YearMonth.now(ZoneId.of("Asia/Shanghai"))
- .minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM")));
- return request;
- }
- @Test
- void shouldAllowRepairWhenOriginalSummaryOnlyHasDraftHours() {
- Map<String, Object> formData = new HashMap<>();
- formData.put("numberField_mmd1smel", 8d);
- Map<String, Object> record = new HashMap<>();
- record.put("formData", formData);
- assertFalse(TimeCardRepairService.hasApprovedOrChangedHours(record));
- }
- @Test
- void shouldBlockRepairWhenApprovedAndChangedHoursRemain() {
- Map<String, Object> formData = new HashMap<>();
- formData.put("numberField_mmeakgi4", 8d);
- formData.put("numberField_mmeakgi5", -2d);
- assertTrue(TimeCardRepairService.hasApprovedOrChangedHours(formData));
- }
- @Test
- void shouldBlockRepairWhenChangeFullyOffsetsApprovedHours() {
- Map<String, Object> formData = new HashMap<>();
- formData.put("numberField_mmeakgi4", 8d);
- formData.put("numberField_mmeakgi5", -8d);
- assertTrue(TimeCardRepairService.hasApprovedOrChangedHours(formData));
- }
- @Test
- void shouldBlockRepairWhenOriginalApprovalStatusIsApproved() {
- Map<String, Object> formData = new HashMap<>();
- formData.put("selectField_mre1xz9g", "已审批");
- assertTrue(TimeCardRepairService.hasApprovedOrChangedHours(formData));
- }
- @Test
- void shouldNotSaveDraftOverApprovedRepairWithCorruptedDraftStatus() {
- YDClient client = mock(YDClient.class);
- MonthClosingService closingService = mock(MonthClosingService.class);
- WHConf conf = new WHConf();
- conf.setFormUuidWorkHoursSummary("original-summary");
- conf.setFormUuidRepairWorkHoursSummary("repair-summary");
- conf.setRepairSummaryFillTypeField("fill-type");
- conf.setRepairSummarySyncStatusField("repair-status");
- when(closingService.isClosed("202608")).thenReturn(true);
- DDR_New<Object> noOriginal = new DDR_New<>();
- noOriginal.setData(Collections.emptyList());
- Map<String, Object> child = new HashMap<>();
- child.put("numberField_mmeakgii", 8d);
- Map<String, Object> formData = new HashMap<>();
- formData.put("repair-status", "暂存");
- formData.put("textField_mmbffvd9", "user-1");
- formData.put("textField_mmbffvdb", "20260818");
- formData.put("tableField_mmeakgid", Collections.singletonList(child));
- Map<String, Object> record = new HashMap<>();
- record.put("formData", formData);
- DDR_New<Object> approvedRepair = new DDR_New<>();
- approvedRepair.setData(Collections.singletonList(record));
- when(client.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
- .thenReturn(noOriginal, approvedRepair);
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "ydClient", client);
- ReflectionTestUtils.setField(service, "whConf", conf);
- ReflectionTestUtils.setField(service, "monthClosingService", closingService);
- TimeCardRepairSummaryUpsertRequest request = new TimeCardRepairSummaryUpsertRequest();
- request.setSubmitterUserId("user-1");
- request.setDayText("20260818");
- request.setFormData(Collections.singletonMap("numberField_mmd1smel", 8d));
- assertThrows(McException.class, () -> service.upsert(request));
- verify(client, never()).operateData(any(YDParam.class), eq(YDConf.FORM_OPERATION.upsert));
- }
- @Test
- void shouldCalculateRepairDraftTotalsFromRowsInsteadOfPageTotals() {
- YDClient client = mock(YDClient.class);
- MonthClosingService closingService = mock(MonthClosingService.class);
- WHConf conf = new WHConf();
- conf.setFormUuidWorkHoursSummary("original-summary");
- conf.setFormUuidRepairWorkHoursSummary("repair-summary");
- conf.setRepairSummaryFillTypeField("fill-type");
- conf.setRepairSummarySyncStatusField("repair-status");
- when(closingService.isClosed("202608")).thenReturn(true);
- DDR_New<Object> empty = new DDR_New<>();
- empty.setData(Collections.emptyList());
- when(client.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
- .thenReturn(empty);
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "ydClient", client);
- ReflectionTestUtils.setField(service, "whConf", conf);
- ReflectionTestUtils.setField(service, "monthClosingService", closingService);
- Map<String, Object> child = new HashMap<>();
- child.put("numberField_mmczo635", 5d);
- Map<String, Object> payload = new HashMap<>();
- payload.put("tableField_mmczo634", Collections.singletonList(child));
- payload.put("numberField_mmeakgi4", 9d);
- payload.put("numberField_mmd1smel", 99d);
- TimeCardRepairSummaryUpsertRequest request = new TimeCardRepairSummaryUpsertRequest();
- request.setSubmitterUserId("user-1");
- request.setDayText("20260818");
- request.setFormData(payload);
- assertTrue(service.upsert(request));
- ArgumentCaptor<YDParam> captor = ArgumentCaptor.forClass(YDParam.class);
- verify(client).operateData(captor.capture(), eq(YDConf.FORM_OPERATION.upsert));
- Map<String, Object> condition = JSON.parseObject(captor.getValue().getSearchCondition(), Map.class);
- Map<String, Object> written = JSON.parseObject(captor.getValue().getFormDataJson(), Map.class);
- assertEquals("补填", condition.get("fill-type"));
- assertEquals(5d, ((Number) written.get("numberField_mmd1smel")).doubleValue());
- assertEquals(0d, ((Number) written.get("numberField_mmeakgi4")).doubleValue());
- assertEquals("补填", written.get("fill-type"));
- assertEquals("暂存", written.get("repair-status"));
- }
- @Test
- void shouldIgnoreChangeRowsDuringRepairResubmitScan() {
- YDClient client = mock(YDClient.class);
- ReSubmitApprovalService approvalService = mock(ReSubmitApprovalService.class);
- WHConf conf = new WHConf();
- conf.setFormUuidRepairWorkHoursSummary("shared-summary");
- conf.setRepairSummaryFillTypeField("fill-type");
- conf.setRepairSummarySyncStatusField("sync-status");
- Map<String, Object> change = new HashMap<>();
- change.put("fill-type", "变更");
- change.put("sync-status", "审批中");
- Map<String, Object> record = new HashMap<>();
- record.put("formData", change);
- DDR_New<Object> summaries = new DDR_New<>();
- summaries.setData(Collections.singletonList(record));
- when(client.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
- .thenReturn(summaries);
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "ydClient", client);
- ReflectionTestUtils.setField(service, "whConf", conf);
- ReflectionTestUtils.setField(service, "reSubmitApprovalService", approvalService);
- TimeCardRepairService.RepairResubmitResult result =
- service.resubmitRecentlyModifiedPending(7);
- assertEquals(0, result.getStarted());
- assertEquals(0, result.getFailed());
- verifyNoInteractions(approvalService);
- }
- @Test
- void shouldTreatBlankLegacyTypeAndRepairTypeAsRepairOnly() {
- assertTrue(TimeCardRepairService.isRepairSummaryRecord(
- Collections.emptyMap(), "fill-type"));
- assertTrue(TimeCardRepairService.isRepairSummaryRecord(
- Collections.singletonMap("fill-type", "补填"), "fill-type"));
- assertFalse(TimeCardRepairService.isRepairSummaryRecord(
- Collections.singletonMap("fill-type", "变更"), "fill-type"));
- }
- @Test
- void shouldReportRepairBatchStillFailedAfterDeferredRetry() {
- YDClient client = mock(YDClient.class);
- ReSubmitApprovalService approvalService = mock(ReSubmitApprovalService.class);
- WHConf conf = new WHConf();
- conf.setFormUuidRepairWorkHoursSummary("repair-summary");
- conf.setFormUuidApproval("bill-approval");
- conf.setFormUuidOtherApproval("other-approval");
- conf.setRepairSummaryFillTypeField("fill-type");
- conf.setRepairSummarySyncStatusField("repair-status");
- conf.setRepairSummaryBatchField("repair-batch");
- conf.setRepairSummarySubmitDateField("repair-submit-date");
- conf.setApprovalFillTypeField("fill-type");
- conf.setOtherApprovalRepairSubmitDateField("other-submit-date");
- conf.setOtherApprovalRepairBatchField("other-batch");
- conf.setOtherApprovalRepairSourceMonthField("other-source-month");
- Map<String, Object> otherRow = new HashMap<>();
- otherRow.put("numberField_mmeakgij", 8d);
- otherRow.put("numberField_mmeakgii", 0d);
- otherRow.put("selectField_mmeakgie", "培训");
- Map<String, Object> formData = new HashMap<>();
- formData.put("fill-type", "补填");
- formData.put("repair-status", "审批中");
- formData.put("repair-batch", "batch-1");
- formData.put("repair-submit-date", 1787000000000L);
- formData.put("textField_mmbffvd9", "user-1");
- formData.put("textField_mmbffvda", "202608");
- formData.put("textField_mmbffvdb", "20260818");
- formData.put("tableField_mmeakgid", Collections.singletonList(otherRow));
- Map<String, Object> record = new HashMap<>();
- record.put("formData", formData);
- DDR_New<Object> summaries = new DDR_New<>();
- summaries.setData(Collections.singletonList(record));
- DDR_New<Object> noApprovals = new DDR_New<>();
- noApprovals.setData(Collections.emptyList());
- when(client.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_form)))
- .thenReturn(summaries);
- when(client.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_process)))
- .thenReturn(noApprovals);
- when(approvalService.startApproval(any(TimeCardApprovalStartRequest.class)))
- .thenThrow(new IllegalStateException("temporary failure"));
- TimeCardRepairService service = new TimeCardRepairService();
- ReflectionTestUtils.setField(service, "ydClient", client);
- ReflectionTestUtils.setField(service, "whConf", conf);
- ReflectionTestUtils.setField(service, "reSubmitApprovalService", approvalService);
- TimeCardRepairService.RepairResubmitResult result =
- service.resubmitRecentlyModifiedPending(7);
- assertEquals(0, result.getStarted());
- assertEquals(1, result.getFailed());
- verify(approvalService, times(2)).startApproval(any(TimeCardApprovalStartRequest.class));
- }
- }
|