fix: retain appointment cancellation reasons and query history
This commit is contained in:
+4
@@ -105,6 +105,10 @@ class InboundPlanControllerTest {
|
||||
assertThatThrownBy(() -> controller.fbaAppointmentCalendar(customer,
|
||||
java.time.LocalDate.of(2026, 10, 1), java.time.LocalDate.of(2026, 11, 1)))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
assertThatThrownBy(() -> controller.fbaAppointmentCancellations(customer,"APT-1",1,20))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
assertThatThrownBy(() -> controller.fbaAppointmentCancellationRecords(customer,1,20))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
verify(appointmentService, never()).detail(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -30,6 +30,12 @@ class InboundPlanPermissionContractTest {
|
||||
}
|
||||
assertEquals("hasAnyAuthority('" + LIST + "','platform:appointment:list')",
|
||||
methods.get("fbaAppointmentDetail").getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("hasAnyAuthority('" + LIST + "','platform:appointment:list')",
|
||||
methods.get("fbaAppointmentCancellations").getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("hasAnyAuthority('" + LIST + "','platform:appointment:list')",
|
||||
methods.get("fbaAppointmentCancellationRecords").getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("hasAuthority('platform:appointment:delete')",
|
||||
methods.get("deleteFbaAppointment").getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("hasAuthority('platform:appointment:list')",
|
||||
methods.get("fbaAppointmentCalendar").getAnnotation(PreAuthorize.class).value());
|
||||
}
|
||||
|
||||
+32
-1
@@ -1,5 +1,7 @@
|
||||
package com.baiyue.lastleg.inboundplan.service;
|
||||
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
|
||||
import com.baiyue.lastleg.appointmentplanning.*;
|
||||
@@ -102,7 +104,7 @@ class FbaAppointmentPersistenceTest {
|
||||
@Test void cancellationReleasesGeneratedClaimsWhileReplayKeepsItsOriginalSnapshot() {
|
||||
try(var context=new AnnotationConfigApplicationContext(Config.class)) {
|
||||
var service=context.getBean(InboundFbaAppointmentService.class);var first=service.createFromSources("operator",request("first"));
|
||||
service.delete("operator",first.appointmentNo(),0);
|
||||
service.delete("operator",first.appointmentNo(),new CancelFbaAppointmentRequest(0,"收货方取消时段"));
|
||||
var second=service.createFromSources("operator",request("second"));assertNotEquals(first.appointmentNo(),second.appointmentNo());
|
||||
assertEquals("CANCELLED",service.detail(first.appointmentNo()).appointmentStatus());
|
||||
assertEquals(first,service.createFromSources("operator",request("first")));
|
||||
@@ -110,6 +112,35 @@ class FbaAppointmentPersistenceTest {
|
||||
assertEquals(2,db.queryForObject("SELECT COUNT(*) FROM ll_inbound_fba_appointment_item WHERE active=1",Integer.class));
|
||||
}
|
||||
}
|
||||
@Test void cancellationHistoryIsPersistedPagedScopedAndDoesNotExposeIdentities() throws Exception {
|
||||
try(var context=new AnnotationConfigApplicationContext(Config.class)) {
|
||||
var service=context.getBean(InboundFbaAppointmentService.class);
|
||||
var original=service.createFromSources("internal-operator",request("history-first"));
|
||||
var reason=new CancelFbaAppointmentRequest(0,"仓库临时关闭");
|
||||
service.delete("internal-operator",original.appointmentNo(),reason);
|
||||
service.delete("internal-operator",original.appointmentNo(),reason);
|
||||
var page=service.cancellationHistory(original.appointmentNo(),1,1);
|
||||
assertEquals(1,service.cancellations(1,1).total());
|
||||
assertEquals(original.appointmentNo(),service.cancellations(1,1).records().get(0).appointmentNo());
|
||||
assertTrue(service.cancellations(2,1).records().isEmpty());
|
||||
assertEquals(1,page.total());assertEquals(1,page.records().size());
|
||||
assertEquals("仓库临时关闭",page.records().get(0).cancelReason());
|
||||
assertEquals(original.appointmentRemark(),page.records().get(0).snapshot().appointmentRemark());
|
||||
assertEquals("CANCELLED",page.records().get(0).snapshot().appointmentStatus());
|
||||
var json=new ObjectMapper().findAndRegisterModules().writeValueAsString(page);
|
||||
assertFalse(json.contains("internal-operator"));assertFalse(json.contains("responseJson"));assertFalse(json.contains("sourceKey"));
|
||||
assertTrue(service.cancellationHistory(original.appointmentNo(),2,1).records().isEmpty());
|
||||
var second=service.createFromSources("internal-operator",request("history-second"));
|
||||
assertEquals(0,service.cancellationHistory(second.appointmentNo(),1,20).total());
|
||||
assertThrows(ResponseStatusException.class,()->service.cancellationHistory("AP-NOT-FOUND",1,20));
|
||||
var db=new JdbcTemplate(context.getBean(DataSource.class));
|
||||
var mapper=new ObjectMapper().findAndRegisterModules();
|
||||
var value=(com.fasterxml.jackson.databind.node.ObjectNode)mapper.readTree(db.queryForObject("SELECT response_json FROM ll_fba_appointment_command WHERE operation=?",String.class,"CANCEL:"+original.appointmentNo()));
|
||||
value.remove("cancelReason");
|
||||
db.update("UPDATE ll_fba_appointment_command SET response_json=? WHERE operation=?",mapper.writeValueAsString(value),"CANCEL:"+original.appointmentNo());
|
||||
assertNull(service.cancellationHistory(original.appointmentNo(),1,20).records().get(0).cancelReason());
|
||||
}
|
||||
}
|
||||
@Test void unknownQuantityOnReviewDoesNotSilentlyKeepThePreviousKnownQuantity() {
|
||||
try(var context=new AnnotationConfigApplicationContext(Config.class)) {
|
||||
var service=context.getBean(InboundFbaAppointmentService.class);var saved=service.createFromSources("operator",request("create"));
|
||||
|
||||
+18
-3
@@ -146,17 +146,17 @@ class InboundFbaAppointmentServiceTest {
|
||||
@Test void staleUpdateAndStaleCancellationDoNotWrite() {
|
||||
var result=create("original");
|
||||
assertThrows(ResponseStatusException.class,()->service.update("operator",result.appointmentNo(),new UpdateFbaAppointmentRequest(pending(null),2,"stale-update")));
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",result.appointmentNo(),2));
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",result.appointmentNo(),new CancelFbaAppointmentRequest(2,"收货方取消时段")));
|
||||
verify(headers,never()).update(any(InboundFbaAppointment.class),any());verify(headers,never()).updateById(any(InboundFbaAppointment.class));
|
||||
}
|
||||
@Test void activeDispatchBlocksChangingOrCancellingItsAppointment() {
|
||||
var result=create("original");when(query.countActiveDispatches(result.id())).thenReturn(1L);
|
||||
assertThrows(ResponseStatusException.class,()->service.update("operator",result.appointmentNo(),new UpdateFbaAppointmentRequest(pending("change"),0,"blocked-update")));
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",result.appointmentNo(),0));assertTrue(storedItems.stream().allMatch(InboundFbaAppointmentItem::getActive));
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",result.appointmentNo(),new CancelFbaAppointmentRequest(0,"收货方取消时段")));assertTrue(storedItems.stream().allMatch(InboundFbaAppointmentItem::getActive));
|
||||
}
|
||||
@Test void cancellationPreservesConfirmedFactsAndHistoryButCannotRestoreOverAnotherActiveClaim() {
|
||||
var result=service.createFromSources("operator",request("confirmed",confirmed(),"ORDER:1"));
|
||||
service.delete("operator",result.appointmentNo(),0);
|
||||
service.delete("operator",result.appointmentNo(),new CancelFbaAppointmentRequest(0,"收货方取消时段"));
|
||||
assertEquals("CANCELLED",storedHeaders.get(0).getAppointmentStatus());assertEquals(at,storedHeaders.get(0).getAppointmentAt());assertFalse(storedItems.get(0).getActive());
|
||||
assertEquals(result,service.createFromSources("operator",request("confirmed",confirmed(),"ORDER:1")));
|
||||
when(query.countActiveSources(anyList(),anyList(),eq(result.id()))).thenReturn(1L);
|
||||
@@ -172,4 +172,19 @@ class InboundFbaAppointmentServiceTest {
|
||||
var one=create("first");when(headers.selectList(any())).thenReturn(List.copyOf(storedHeaders));clearInvocations(items);
|
||||
var calendar=service.calendar(from,from.plusDays(30));assertEquals(one.items(),calendar.get(0).items());verify(items,times(1)).selectList(any());
|
||||
}
|
||||
@Test void cancellationRequiresReasonAndKeepsOriginalRemarkAndCommandIdentity() {
|
||||
var original=service.createFromSources("operator",request("cancel-test",pending("保留业务备注"),"ORDER:1"));
|
||||
for(String reason:Arrays.asList(null," ","x".repeat(1001))) {
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",original.appointmentNo(),new CancelFbaAppointmentRequest(0,reason)));
|
||||
}
|
||||
var cancel=new CancelFbaAppointmentRequest(0,"收货方取消时段");
|
||||
service.delete("operator",original.appointmentNo(),cancel);
|
||||
service.delete("operator",original.appointmentNo(),cancel);
|
||||
assertEquals("保留业务备注",service.detail(original.appointmentNo()).appointmentRemark());
|
||||
assertEquals(1,service.detail(original.appointmentNo()).version());
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("operator",original.appointmentNo(),new CancelFbaAppointmentRequest(0,"不同原因")));
|
||||
assertThrows(ResponseStatusException.class,()->service.delete("another-operator",original.appointmentNo(),cancel));
|
||||
assertThrows(ResponseStatusException.class,()->service.cancellationHistory(original.appointmentNo(),0,20));
|
||||
assertThrows(ResponseStatusException.class,()->service.cancellationHistory(original.appointmentNo(),1,101));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user