perf(selfop): share dashboard aggregates and cover daily trend reads
This commit is contained in:
+8
-1
@@ -23,7 +23,8 @@ public class DashboardQueryService {
|
||||
base.completeFrom(),base.completeUntil(),base.coveredFrom(),base.coveredUntil(),base.salesKnown(),base.formalSales(),base.complete30(),base.complete30(),base.stockFresh(),0,10,"DASHBOARD");
|
||||
var o=new AnalyticsInventoryOptions(null,null,null,null,null,null,null,null);
|
||||
var days=mapper.daily(p,c.rangeStartDate(),c.rangeEndDateExclusive());
|
||||
Map<String,Object> stats=normalize(mapper.statistics(p,o));var ranked=mapper.rankings(p,o);
|
||||
var report=mapper.report(p,o).stream().map(DashboardQueryService::normalize).toList();
|
||||
Map<String,Object> stats=report.get(0);var ranked=report.stream().filter(row->row.get("rank_code")!=null).map(DashboardQueryService::ranked).toList();
|
||||
var trendPoints=DashboardSalesPolicy.points(c,snapshot.sourceKnown(),c.rangeStartDate(),c.rangeEndDateExclusive(),days);
|
||||
var trendSummary=SalesBucketPolicy.aggregate(trendPoints);
|
||||
var fixedPoints=DashboardSalesPolicy.points(c,snapshot.sourceKnown(),fixedFrom,fixedUntil,days);var fixed=SalesBucketPolicy.aggregate(fixedPoints);
|
||||
@@ -84,6 +85,12 @@ public class DashboardQueryService {
|
||||
return new DashboardResponses.Ranking(from,until,30,available?count(s,prefix+"_eligible"):null,available?unknown:null,items,status,reasons);
|
||||
}
|
||||
private static Map<String,Object> normalize(Map<String,Object> source){Map<String,Object> result=new HashMap<>();if(source!=null)source.forEach((key,value)->result.put(key.toLowerCase(Locale.ROOT),value));return result;}
|
||||
static DashboardReadMapper.Ranked ranked(Map<String,Object> row){
|
||||
return new DashboardReadMapper.Ranked((String)row.get("rank_code"),(String)row.get("customer_code"),(String)row.get("sku_code"),(String)row.get("product_name"),
|
||||
decimal(row,"main_value"),decimal(row,"owner_outbound"),row.get("order_count")==null?null:decimal(row,"order_count").longValueExact(),
|
||||
decimal(row,"physical_quantity"),decimal(row,"available_quantity"),decimal(row,"frozen_quantity"),decimal(row,"defective_physical_quantity"),
|
||||
decimal(row,"outbound_quantity30d"),decimal(row,"coverage_days"));
|
||||
}
|
||||
private static BigDecimal decimal(Map<String,Object> source,String key){Object value=source.get(key);return value==null?null:value instanceof BigDecimal decimal?decimal:new BigDecimal(value.toString());}
|
||||
private static long count(Map<String,Object> source,String key){BigDecimal value=decimal(source,key);return value==null?0:value.longValueExact();}
|
||||
private static BigDecimal divide30(BigDecimal value){return value==null?null:value.divide(BigDecimal.valueOf(30),12,RoundingMode.HALF_UP);}
|
||||
|
||||
+5
-5
@@ -12,13 +12,13 @@ public interface DashboardReadMapper {
|
||||
record Ranked(String rankCode,String customerCode,String skuCode,String productName,BigDecimal mainValue,
|
||||
BigDecimal ownerOutbound,Long orderCount,BigDecimal physicalQuantity,BigDecimal availableQuantity,
|
||||
BigDecimal frozenQuantity,BigDecimal defectivePhysicalQuantity,BigDecimal outboundQuantity30d,BigDecimal coverageDays){}
|
||||
@Select("SELECT business_date,SUM(CASE WHEN quantity_unit='PCS' AND outbound_quantity>=0 THEN outbound_quantity END) observed_quantity,"
|
||||
// Dense 366-day EXPLAIN: the natural unique-index plan performs non-covering reads;
|
||||
// the date-ordered covering index also matches warehouse daily GROUP BY/ORDER BY.
|
||||
@Select("SELECT /*+ INDEX(ll_selfop_sku_daily_sales idx_selfop_daily_window_cover) */ business_date,SUM(CASE WHEN quantity_unit='PCS' AND outbound_quantity>=0 THEN outbound_quantity END) observed_quantity,"
|
||||
+"COUNT(CASE WHEN quantity_unit IS NULL OR quantity_unit<>'PCS' OR outbound_quantity IS NULL OR outbound_quantity<0 THEN 1 END) invalid_row_count "
|
||||
+"FROM ll_selfop_sku_daily_sales WHERE sales_run_id=#{p.salesRun} AND source_account=#{p.account} AND warehouse_code=#{p.warehouse} "
|
||||
+"AND ((business_date>=#{chartFrom} AND business_date<#{chartUntil}) OR (business_date>=#{p.from} AND business_date<#{p.until})) GROUP BY business_date ORDER BY business_date")
|
||||
List<Day> daily(@Param("p") AnalyticsSqlRequest request,@Param("chartFrom") LocalDate chartFrom,@Param("chartUntil") LocalDate chartUntil);
|
||||
@SelectProvider(type=DashboardReadSql.class,method="statistics")
|
||||
Map<String,Object> statistics(@Param("p") AnalyticsSqlRequest request,@Param("o") AnalyticsInventoryOptions options);
|
||||
@SelectProvider(type=DashboardReadSql.class,method="rankings")
|
||||
List<Ranked> rankings(@Param("p") AnalyticsSqlRequest request,@Param("o") AnalyticsInventoryOptions options);
|
||||
@SelectProvider(type=DashboardReadSql.class,method="report")
|
||||
List<Map<String,Object>> report(@Param("p") AnalyticsSqlRequest request,@Param("o") AnalyticsInventoryOptions options);
|
||||
}
|
||||
|
||||
+13
-12
@@ -3,13 +3,16 @@ package com.baiyue.lastleg.selfoperated.analytics.query;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Three bounded aggregate queries: warehouse daily points, one statistics row, at most 30 ranked rows. */
|
||||
/** One shared inventory/sales aggregate returns statistics and at most 30 ranked rows. */
|
||||
public final class DashboardReadSql {
|
||||
private DashboardReadSql(){}
|
||||
public static String statistics(Map<String,Object> args){
|
||||
public static String report(Map<String,Object> args){
|
||||
var p=(AnalyticsSqlRequest)args.get("p");StringBuilder sql=new StringBuilder(AnalyticsInventorySql.cte(p));
|
||||
// SKU identity is unique already; DISTINCT is a MySQL merge barrier so the four consumers
|
||||
// share one calculation rather than expanding profile/stock/sales joins for each ranking.
|
||||
sql.append(", dashboard_result AS (SELECT DISTINCT * FROM result)");
|
||||
sql.append(", classified AS (SELECT r.*,CASE WHEN ").append(CoverageDistributionSql.candidate()).append(" THEN ")
|
||||
.append(CoverageDistributionSql.bucket()).append(" END bucket_code,").append(CoverageDistributionSql.unknownReason()).append(" unknown_reason FROM result r) SELECT COUNT(*) total_skus");
|
||||
.append(CoverageDistributionSql.bucket()).append(" END bucket_code,").append(CoverageDistributionSql.unknownReason()).append(" unknown_reason FROM dashboard_result r), statistics AS (SELECT COUNT(*) total_skus");
|
||||
for(String field:List.of("physical_quantity","available_quantity"))sql.append(",SUM(").append(field).append(") ").append(field).append("_known,COUNT(*)-COUNT(").append(field).append(") ").append(field).append("_unknown");
|
||||
sql.append(",COUNT(CASE WHEN physical_quantity>0 THEN 1 END) physical_skus,COUNT(CASE WHEN physical_quantity=0 THEN 1 END) excluded_zero_skus")
|
||||
.append(",COUNT(CASE WHEN bucket_code IS NOT NULL THEN 1 END) candidate_skus")
|
||||
@@ -24,15 +27,13 @@ public final class DashboardReadSql {
|
||||
}
|
||||
for(String reason:List.of("PHYSICAL_INVENTORY_UNKNOWN","AVAILABLE_INVENTORY_UNKNOWN","UNIT_UNKNOWN_OR_CONFLICT","SALES_WINDOW_UNAVAILABLE","OBSERVATION_INSUFFICIENT","METRIC_DEPENDENCY_UNKNOWN"))
|
||||
sql.append(",COUNT(CASE WHEN bucket_code='UNKNOWN' AND unknown_reason='").append(reason).append("' THEN 1 END) reason_").append(reason.toLowerCase(java.util.Locale.ROOT));
|
||||
return AnalyticsInventorySql.typed(sql+" FROM classified");
|
||||
}
|
||||
public static String rankings(Map<String,Object> args){var p=(AnalyticsSqlRequest)args.get("p");
|
||||
String sql=AnalyticsInventorySql.cte(p)
|
||||
+", hot_rank AS (SELECT * FROM result WHERE owner_outbound>0 ORDER BY owner_outbound DESC,order_count DESC,sku_code ASC,customer_code ASC LIMIT 10)"
|
||||
+", long_rank AS (SELECT * FROM result WHERE "+CoverageDistributionSql.finitePositive()+" ORDER BY coverage_days DESC,sku_code ASC,customer_code ASC LIMIT 10)"
|
||||
+", no_rank AS (SELECT * FROM result WHERE no_outbound=1 ORDER BY physical_quantity DESC,sku_code ASC,customer_code ASC LIMIT 10) ";
|
||||
sql+=select("HOT","owner_outbound","hot_rank")+" UNION ALL "+select("LONG","coverage_days","long_rank")+" UNION ALL "+select("NO_OUTBOUND","physical_quantity","no_rank");
|
||||
return AnalyticsInventorySql.typed(sql);
|
||||
sql.append(" FROM classified)")
|
||||
.append(", hot_rank AS (SELECT * FROM dashboard_result WHERE owner_outbound>0 ORDER BY owner_outbound DESC,order_count DESC,sku_code ASC,customer_code ASC LIMIT 10)")
|
||||
.append(", long_rank AS (SELECT * FROM dashboard_result WHERE ").append(CoverageDistributionSql.finitePositive()).append(" ORDER BY coverage_days DESC,sku_code ASC,customer_code ASC LIMIT 10)")
|
||||
.append(", no_rank AS (SELECT * FROM dashboard_result WHERE no_outbound=1 ORDER BY physical_quantity DESC,sku_code ASC,customer_code ASC LIMIT 10)")
|
||||
.append(", rankings AS (").append(select("HOT","owner_outbound","hot_rank")).append(" UNION ALL ").append(select("LONG","coverage_days","long_rank"))
|
||||
.append(" UNION ALL ").append(select("NO_OUTBOUND","physical_quantity","no_rank")).append(") SELECT s.*,r.* FROM statistics s LEFT JOIN rankings r ON 1=1");
|
||||
return AnalyticsInventorySql.typed(sql.toString());
|
||||
}
|
||||
private static String select(String kind,String value,String table){return "SELECT '"+kind+"' rank_code,customer_code,sku_code,product_name,"+value+" main_value,owner_outbound,order_count,physical_quantity,available_quantity,frozen_quantity,defective_physical_quantity,outbound_quantity30d,coverage_days FROM "+table;}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user