|
|
@@ -0,0 +1,490 @@
|
|
|
+package com.cyksj.service.relation.impl;
|
|
|
+
|
|
|
+import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
+import com.cyksj.mapper.TicketDurationRepairMapper;
|
|
|
+import com.cyksj.mapper.manage.cms.CmsUserMapper;
|
|
|
+import com.cyksj.mapper.manage.statistics.IncrExpiryTimeRecordMapper;
|
|
|
+import com.cyksj.model.dto.TicketDurationRepairOrder;
|
|
|
+import com.cyksj.model.dto.TicketDurationRepairRow;
|
|
|
+import com.cyksj.model.entity.CmsUser;
|
|
|
+import com.cyksj.model.entity.GroupsRelation;
|
|
|
+import com.cyksj.model.entity.IncrExpiryTimeRecord;
|
|
|
+import com.cyksj.model.request.TicketDurationRepairReq;
|
|
|
+import com.cyksj.model.response.TicketDurationRepairItemResp;
|
|
|
+import com.cyksj.model.response.TicketDurationRepairPreviewResp;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.junit.jupiter.params.ParameterizedTest;
|
|
|
+import org.junit.jupiter.params.provider.CsvSource;
|
|
|
+import org.junit.jupiter.params.provider.NullAndEmptySource;
|
|
|
+import org.junit.jupiter.params.provider.ValueSource;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+import static org.mockito.ArgumentMatchers.*;
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
+
|
|
|
+/** These tests use mapper mocks; no application context or database is started. */
|
|
|
+class TicketDurationRepairServiceImplTest {
|
|
|
+
|
|
|
+ private TicketDurationRepairMapper mapper;
|
|
|
+ private IncrExpiryTimeRecordMapper recordMapper;
|
|
|
+ private CmsUserMapper cmsUserMapper;
|
|
|
+ private TicketDurationRepairServiceImpl service;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ mapper = mock(TicketDurationRepairMapper.class);
|
|
|
+ recordMapper = mock(IncrExpiryTimeRecordMapper.class);
|
|
|
+ cmsUserMapper = mock(CmsUserMapper.class);
|
|
|
+ service = new TicketDurationRepairServiceImpl(mapper, recordMapper, cmsUserMapper);
|
|
|
+ when(mapper.selectAdjustedRelationIds(anyList())).thenReturn(Collections.emptyList());
|
|
|
+ CmsUser admin = new CmsUser();
|
|
|
+ admin.setNickname("repair-admin");
|
|
|
+ when(cmsUserMapper.selectById(99L)).thenReturn(admin);
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @CsvSource({
|
|
|
+ "772, 2025-03-15T12:30:00, 2026-03-15T12:30:00, 2025-09-15T12:30:00",
|
|
|
+ "774, 2023-08-31T12:30:00, 2024-08-31T12:30:00, 2024-02-29T12:30:00",
|
|
|
+ "774, 2024-02-29T12:30:00, 2025-02-28T12:30:00, 2024-08-28T12:30:00"
|
|
|
+ })
|
|
|
+ void previewUsesCalendarMonthsIncludingMonthEndsAndLeapYears(
|
|
|
+ long skuId, String start, String expiry, String repairedExpiry) {
|
|
|
+ TicketDurationRepairRow row = row(10L, skuId, start, expiry);
|
|
|
+ TicketDurationRepairItemResp item = preview(row, ordinaryOrder(row)).getItems().get(0);
|
|
|
+
|
|
|
+ assertEquals("candidate", item.getDecision());
|
|
|
+ assertEquals(date(repairedExpiry).getTime(), item.getNewExpiryTime().longValue());
|
|
|
+ assertEquals(0L, item.getYearDifferenceMillis().longValue());
|
|
|
+ assertEquals(row.getExpiryTime().getTime(), item.getExpiryTime().longValue());
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(longs = {-1000L, 1000L})
|
|
|
+ void smallClockDifferencesRequireReview(long differenceMillis) {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ row.setExpiryTime(new Date(row.getExpiryTime().getTime() + differenceMillis));
|
|
|
+
|
|
|
+ TicketDurationRepairItemResp item = preview(row, ordinaryOrder(row)).getItems().get(0);
|
|
|
+
|
|
|
+ assertEquals("review", item.getDecision());
|
|
|
+ assertEquals(differenceMillis, item.getYearDifferenceMillis().longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(strings = {"start", "expiry", "both"})
|
|
|
+ void unsetTimesAreSkipped(String absent) {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ if (!"expiry".equals(absent)) {
|
|
|
+ row.setStartTime(null);
|
|
|
+ }
|
|
|
+ if (!"start".equals(absent)) {
|
|
|
+ row.setExpiryTime(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ assertEquals("skipped", preview(row, ordinaryOrder(row)).getItems().get(0).getDecision());
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void existingHalfYearIsSkipped() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ row.setExpiryTime(date("2025-09-15T12:30:00"));
|
|
|
+
|
|
|
+ assertEquals("skipped", preview(row, ordinaryOrder(row)).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void renewalIsNotAutomaticallyShortened() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairOrder renewal = ordinaryOrder(row);
|
|
|
+ renewal.setOrderType(2);
|
|
|
+
|
|
|
+ assertEquals("review", preview(row, renewal).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void multipleOrdersRequireReviewEvenWhenTheirTotalDurationIsOneYear() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairOrder original = ordinaryOrder(row);
|
|
|
+ TicketDurationRepairOrder additional = ordinaryOrder(row);
|
|
|
+ additional.setOrderId(original.getOrderId() + 1);
|
|
|
+
|
|
|
+ assertEquals("review", preview(row, original, additional).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void duplicateDirectAndSlotAssociationsCountAsOneOrder() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairOrder direct = ordinaryOrder(row);
|
|
|
+ TicketDurationRepairOrder slot = ordinaryOrder(row);
|
|
|
+ direct.setNum(4);
|
|
|
+ slot.setNum(4);
|
|
|
+ slot.setSlot(true);
|
|
|
+
|
|
|
+ assertEquals("candidate", preview(row, direct, slot).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void slotAssociationAllowsMultiplePurchasedTicketsWithoutTreatingThemAsRenewals() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairOrder slot = ordinaryOrder(row);
|
|
|
+ slot.setNum(4);
|
|
|
+ slot.setSlot(true);
|
|
|
+
|
|
|
+ assertEquals("candidate", preview(row, slot).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(strings = {"frozen", "recover", "reserved", "compensation", "manual"})
|
|
|
+ void exceptionalDurationHistoryRequiresReview(String history) {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ switch (history) {
|
|
|
+ case "frozen": row.setIsFrozen(true); break;
|
|
|
+ case "recover": row.setIsRecover(true); break;
|
|
|
+ case "reserved": row.setReservedDays(3); break;
|
|
|
+ case "compensation": row.setCompensationDays(3); break;
|
|
|
+ case "manual":
|
|
|
+ when(mapper.selectAdjustedRelationIds(anyList())).thenReturn(Collections.singletonList(10L));
|
|
|
+ break;
|
|
|
+ default: fail("Unhandled case");
|
|
|
+ }
|
|
|
+
|
|
|
+ assertEquals("review", preview(row, ordinaryOrder(row)).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void missingOrderRequiresReview() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+
|
|
|
+ assertEquals("review", preview(row).getItems().get(0).getDecision());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void previewPaginationDoesNotSkipTheLookaheadRow() {
|
|
|
+ TicketDurationRepairRow first = annualRow(10L);
|
|
|
+ TicketDurationRepairRow second = annualRow(20L);
|
|
|
+ TicketDurationRepairRow lookahead = annualRow(30L);
|
|
|
+ when(mapper.selectPage(0L, 3, null)).thenReturn(Arrays.asList(first, second, lookahead));
|
|
|
+ when(mapper.selectOrders(anyList())).thenReturn(Arrays.asList(
|
|
|
+ ordinaryOrder(first), ordinaryOrder(second), ordinaryOrder(lookahead)));
|
|
|
+
|
|
|
+ TicketDurationRepairPreviewResp result = service.preview(0L, 2, null);
|
|
|
+
|
|
|
+ assertEquals(2, result.getItems().size());
|
|
|
+ assertEquals(20L, result.getNextAfterId().longValue());
|
|
|
+ assertTrue(result.isHasMore());
|
|
|
+ assertNull(result.getUserId());
|
|
|
+ verify(mapper).selectPage(0L, 3, null);
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void previewFiltersByUserAndEchoesTheFilterWithoutWriting() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ Long userId = row.getUserId();
|
|
|
+ when(mapper.selectPage(0L, 101, userId)).thenReturn(Collections.singletonList(row));
|
|
|
+ when(mapper.selectOrders(anyList())).thenReturn(Collections.singletonList(ordinaryOrder(row)));
|
|
|
+
|
|
|
+ TicketDurationRepairPreviewResp result = service.preview(0L, 100, userId);
|
|
|
+
|
|
|
+ assertEquals(userId, result.getUserId());
|
|
|
+ assertEquals(1, result.getItems().size());
|
|
|
+ assertEquals(userId, result.getItems().get(0).getUserId());
|
|
|
+ assertEquals("candidate", result.getItems().get(0).getDecision());
|
|
|
+ verify(mapper).selectPage(0L, 101, userId);
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(longs = {0L, -1L})
|
|
|
+ void previewRejectsNonPositiveUserIdsBeforeQuerying(long userId) {
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.preview(0L, 100, userId));
|
|
|
+
|
|
|
+ verifyNoInteractions(mapper, recordMapper, cmsUserMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @ValueSource(strings = {"groups", "sku", "user", "start", "expiry", "update", "nullUpdate"})
|
|
|
+ void changedPreviewSnapshotIsRejectedBeforeWriting(String changed) {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairReq req = request(row);
|
|
|
+ switch (changed) {
|
|
|
+ case "groups": row.setGroupsId(row.getGroupsId() + 1); break;
|
|
|
+ case "sku": row.setSkuId(774L); break;
|
|
|
+ case "user": row.setUserId(row.getUserId() + 1); break;
|
|
|
+ case "start": row.setStartTime(new Date(row.getStartTime().getTime() + 1000)); break;
|
|
|
+ case "expiry": row.setExpiryTime(new Date(row.getExpiryTime().getTime() + 1000)); break;
|
|
|
+ case "update": row.setUpdateTime(new Date(row.getUpdateTime().getTime() + 1000)); break;
|
|
|
+ case "nullUpdate": row.setUpdateTime(null); break;
|
|
|
+ default: fail("Unhandled case");
|
|
|
+ }
|
|
|
+ arrangeExecution(row);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(req, 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void executionRechecksEligibilityAfterPreview() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairReq req = request(row);
|
|
|
+ row.setIsFrozen(true);
|
|
|
+ arrangeExecution(row);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(req, 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void executionRejectsDuplicateIds() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairReq req = request(row, row);
|
|
|
+ arrangeExecution(row);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(req, 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void emptyExecutionRequestIsRejected() {
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(request(), 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void executionBatchLargerThanLimitIsRejected() {
|
|
|
+ TicketDurationRepairRow[] rows = new TicketDurationRepairRow[201];
|
|
|
+ for (int index = 0; index < rows.length; index++) {
|
|
|
+ rows[index] = annualRow(index + 1L);
|
|
|
+ }
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(request(rows), 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void missingTicketIsRejected() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(request(row), 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void validatesEverySnapshotBeforeUpdatingAnyTicket() {
|
|
|
+ TicketDurationRepairRow first = annualRow(10L);
|
|
|
+ TicketDurationRepairRow second = annualRow(20L);
|
|
|
+ TicketDurationRepairReq req = request(first, second);
|
|
|
+ second.setUserId(second.getUserId() + 1);
|
|
|
+ arrangeExecution(first, second);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(req, 99L));
|
|
|
+
|
|
|
+ verify(mapper, never()).updateExpiry(any(), any());
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void failedConditionalUpdateDoesNotWriteAnAuditRecord() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ arrangeExecution(row);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(0);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(request(row), 99L));
|
|
|
+
|
|
|
+ verifyNoInteractions(recordMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @ParameterizedTest
|
|
|
+ @NullAndEmptySource
|
|
|
+ @ValueSource(strings = {" \t\n"})
|
|
|
+ void successfulExecutionRecordsOldExpiryAndNegativeDayAdjustmentWithDefaultRemark(String remark) {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ Date oldExpiry = new Date(row.getExpiryTime().getTime());
|
|
|
+ TicketDurationRepairReq req = request(row);
|
|
|
+ req.setRemark(remark);
|
|
|
+ arrangeExecution(row);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(1);
|
|
|
+ when(recordMapper.insert(any(IncrExpiryTimeRecord.class))).thenReturn(1);
|
|
|
+
|
|
|
+ List<TicketDurationRepairItemResp> result = service.execute(req, 99L);
|
|
|
+
|
|
|
+ verify(mapper).updateExpiry(any(TicketDurationRepairRow.class), eq(date("2025-09-15T12:30:00")));
|
|
|
+ ArgumentCaptor<IncrExpiryTimeRecord> audit = ArgumentCaptor.forClass(IncrExpiryTimeRecord.class);
|
|
|
+ verify(recordMapper).insert(audit.capture());
|
|
|
+ assertEquals(10L, audit.getValue().getRelationId().longValue());
|
|
|
+ assertEquals(row.getUserId(), audit.getValue().getUserId());
|
|
|
+ assertEquals(oldExpiry, audit.getValue().getExpiryTimeInitial());
|
|
|
+ assertTrue(audit.getValue().getIncrExpiryDays() < 0);
|
|
|
+ assertEquals("规格772/774半年付时长修复:原到期时间减6个自然月;原到期="
|
|
|
+ + "2026-03-15 12:30:00;新到期=2025-09-15 12:30:00", audit.getValue().getRemark());
|
|
|
+ assertEquals(1, result.size());
|
|
|
+ assertEquals("updated", result.get(0).getDecision());
|
|
|
+ assertEquals(date("2025-09-15T12:30:00").getTime(), result.get(0).getNewExpiryTime().longValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void customRemarkIsSavedVerbatimForEveryTicketInTheBatch() {
|
|
|
+ TicketDurationRepairRow first = annualRow(10L);
|
|
|
+ TicketDurationRepairRow second = annualRow(20L);
|
|
|
+ second.setSkuId(774L);
|
|
|
+ String remark = " 半年付时长误设为一年,已核实扣除半年。\n客户备注保留 ";
|
|
|
+ TicketDurationRepairReq req = request(first, second);
|
|
|
+ req.setRemark(remark);
|
|
|
+ arrangeExecution(first, second);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(1);
|
|
|
+ when(recordMapper.insert(any(IncrExpiryTimeRecord.class))).thenReturn(1);
|
|
|
+
|
|
|
+ List<TicketDurationRepairItemResp> result = service.execute(req, 99L);
|
|
|
+
|
|
|
+ ArgumentCaptor<IncrExpiryTimeRecord> audit = ArgumentCaptor.forClass(IncrExpiryTimeRecord.class);
|
|
|
+ verify(recordMapper, times(2)).insert(audit.capture());
|
|
|
+ assertEquals(2, result.size());
|
|
|
+ assertEquals(10L, audit.getAllValues().get(0).getRelationId().longValue());
|
|
|
+ assertEquals(20L, audit.getAllValues().get(1).getRelationId().longValue());
|
|
|
+ for (IncrExpiryTimeRecord record : audit.getAllValues()) {
|
|
|
+ assertEquals(remark, record.getRemark());
|
|
|
+ assertEquals("repair-admin", record.getOperator());
|
|
|
+ assertTrue(record.getIncrExpiryDays() < 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void unchangedNullUpdateTimeCanBeRepaired() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ row.setUpdateTime(null);
|
|
|
+ arrangeExecution(row);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(1);
|
|
|
+ when(recordMapper.insert(any(IncrExpiryTimeRecord.class))).thenReturn(1);
|
|
|
+
|
|
|
+ assertEquals(1, service.execute(request(row), 99L).size());
|
|
|
+
|
|
|
+ verify(mapper).updateExpiry(any(), eq(date("2025-09-15T12:30:00")));
|
|
|
+ verify(recordMapper).insert(any(IncrExpiryTimeRecord.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void unsuccessfulAuditInsertionRaisesAnErrorToRollbackTheTransaction() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ arrangeExecution(row);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(1);
|
|
|
+ when(recordMapper.insert(any(IncrExpiryTimeRecord.class))).thenReturn(0);
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(request(row), 99L));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void repeatExecutionCannotDeductAnotherSixMonths() {
|
|
|
+ TicketDurationRepairRow row = annualRow(10L);
|
|
|
+ TicketDurationRepairReq originalRequest = request(row);
|
|
|
+ arrangeExecution(row);
|
|
|
+ when(mapper.updateExpiry(any(), any())).thenReturn(1);
|
|
|
+ when(recordMapper.insert(any(IncrExpiryTimeRecord.class))).thenReturn(1);
|
|
|
+ service.execute(originalRequest, 99L);
|
|
|
+ row.setExpiryTime(date("2025-09-15T12:30:00"));
|
|
|
+ row.setUpdateTime(new Date(row.getUpdateTime().getTime() + 1000));
|
|
|
+
|
|
|
+ assertThrows(BusinessRuntimeException.class, () -> service.execute(originalRequest, 99L));
|
|
|
+
|
|
|
+ verify(mapper, times(1)).updateExpiry(any(), any());
|
|
|
+ verify(recordMapper, times(1)).insert(any(IncrExpiryTimeRecord.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ private TicketDurationRepairPreviewResp preview(TicketDurationRepairRow row,
|
|
|
+ TicketDurationRepairOrder... orders) {
|
|
|
+ when(mapper.selectPage(0L, 101, null)).thenReturn(Collections.singletonList(row));
|
|
|
+ when(mapper.selectOrders(anyList())).thenReturn(Arrays.asList(orders));
|
|
|
+ return service.preview(0L, 100, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void arrangeExecution(TicketDurationRepairRow... rows) {
|
|
|
+ for (TicketDurationRepairRow row : rows) {
|
|
|
+ when(mapper.selectForUpdate(row.getRelationId())).thenReturn(row);
|
|
|
+ }
|
|
|
+ TicketDurationRepairOrder[] orders = Arrays.stream(rows)
|
|
|
+ .map(this::ordinaryOrder).toArray(TicketDurationRepairOrder[]::new);
|
|
|
+ when(mapper.selectOrders(anyList())).thenReturn(Arrays.asList(orders));
|
|
|
+ }
|
|
|
+
|
|
|
+ private TicketDurationRepairRow annualRow(long relationId) {
|
|
|
+ return row(relationId, 772L, "2025-03-15T12:30:00", "2026-03-15T12:30:00");
|
|
|
+ }
|
|
|
+
|
|
|
+ private TicketDurationRepairRow row(long relationId, long skuId, String start, String expiry) {
|
|
|
+ TicketDurationRepairRow row = new TicketDurationRepairRow();
|
|
|
+ row.setRelationId(relationId);
|
|
|
+ row.setGroupsId(100L);
|
|
|
+ row.setSkuId(skuId);
|
|
|
+ row.setUserId(200L);
|
|
|
+ row.setStatus(GroupsRelation.Status.validity);
|
|
|
+ row.setStartTime(date(start));
|
|
|
+ row.setExpiryTime(date(expiry));
|
|
|
+ row.setUpdateTime(date("2025-03-16T08:00:00"));
|
|
|
+ row.setIsFrozen(false);
|
|
|
+ row.setIsRecover(false);
|
|
|
+ row.setIsVip(false);
|
|
|
+ row.setReservedDays(0);
|
|
|
+ row.setCompensationDays(0);
|
|
|
+ row.setAqType(1);
|
|
|
+ return row;
|
|
|
+ }
|
|
|
+
|
|
|
+ private TicketDurationRepairOrder ordinaryOrder(TicketDurationRepairRow row) {
|
|
|
+ TicketDurationRepairOrder order = new TicketDurationRepairOrder();
|
|
|
+ order.setRelationId(row.getRelationId());
|
|
|
+ order.setOrderId(row.getRelationId() + 1000L);
|
|
|
+ order.setSkuId(row.getSkuId());
|
|
|
+ order.setOrderType(1);
|
|
|
+ order.setNum(1);
|
|
|
+ order.setSlot(false);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ private TicketDurationRepairReq request(TicketDurationRepairRow... rows) {
|
|
|
+ TicketDurationRepairReq req = new TicketDurationRepairReq();
|
|
|
+ java.util.ArrayList<TicketDurationRepairReq.Snapshot> items = new java.util.ArrayList<>();
|
|
|
+ for (TicketDurationRepairRow row : rows) {
|
|
|
+ TicketDurationRepairReq.Snapshot item = new TicketDurationRepairReq.Snapshot();
|
|
|
+ item.setRelationId(row.getRelationId());
|
|
|
+ item.setGroupsId(row.getGroupsId());
|
|
|
+ item.setSkuId(row.getSkuId());
|
|
|
+ item.setUserId(row.getUserId());
|
|
|
+ item.setStartTime(row.getStartTime() == null ? null : row.getStartTime().getTime());
|
|
|
+ item.setExpiryTime(row.getExpiryTime() == null ? null : row.getExpiryTime().getTime());
|
|
|
+ item.setUpdateTime(row.getUpdateTime() == null ? null : row.getUpdateTime().getTime());
|
|
|
+ items.add(item);
|
|
|
+ }
|
|
|
+ req.setItems(items);
|
|
|
+ return req;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Date date(String value) {
|
|
|
+ return Date.from(LocalDateTime.parse(value).atZone(ZoneId.systemDefault()).toInstant());
|
|
|
+ }
|
|
|
+}
|