From 040f3bbd93cf32afdb7aaf3bbec51c9e6a9733c6 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 9 Oct 2026 22:31:51 +0800 Subject: [PATCH] fix(lastleg): preserve followup timestamp on replay --- .../appointmentplanning/AppointmentFollowupService.java | 4 +++- .../AppointmentFollowupPersistenceTest.java | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupService.java index 053d082a9..9999d11d4 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupService.java @@ -67,7 +67,9 @@ public class AppointmentFollowupService { value.setStage(request.stage()); value.setVersion(request.expectedVersion()+1); value.setContactedParty(clean(request.contactedParty())); value.setResult(request.result().trim()); value.setNextAction(clean(request.nextAction())); value.setFollowupDate(request.followupDate()); value.setOperatorId(operatorId); value.setOperatorName(clean(operatorName)); value.setCommandKey(request.idempotencyKey()); - value.setRequestHash(hash); value.setCreatedAt(LocalDateTime.now(ZoneOffset.UTC)); + value.setRequestHash(hash); + // Return the same precision that DATETIME(3) can persist, including the first response. + value.setCreatedAt(LocalDateTime.now(ZoneOffset.UTC).truncatedTo(java.time.temporal.ChronoUnit.MILLIS)); try { mapper.insert(value); } catch (org.springframework.dao.DuplicateKeyException conflict) { var raced = mapper.command(sourceKey,customerId,request.stage(),request.idempotencyKey()); diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupPersistenceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupPersistenceTest.java index 885ccc328..922d9dfdb 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupPersistenceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/appointmentplanning/AppointmentFollowupPersistenceTest.java @@ -35,6 +35,14 @@ class AppointmentFollowupPersistenceTest { assertThrows(RuntimeException.class, () -> mapper.insert(record(13L,"DRAYAGE_CONTACT",2,"key-a"))); assertThrows(RuntimeException.class, () -> mapper.insert(record(14L,"DRAYAGE_CONTACT",1,"key-b"))); assertEquals(2,mapper.count("ORDER:1","A")); + var sources=org.mockito.Mockito.mock(AppointmentSourceService.class); + org.mockito.Mockito.when(sources.resolve("ORDER:1","A",null)).thenReturn(new AppointmentSourceService.Source( + "ORDER:1","A",1L,"IN-1",null,"CONTAINER","100","US warehouse","FBA:ONT8","ONT8",20,"ORDER:1",true,java.util.List.of())); + var service=new AppointmentFollowupService(sources,mapper,new com.fasterxml.jackson.databind.ObjectMapper().findAndRegisterModules()); + var command=new AppointmentFollowupService.Save("FBA_RECEIVING",1,"timestamp-replay",null,"Test contact",null,null); + var saved=service.save("ORDER:1","A",command,"operator","Operator"); + var replay=service.save("ORDER:1","A",command,"operator","Operator"); + assertEquals(saved,replay,"Persisted timestamp precision must not change the response on retry"); } } private static AppointmentFollowup record(long id,String stage,int version,String key) {