diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundFbaAppointmentService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundFbaAppointmentService.java index b3a63b038..fa694aaaf 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundFbaAppointmentService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundFbaAppointmentService.java @@ -36,7 +36,7 @@ public class InboundFbaAppointmentService { if(from==null || to==null || !to.isAfter(from) || ChronoUnit.DAYS.between(from,to)>42) throw bad("日历查询范围必须为1至42天"); var query=new LambdaQueryWrapper() .notIn(InboundFbaAppointment::getAppointmentStatus,List.of("CANCELLED","FAILED")) - .apply("COALESCE(appointment_at,planned_at,planned_date)>={0} AND COALESCE(appointment_at,planned_at,planned_date)<{1}",from.atStartOfDay(),to.atStartOfDay()) + .apply("COALESCE(appointment_at,planned_at,planned_date)<{1} AND (COALESCE(appointment_at,planned_at,planned_date)>={0} OR CASE WHEN appointment_at IS NOT NULL THEN appointment_end_at WHEN planned_at IS NOT NULL THEN planned_end_at ELSE NULL END>{0})",from.atStartOfDay(),to.atStartOfDay()) .orderByAsc(InboundFbaAppointment::getId); return responses(appointmentMapper.selectList(query)); } diff --git a/lastleg-service/src/main/resources/openapi/fba-appointment-calendar-v1.yaml b/lastleg-service/src/main/resources/openapi/fba-appointment-calendar-v1.yaml index cd04110cb..bce37a011 100644 --- a/lastleg-service/src/main/resources/openapi/fba-appointment-calendar-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/fba-appointment-calendar-v1.yaml @@ -67,7 +67,7 @@ paths: get: operationId: fbaAppointmentCalendar summary: 按日期范围查询 FBA 预约 - description: '权限:platform:appointment:list。按确认当地时间、计划时段或仅计划日期选取,排除取消/失败。仅有日期时不补造零点时段;无日期记录走未定时分页入口。固定批量查询预约及明细,不逐预约查详情。跨时区总览仍需按地点标注,不将当地值当成浏览器时区。' + description: '权限:platform:appointment:list。按确认当地时间、计划时段或仅计划日期选取,排除取消/失败。跨日时段只要与查询区间相交即返回,结束时间为不包含的边界;已有确认开始时间时只使用对应确认结束时间,不借用计划结束时间。仅有日期时不补造零点时段;无日期记录走未定时分页入口。固定批量查询预约及明细,不逐预约查详情。跨时区总览仍需按地点标注,不将当地值当成浏览器时区。' parameters: - name: from in: query diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/FbaAppointmentPersistenceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/FbaAppointmentPersistenceTest.java index ef459088e..d07023399 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/FbaAppointmentPersistenceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/FbaAppointmentPersistenceTest.java @@ -25,6 +25,25 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; class FbaAppointmentPersistenceTest { + @Test void calendarIncludesOverlappingSlotsButNotAnExclusiveMidnightEnd() { + try(var context=new AnnotationConfigApplicationContext(Config.class)) { + var service=context.getBean(InboundFbaAppointmentService.class); + service.createFromSources("operator",request("overlap")); + var db=new JdbcTemplate(context.getBean(DataSource.class)); + var day=java.time.LocalDate.of(2026,10,15); + db.update("UPDATE ll_inbound_fba_appointment SET planned_at='2026-10-14 23:00:00',planned_end_at='2026-10-15 01:00:00'"); + assertEquals(1,service.calendar(day,day.plusDays(1)).size()); + db.update("UPDATE ll_inbound_fba_appointment SET planned_end_at='2026-10-15 00:00:00'"); + assertTrue(service.calendar(day,day.plusDays(1)).isEmpty()); + db.update("UPDATE ll_inbound_fba_appointment SET appointment_at='2026-10-14 23:00:00',appointment_end_at='2026-10-15 01:00:00'"); + assertEquals(1,service.calendar(day,day.plusDays(1)).size()); + db.update("UPDATE ll_inbound_fba_appointment SET appointment_end_at=NULL,planned_end_at='2026-10-16 01:00:00'"); + assertTrue(service.calendar(day,day.plusDays(1)).isEmpty()); + db.update("UPDATE ll_inbound_fba_appointment SET appointment_at=NULL,planned_at=NULL,planned_end_at=NULL,planned_date='2026-10-15'"); + assertEquals(1,service.calendar(day,day.plusDays(1)).size()); + assertTrue(service.calendar(day.plusDays(1),day.plusDays(2)).isEmpty()); + } + } interface OrderVersionProbe { @org.apache.ibatis.annotations.Select("SELECT version FROM ll_order WHERE id=#{id}") int version(long id);