fix(self-operated): rank hot SKUs for selected dates

This commit is contained in:
2026-10-13 18:44:49 +08:00
parent 38958c891a
commit f151e74f5c
12 changed files with 80 additions and 28 deletions
@@ -35,6 +35,14 @@ class AnalyticsQueryServiceTest {
when(mapper.profile(22,"demo","W","C","SKU")).thenReturn(profile("PCS","INVENTORY_OBSERVATION",time(D.minusDays(35))));
when(mapper.daily(anyLong(),anyString(),anyString(),anyString(),anyString(),any(),any())).thenReturn(List.of());
}
@Test void customHotRangeMatchesSelectedDatesIncludingTodayAndLongHistory(){
var q=new AnalyticsQuery("demo","W",null,null,"CUSTOM",D.minusDays(44),D,null,null,1,20,null,null,null,null,null);
var result=service.hotSkus(q);
var cap=ArgumentCaptor.forClass(AnalyticsSqlRequest.class);verify(mapper).page(cap.capture());
assertEquals(D.minusDays(44),cap.getValue().from());assertEquals(D.plusDays(1),cap.getValue().until());
assertFalse(cap.getValue().completeWindow());assertEquals(D.minusDays(44),result.context().rangeStartDate());
assertThrows(com.baiyue.common.core.exception.BizException.class,()->new AnalyticsQuery("demo","W",null,null,"CUSTOM",D.minusDays(2),D,null,7,1,20,null,null,null,null,null).validate(false,false,true));
}
@Test void summaryUsesCountAndBoundPageWithoutLoadingProfilesOrHistory(){
when(mapper.count(any())).thenReturn(240L);
var response=service.salesSummary(chart("LAST_3_DAYS",null,null,null,null));
@@ -34,7 +34,7 @@ class AnalyticsReadMapperTest {
}
var b=bound(c,"page",Map.of("p",request("HOT")));
var names=b.getParameterMappings().stream().map(org.apache.ibatis.mapping.ParameterMapping::getProperty).toList();
for(String property:List.of("p.account","p.warehouse","p.customer","p.sku","p.salesRun","p.inventoryRun","p.anchor","p.windowFrom","p.size","p.offset"))assertTrue(names.contains(property),property);
for(String property:List.of("p.account","p.warehouse","p.customer","p.sku","p.salesRun","p.inventoryRun","p.anchor","p.from","p.until","p.size","p.offset"))assertTrue(names.contains(property),property);
var summaryNames=bound(c,"page",Map.of("p",request("SUMMARY"))).getParameterMappings().stream()
.map(org.apache.ibatis.mapping.ParameterMapping::getProperty).toList();
for(String property:List.of("p.from","p.until"))assertTrue(summaryNames.contains(property),property);
@@ -83,7 +83,8 @@ class AnalyticsReadMapperTest {
var p=Map.<String,Object>of("p",request(kind)); String sql=bound(c,"page",p).getSql();
assertTrue(sql.contains("st.snapshot_run_id=?")); assertTrue(sql.contains("st.source_account=?")); assertTrue(sql.contains("st.warehouse_code=?"));
assertTrue(sql.contains("AND st.product_quality=0"));
assertTrue(sql.contains("business_date>=TIMESTAMPADD(DAY,-30,CAST(? AS DATE)) AND business_date<TIMESTAMPADD(DAY,1,CAST(? AS DATE))"));
if("HOT".equals(kind))assertTrue(sql.contains("business_date>=LEAST(CAST(? AS DATE),TIMESTAMPADD(DAY,-30,CAST(? AS DATE))) AND business_date<GREATEST(CAST(? AS DATE),TIMESTAMPADD(DAY,1,CAST(? AS DATE)))"));
else assertTrue(sql.contains("business_date>=TIMESTAMPADD(DAY,-30,CAST(? AS DATE)) AND business_date<TIMESTAMPADD(DAY,1,CAST(? AS DATE))"));
assertTrue(sql.contains("GROUP BY customer_code,sku_code"));
assertTrue(sql.indexOf("GROUP BY customer_code,sku_code")<sql.indexOf("LEFT JOIN ll_selfop_inventory_snapshot"));
if("HOT".equals(kind))assertTrue(sql.indexOf("LIMIT ? OFFSET ?")<sql.indexOf("LEFT JOIN ll_selfop_inventory_snapshot"));
@@ -28,10 +28,11 @@ class DashboardQueryServiceTest {
when(mapper.report(any(),any())).thenReturn(List.of(reportRow));
var result=service.dashboard(q);
assertEquals(0,BigDecimal.ONE.compareTo(result.kpis().averageDailyOutboundQuantity30d().value()));
assertEquals(15,result.hotSkus().calculationWindowDays());assertEquals("PARTIAL",result.hotSkus().dataStatus());assertNull(result.hotSkus().items().get(0).averageDailyOutboundQuantity());
assertEquals(200L,result.hotSkus().eligibleCount());assertEquals(1,result.hotSkus().items().size());assertEquals("A",result.hotSkus().items().get(0).customerCode());
assertNull(result.coverageDistribution().candidateSkuCount());assertTrue(result.coverageDistribution().buckets().stream().allMatch(b->b.skuCount()==null));
assertNull(result.context().inventoryDrilldownExpiresAt());assertEquals(day.minusDays(30),result.context().fixedWindowStartDate());
verify(mapper).daily(any(),any(),any());verify(mapper).report(any(),any());verifyNoMoreInteractions(mapper);
verify(mapper).daily(any(),any(),any());verify(mapper,times(2)).report(any(),any());verifyNoMoreInteractions(mapper);
var json=new ObjectMapper().registerModule(new JavaTimeModule()).valueToTree(result);assertTrue(json.path("context").has("salesRunId"));assertFalse(json.path("context").has("versionContext"));
assertTrue(DashboardQueryService.class.getAnnotation(Transactional.class).readOnly());
}
@@ -39,6 +39,21 @@ class DashboardSqlSemanticTest {
assertTrue(bound.getSql().endsWith("ORDER BY business_date"));
}
}
@Test void selectedRangeHotUsesObservedSalesIncludingTodayAndMatchesTop100DespiteIncompleteCoverage()throws Exception{
fixture("RECENT","50","50","3");fixture("OLD","50","50",null);
daily("C","RECENT",D,"7","PCS");daily("C","OLD",D.minusDays(20),"100","PCS");
var b=request(false,true);
var p=new AnalyticsSqlRequest(b.account(),b.warehouse(),null,null,b.salesRun(),b.inventoryRun(),D.minusDays(2),D.plusDays(1),D,D.minusDays(2),
b.completeFrom(),b.completeUntil(),b.coveredFrom(),b.coveredUntil(),true,true,false,false,true,0,10,"HOT_DASHBOARD");
var rows=query(DashboardReadMapper.class,"report",Map.of("p",p,"o",options()));
assertEquals(1,rows.size());assertEquals("RECENT",((Map<?,?>)rows.get(0)).get("SKU_CODE"));
assertEquals(0,new BigDecimal("10").compareTo((BigDecimal)((Map<?,?>)rows.get(0)).get("MAIN_VALUE")));
var top=new AnalyticsSqlRequest(p.account(),p.warehouse(),null,null,p.salesRun(),p.inventoryRun(),p.from(),p.until(),D,p.from(),
p.completeFrom(),p.completeUntil(),p.coveredFrom(),p.coveredUntil(),true,true,false,false,true,0,100,"HOT");
var topRows=query(AnalyticsReadMapper.class,"page",Map.of("p",top));
assertEquals(1,topRows.size());var item=(AnalyticsReadRows.Aggregate)topRows.get(0);
assertEquals("RECENT",item.skuCode());assertEquals(0,new BigDecimal("10").compareTo(item.windowQuantity()));assertNull(item.sales30Quantity());
}
@Test void combinedReportKeepsStatisticsWhenAllThreeRankingsAreEmpty()throws Exception{
var empty=report();assertEquals(1,empty.size());assertEquals(0,count(empty.get(0),"total_skus"));
assertNull(empty.get(0).get("rank_code"));assertNull(empty.get(0).get("physical_quantity_known"));