fix: include overlapping overnight FBA appointments in calendar

This commit is contained in:
2026-10-10 12:40:35 +08:00
parent 6e1d90aafe
commit 721d92ac7b
3 changed files with 21 additions and 2 deletions
@@ -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<InboundFbaAppointment>()
.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));
}
@@ -67,7 +67,7 @@ paths:
get:
operationId: fbaAppointmentCalendar
summary: 按日期范围查询 FBA 预约
description: '权限:platform:appointment:list。按确认当地时间、计划时段或仅计划日期选取,排除取消/失败。仅有日期时不补造零点时段;无日期记录走未定时分页入口。固定批量查询预约及明细,不逐预约查详情。跨时区总览仍需按地点标注,不将当地值当成浏览器时区。'
description: '权限:platform:appointment:list。按确认当地时间、计划时段或仅计划日期选取,排除取消/失败。跨日时段只要与查询区间相交即返回,结束时间为不包含的边界;已有确认开始时间时只使用对应确认结束时间,不借用计划结束时间。仅有日期时不补造零点时段;无日期记录走未定时分页入口。固定批量查询预约及明细,不逐预约查详情。跨时区总览仍需按地点标注,不将当地值当成浏览器时区。'
parameters:
- name: from
in: query
@@ -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);