| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- package com.malk.service.workhours;
- 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.MonthClosingConf;
- import com.malk.server.workhours.MonthClosingStatusResult;
- import com.malk.server.workhours.MonthClosingValidationResult;
- import com.malk.server.workhours.WHConf;
- import com.malk.service.aliwork.YDClient;
- import org.junit.jupiter.api.Test;
- import org.mockito.invocation.InvocationOnMock;
- import org.springframework.test.util.ReflectionTestUtils;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- 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.verifyNoInteractions;
- import static org.mockito.Mockito.when;
- class MonthClosingServiceTest {
- private static final String CLOSING_FORM = "FORM-CLOSING";
- private static final String PROJECT_FORM = "FORM-PROJECT-APPROVAL";
- private static final String OTHER_FORM = "FORM-OTHER-APPROVAL";
- private static final String SUMMARY_FORM = "FORM-SUMMARY";
- private static final String REPAIR_SUMMARY_FORM = "FORM-REPAIR-SUMMARY";
- private static final String CLOSING_MONTH_FIELD = "dateField_closing_month";
- private static final String CLOSING_STATUS_FIELD = "radioField_closing_status";
- private static final String SYNC_STATUS_FIELD = "selectField_sync_status";
- private static final String SUMMARY_STATUS_FIELD = "selectField_summary_status";
- private static final String APPROVAL_MONTH_FIELD = "textField_mmd2lv0y";
- private static final String FILL_TYPE_FIELD = "radioField_fill_type";
- private static final String PROJECT_REPAIR_SUBMIT_DATE_FIELD = "dateField_project_repair_submit";
- private static final String OTHER_REPAIR_SUBMIT_DATE_FIELD = "dateField_other_repair_submit";
- private static final String REPAIR_SUMMARY_SUBMIT_DATE_FIELD = "dateField_repair_summary_submit";
- private static final String REPAIR_SUMMARY_STATUS_FIELD = "selectField_repair_summary_status";
- @Test
- void shouldRejectInvalidMonthBeforeQueryingYida() {
- YDClient ydClient = mock(YDClient.class);
- MonthClosingService service = service(ydClient);
- assertThrows(
- McException.class,
- () -> service.status(request("202613")));
- verifyNoInteractions(ydClient);
- }
- @Test
- void shouldAllowClosingWhenNoApprovalIsPending() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenReturn(response(Collections.emptyList()));
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202608"));
- assertTrue(result.isAllowed());
- assertFalse(result.isAlreadyClosed());
- assertEquals(0, result.getProjectRunningCount());
- assertEquals(0, result.getOtherRunningCount());
- assertEquals(0, result.getWriteBackPendingCount());
- assertEquals(0, result.getSummaryPendingCount());
- }
- @Test
- void shouldBlockClosingWhenProjectApprovalIsRunning() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(this::queryResponse);
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202608"));
- assertFalse(result.isAllowed());
- assertEquals(1, result.getProjectRunningCount());
- assertTrue(result.getMessage().contains("项目工时 1 张"));
- }
- @Test
- void shouldTreatApprovedClosingProcessAsClosed() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_process)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (CLOSING_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- Map<String, Object> formData = new HashMap<>();
- formData.put(CLOSING_MONTH_FIELD, "2026-08-01");
- formData.put(CLOSING_STATUS_FIELD, "封账");
- Map<String, Object> item = new HashMap<>();
- item.put("approvedResult", "agree");
- item.put("modifiedTimeGMT", 1_777_590_000_000L);
- item.put("formData", formData);
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingStatusResult result = service.status(request("202608"));
- assertTrue(result.isClosed());
- assertEquals(1, result.getApprovedClosingCount());
- assertEquals("封账", result.getCurrentStatus());
- }
- @Test
- void shouldTreatLatestApprovedReopeningAsOpen() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), eq(YDConf.FORM_QUERY.retrieve_search_process)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (CLOSING_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- return response(Arrays.asList(
- closingAction("启封", 1_777_676_400_000L),
- closingAction("封账", 1_777_590_000_000L)));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingStatusResult result = service.status(request("202608"));
- assertFalse(result.isClosed());
- assertEquals("启封", result.getCurrentStatus());
- assertEquals(1, result.getApprovedClosingCount());
- assertEquals(1, result.getApprovedReopeningCount());
- }
- @Test
- void shouldAllowReopeningOnlyWhenMonthIsClosed() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (CLOSING_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- return response(Collections.singletonList(
- closingAction("封账", 1_777_590_000_000L)));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202608", "启封"));
- assertTrue(result.isAllowed());
- assertEquals("启封", result.getRequestedStatus());
- assertEquals("封账", result.getCurrentStatus());
- }
- @Test
- void shouldBlockReopeningWhenMonthIsAlreadyOpen() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenReturn(response(Collections.emptyList()));
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202608", "启封"));
- assertFalse(result.isAllowed());
- assertTrue(result.getMessage().contains("当前未封账"));
- }
- @Test
- void shouldNormalizeDateFieldValuesToMonth() {
- assertEquals("202608", MonthClosingService.monthFromDateValue("2026-08-01"));
- assertEquals(
- "202608",
- MonthClosingService.monthFromDateValue(
- java.time.Instant.parse("2026-08-01T00:00:00Z").toEpochMilli()));
- }
- @Test
- void shouldBlockCompletedApprovalWhoseWriteBackIsNotSuccessful() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- YDConf.FORM_QUERY queryType = invocation.getArgument(1);
- if (queryType == YDConf.FORM_QUERY.retrieve_search_process
- && PROJECT_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- Map<String, Object> formData = new HashMap<>();
- formData.put(SYNC_STATUS_FIELD, "部分失败");
- Map<String, Object> item = new HashMap<>();
- item.put("approvedResult", "agree");
- item.put("formData", formData);
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202608"));
- assertFalse(result.isAllowed());
- assertEquals(1, result.getWriteBackPendingCount());
- }
- @Test
- void shouldAssignRunningRepairApprovalToSubmitMonthInsteadOfSourceMonth() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (PROJECT_FORM.equals(param.getFormUuid())
- && "RUNNING".equals(param.getInstanceStatus())) {
- return response(Collections.singletonList(
- repairApproval(PROJECT_REPAIR_SUBMIT_DATE_FIELD,
- "2026-09-15", "", "")));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult september = service.validate(request("202609"));
- MonthClosingValidationResult august = service.validate(request("202608"));
- assertFalse(september.isAllowed());
- assertEquals(1, september.getProjectRunningCount());
- assertTrue(august.isAllowed());
- }
- @Test
- void shouldBlockClosingWhenOtherRepairApprovalAwaitsWriteBack() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (OTHER_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- return response(Collections.singletonList(
- repairApproval(OTHER_REPAIR_SUBMIT_DATE_FIELD,
- "2026-09-15", "agree", "部分失败")));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202609"));
- assertFalse(result.isAllowed());
- assertEquals(1, result.getWriteBackPendingCount());
- }
- @Test
- void shouldAllowClosingAfterRepairApprovalAndSummaryWriteBackFinish() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (OTHER_FORM.equals(param.getFormUuid())
- && "COMPLETED".equals(param.getInstanceStatus())) {
- return response(Collections.singletonList(
- repairApproval(OTHER_REPAIR_SUBMIT_DATE_FIELD,
- "2026-09-15", "agree", "全部成功")));
- }
- if (REPAIR_SUMMARY_FORM.equals(param.getFormUuid())) {
- Map<String, Object> data = new HashMap<>();
- data.put(REPAIR_SUMMARY_SUBMIT_DATE_FIELD, "2026-09-15");
- data.put(REPAIR_SUMMARY_STATUS_FIELD, "全部成功");
- Map<String, Object> item = new HashMap<>();
- item.put("formData", data);
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202609"));
- assertTrue(result.isAllowed());
- assertEquals(0, result.getWriteBackPendingCount());
- assertEquals(0, result.getSummaryPendingCount());
- }
- @Test
- void shouldBlockClosingWhileRepairSummaryWriteBackIsPartial() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (REPAIR_SUMMARY_FORM.equals(param.getFormUuid())) {
- Map<String, Object> data = new HashMap<>();
- data.put(REPAIR_SUMMARY_SUBMIT_DATE_FIELD, "2026-09-15");
- data.put(REPAIR_SUMMARY_STATUS_FIELD, "部分成功");
- Map<String, Object> item = new HashMap<>();
- item.put("formData", data);
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202609"));
- MonthClosingValidationResult previousMonth = service.validate(request("202608"));
- assertFalse(result.isAllowed());
- assertEquals(1, result.getSummaryPendingCount());
- assertTrue(previousMonth.isAllowed());
- }
- @Test
- void shouldKeepBlockingClosingForNormalSummaryInApproval() {
- YDClient ydClient = mock(YDClient.class);
- when(ydClient.queryData(any(YDParam.class), any(YDConf.FORM_QUERY.class)))
- .thenAnswer(invocation -> {
- YDParam param = invocation.getArgument(0);
- if (SUMMARY_FORM.equals(param.getFormUuid())) {
- Map<String, Object> data = new HashMap<>();
- data.put("textField_mmbffvda", "202609");
- data.put(SUMMARY_STATUS_FIELD, "审批中");
- Map<String, Object> item = new HashMap<>();
- item.put("formData", data);
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- });
- MonthClosingService service = service(ydClient);
- MonthClosingValidationResult result = service.validate(request("202609"));
- assertFalse(result.isAllowed());
- assertEquals(1, result.getSummaryPendingCount());
- }
- private static Map<String, Object> repairApproval(String submitDateField,
- String submitDate, String result,
- String syncStatus) {
- Map<String, Object> formData = new HashMap<>();
- formData.put(APPROVAL_MONTH_FIELD, "202608");
- formData.put(FILL_TYPE_FIELD, "补填");
- formData.put(submitDateField, submitDate);
- formData.put(SYNC_STATUS_FIELD, syncStatus);
- Map<String, Object> item = new HashMap<>();
- item.put("approvedResult", result);
- item.put("formData", formData);
- return item;
- }
- private DDR_New queryResponse(InvocationOnMock invocation) {
- YDParam param = invocation.getArgument(0);
- YDConf.FORM_QUERY queryType = invocation.getArgument(1);
- if (queryType == YDConf.FORM_QUERY.retrieve_search_process
- && PROJECT_FORM.equals(param.getFormUuid())
- && "RUNNING".equals(param.getInstanceStatus())) {
- Map<String, Object> item = new HashMap<>();
- item.put("processInstanceId", "PROC-RUNNING");
- return response(Collections.singletonList(item));
- }
- return response(Collections.emptyList());
- }
- private MonthClosingService service(YDClient ydClient) {
- WHConf whConf = new WHConf();
- whConf.setYidaAppType("APP-TEST");
- whConf.setYidaSystemToken("TOKEN-TEST");
- whConf.setFormUuidApproval(PROJECT_FORM);
- whConf.setFormUuidOtherApproval(OTHER_FORM);
- whConf.setFormUuidWorkHoursSummary(SUMMARY_FORM);
- whConf.setFormUuidRepairWorkHoursSummary(REPAIR_SUMMARY_FORM);
- whConf.setApprovalSyncStatusField(SYNC_STATUS_FIELD);
- whConf.setOtherApprovalSyncStatusField(SYNC_STATUS_FIELD);
- whConf.setSummaryApprovalStatusField(SUMMARY_STATUS_FIELD);
- whConf.setApprovalFillTypeField(FILL_TYPE_FIELD);
- whConf.setApprovalRepairSubmitDateField(PROJECT_REPAIR_SUBMIT_DATE_FIELD);
- whConf.setOtherApprovalRepairSubmitDateField(OTHER_REPAIR_SUBMIT_DATE_FIELD);
- whConf.setRepairSummarySubmitDateField(REPAIR_SUMMARY_SUBMIT_DATE_FIELD);
- whConf.setRepairSummarySyncStatusField(REPAIR_SUMMARY_STATUS_FIELD);
- MonthClosingConf closingConf = new MonthClosingConf();
- closingConf.setFormUuid(CLOSING_FORM);
- closingConf.setMonthDateField(CLOSING_MONTH_FIELD);
- closingConf.setStatusField(CLOSING_STATUS_FIELD);
- MonthClosingService service = new MonthClosingService();
- ReflectionTestUtils.setField(service, "ydClient", ydClient);
- ReflectionTestUtils.setField(service, "whConf", whConf);
- ReflectionTestUtils.setField(service, "closingConf", closingConf);
- return service;
- }
- private static MonthClosingCheckRequest request(String monthText) {
- return request(monthText, "封账");
- }
- private static MonthClosingCheckRequest request(String monthText, String closingStatus) {
- return MonthClosingCheckRequest.builder()
- .monthText(monthText)
- .closingStatus(closingStatus)
- .build();
- }
- private static Map<String, Object> closingAction(String status, long modifiedTime) {
- Map<String, Object> formData = new HashMap<>();
- formData.put(CLOSING_MONTH_FIELD, "2026-08-01");
- formData.put(CLOSING_STATUS_FIELD, status);
- Map<String, Object> item = new HashMap<>();
- item.put("approvedResult", "agree");
- item.put("modifiedTimeGMT", modifiedTime);
- item.put("formData", formData);
- return item;
- }
- private static DDR_New response(List<Map> data) {
- DDR_New response = new DDR_New();
- response.setData(new ArrayList<>(data));
- response.setTotalCount(data.size());
- return response;
- }
- }
|