feat(selfop): provide frozen turnover batches for backend Excel exports
This commit is contained in:
+6
-3
@@ -16,6 +16,7 @@ import com.baiyue.lastleg.fleet.quote.FleetQuoteModels;
|
||||
import com.baiyue.lastleg.fleet.quote.FleetQuoteService;
|
||||
import com.baiyue.lastleg.fleet.reporting.TripOperationsModels;
|
||||
import com.baiyue.lastleg.fleet.reporting.TripOperationsService;
|
||||
import com.baiyue.lastleg.selfoperated.analytics.query.InventoryTurnoverExport;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -41,6 +42,7 @@ public class LastlegExportProviderService {
|
||||
@Resource private VehicleApplicationService vehicles;
|
||||
@Resource private FleetQuoteService fleetQuotes;
|
||||
@Resource private TripOperationsService operations;
|
||||
@Resource private InventoryTurnoverExport turnover;
|
||||
@Resource private ObjectMapper json;
|
||||
@Resource private Validator validator;
|
||||
@Value("${baiyue.security.internal-service-token}") private String signingKey;
|
||||
@@ -51,15 +53,16 @@ public class LastlegExportProviderService {
|
||||
}
|
||||
if (request.taskId() == null || request.requestedBy() == null || request.dispatchVersion() < 1
|
||||
|| request.limit() < 1 || request.limit() > 200) throw invalid("导出批次参数无效");
|
||||
List<ExportColumn> columns;
|
||||
try { columns = LastlegExportColumns.columns(type); }
|
||||
catch (IllegalArgumentException e) { throw invalid("不支持该导出类型"); }
|
||||
int offset = cursor(request.cursor());
|
||||
if (offset % request.limit() != 0) throw invalid("导出游标无效");
|
||||
String snapshot = signature(type, request);
|
||||
boolean first = request.snapshotToken() == null;
|
||||
if ((first && offset != 0) || (!first && !MessageDigest.isEqual(snapshot.getBytes(StandardCharsets.UTF_8),
|
||||
request.snapshotToken().getBytes(StandardCharsets.UTF_8)))) throw invalid("导出筛选快照不匹配");
|
||||
if (InventoryTurnoverExport.TYPE.equals(type)) return turnover.batch(request, offset, snapshot);
|
||||
List<ExportColumn> columns;
|
||||
try { columns = LastlegExportColumns.columns(type); }
|
||||
catch (IllegalArgumentException e) { throw invalid("不支持该导出类型"); }
|
||||
Map<String, Object> parameters = new TreeMap<>(request.parameters() == null ? Map.of() : request.parameters());
|
||||
String customer = text(parameters.remove("customerId"));
|
||||
if (customer != null && !customer.matches("[1-9][0-9]*")) throw invalid("客户参数无效");
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package com.baiyue.lastleg.selfoperated.analytics.query;
|
||||
|
||||
import com.baiyue.common.core.export.ExportContracts.*;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import static com.baiyue.lastleg.selfoperated.analytics.query.InventoryTurnoverResponses.*;
|
||||
|
||||
/** Download-center projection: the browser supplies column keys, never headings or cell values. */
|
||||
@Service
|
||||
public class InventoryTurnoverExport {
|
||||
public static final String TYPE = "SELFOP_INVENTORY_TURNOVER";
|
||||
private final InventoryTurnoverQueryService queries;
|
||||
private final ObjectMapper json;
|
||||
public InventoryTurnoverExport(InventoryTurnoverQueryService queries, ObjectMapper json) { this.queries=queries; this.json=json; }
|
||||
private static final Map<String,String> LABELS = Map.ofEntries(
|
||||
Map.entry("sku","SKU"), Map.entry("productName","商品名称"), Map.entry("customerCode","所属货主"),
|
||||
Map.entry("physicalQuantity","实物件数"), Map.entry("availableQuantity","正品可售"), Map.entry("transitQuantity","在途"),
|
||||
Map.entry("frozenQuantity","冻结件数"), Map.entry("defectiveQuantity","次品实物"), Map.entry("physicalSkuCount","有实物SKU数"),
|
||||
Map.entry("inventoryVolumeM3","库存立方数"), Map.entry("outboundVolumesM3","出库立方数"), Map.entry("unitVolumeM3","单件体积"),
|
||||
Map.entry("outbound7","7日出库"), Map.entry("outbound15","15日出库"), Map.entry("outbound30","30日出库"),
|
||||
Map.entry("averageDailyOutboundQuantity","日均"), Map.entry("inventoryCoverageDays","预计可售天数"),
|
||||
Map.entry("quantityTurnoverRate","期间周转率"), Map.entry("quantityTurnoverCount","期间件数周转"),
|
||||
Map.entry("averagePhysicalQuantity","期间平均实物"), Map.entry("lastObservedOutboundDate","最近已观测出库日"),
|
||||
Map.entry("noOutboundInWindow","无出库SKU / 实物"), Map.entry("slowCoverage","慢消化SKU / 待判断"),
|
||||
Map.entry("period","统计期间"), Map.entry("periodOutbound","期间出库件数"), Map.entry("historySource","历史库存来源"),
|
||||
Map.entry("historyDays","合格历史库存天数"), Map.entry("historyReason","历史库存说明"));
|
||||
private static final List<String> REQUIRED = List.of("period","periodOutbound","averagePhysicalQuantity","quantityTurnoverRate","historySource","historyDays","historyReason");
|
||||
|
||||
public ExportBatchResponse batch(ExportBatchRequest request, int offset, String signature) {
|
||||
if(request.limit()>100) throw invalid("库存周转每批最多100条");
|
||||
Map<String,Object> parameters=new LinkedHashMap<>(request.parameters()==null?Map.of():request.parameters());
|
||||
Object rawColumns=parameters.remove("columns"), rawTotal=parameters.remove("expectedTotal");
|
||||
if(!(rawColumns instanceof List<?> selected)||selected.isEmpty()||selected.size()>LABELS.size()) throw invalid("导出列无效");
|
||||
Set<String> keys=new LinkedHashSet<>();
|
||||
for(Object key:selected) if(!(key instanceof String s)||!LABELS.containsKey(s)||!keys.add(s)) throw invalid("导出列重复或不支持");
|
||||
keys.addAll(REQUIRED);
|
||||
long expected;
|
||||
try { expected=new BigDecimal(String.valueOf(rawTotal)).longValueExact(); }
|
||||
catch(RuntimeException e) { throw invalid("导出条数无效"); }
|
||||
if(expected<0||expected>200000||offset>expected) throw invalid("导出条数超出范围");
|
||||
if(parameters.containsKey("current")||parameters.containsKey("size")||parameters.containsKey("includeOverview")) throw invalid("导出不接受列表分页参数");
|
||||
InventoryTurnoverRequest.validateParameters(parameters.keySet());
|
||||
parameters.put("current",offset/request.limit()+1);parameters.put("size",request.limit());parameters.put("includeOverview",false);
|
||||
InventoryTurnoverRequest query;
|
||||
try { query=json.convertValue(parameters,InventoryTurnoverRequest.class); }
|
||||
catch(IllegalArgumentException e) { throw invalid("导出筛选无效"); }
|
||||
if(query.snapshotContext()==null||query.snapshotContext().isBlank()||query.endDate()==null||query.baseWindowDays()==null) throw invalid("请先查询后导出");
|
||||
boolean owner="OWNER".equals(query.group());
|
||||
if(owner&&(keys.contains("sku")||keys.contains("productName")||keys.contains("unitVolumeM3")||keys.contains("inventoryCoverageDays"))
|
||||
||!owner&&(keys.contains("physicalSkuCount")||keys.contains("noOutboundInWindow")||keys.contains("slowCoverage"))) throw invalid("导出列不适用于当前分组");
|
||||
Response result=queries.query(query);
|
||||
if(result.total()!=expected||!query.snapshotContext().equals(result.context().snapshotContext())
|
||||
||result.rows().size()!=Math.min(request.limit(),expected-offset)) throw invalid("导出数据范围或版本已变化,请整体刷新后重新导出");
|
||||
Set<List<String>> seen=new HashSet<>();
|
||||
for(Row row:result.rows()) if(!seen.add(Arrays.asList(row.customerCode(),row.sku()))) throw invalid("导出数据重复");
|
||||
List<ExportColumn> columns=keys.stream().map(key->new ExportColumn(key,title(key,query),ExportColumnType.STRING,24)).toList();
|
||||
List<Map<String,Object>> rows=result.rows().stream().map(row->{
|
||||
Map<String,Object> values=new LinkedHashMap<>();for(String key:keys)values.put(key,cell(row,key,query));return values;
|
||||
}).toList();
|
||||
String hash;
|
||||
try { hash="sha256:"+HexFormat.of().formatHex(java.security.MessageDigest.getInstance("SHA-256").digest(json.writeValueAsBytes(columns))); }
|
||||
catch(Exception e) { throw new IllegalStateException("Cannot hash turnover export columns",e); }
|
||||
long next=offset+rows.size();
|
||||
return new ExportBatchResponse(1,hash,signature,request.snapshotToken()==null?columns:List.of(),rows,String.valueOf(next),next<expected,request.snapshotToken()==null?expected:null);
|
||||
}
|
||||
private static String title(String key,InventoryTurnoverRequest q) {
|
||||
if(key.equals("outboundVolumesM3"))return q.days()+"日出库立方数";
|
||||
if(key.equals("averageDailyOutboundQuantity"))return q.days()+"日日均";
|
||||
return LABELS.get(key);
|
||||
}
|
||||
static String cell(Row r,String key,InventoryTurnoverRequest q) {
|
||||
return switch(key) {
|
||||
case "sku" -> text(r.sku()); case "productName" -> text(r.productName());
|
||||
case "customerCode" -> r.customerName()==null||r.customerName().isBlank()?text(r.customerCode()):r.customerName();
|
||||
case "physicalQuantity" -> total(r.physicalQuantity()); case "availableQuantity" -> total(r.availableQuantity());
|
||||
case "transitQuantity" -> total(r.transitQuantity()); case "frozenQuantity" -> total(r.frozenQuantity()); case "defectiveQuantity" -> total(r.defectiveQuantity());
|
||||
case "physicalSkuCount" -> number(r.physicalSkuCount(),2);
|
||||
case "inventoryVolumeM3" -> volume(r.inventoryVolumeM3(),false);
|
||||
case "outboundVolumesM3" -> volume(r.outboundVolumesM3().stream().filter(v->v.days()==q.days()).map(VolumeWindow::volume).findFirst().orElse(null),true);
|
||||
case "unitVolumeM3" -> number(r.unitVolumeM3(),9)+(r.unitVolumeM3()==null?"":" m³")+";"+switch(text(r.dimensionSource())) { case "WAREHOUSE_MEASURED" -> "仓库实测尺寸";case "PRODUCT_DECLARED" -> "商品尺寸估算";default -> "缺少有效尺寸"; };
|
||||
case "outbound7","outbound15","outbound30" -> window(r,Integer.parseInt(key.substring(8)));
|
||||
case "periodOutbound" -> window(r,q.days());
|
||||
case "averageDailyOutboundQuantity" -> number(r.averageDailyOutboundQuantity(),2);
|
||||
case "inventoryCoverageDays" -> number(r.inventoryCoverageDays(),1)+";"+reason(r.coverageStatus());
|
||||
case "averagePhysicalQuantity" -> number(r.averagePhysicalQuantity(),2);
|
||||
case "quantityTurnoverRate" -> r.quantityTurnoverCount()==null?"—":number(r.quantityTurnoverCount().multiply(BigDecimal.valueOf(100)),2)+"%";
|
||||
case "quantityTurnoverCount" -> r.quantityTurnoverCount()==null?"—":number(r.quantityTurnoverCount(),2)+" 次/"+q.days()+"日";
|
||||
case "period" -> "截至 "+q.endDate()+",最近 "+q.days()+" 天";
|
||||
case "historySource" -> "库存流水还原(日末实物库存)";
|
||||
case "historyDays" -> r.qualifiedSampleDays()+"/"+r.requiredSampleDays();
|
||||
case "historyReason" -> reasons(r.sampleReasonCodes());
|
||||
case "lastObservedOutboundDate" -> r.lastObservedOutboundDate()==null?"已采集历史内未见出库":r.lastObservedOutboundDate().toString();
|
||||
case "noOutboundInWindow" -> number(r.noOutboundInWindow().matchedSkuCount(),2)+" SKU / "+number(r.noOutboundInWindow().knownPhysicalQuantity(),2)+" 件;待判断 "+r.noOutboundInWindow().unknownSkuCount()+" SKU";
|
||||
case "slowCoverage" -> q.coverageDaysThreshold()==null?"未设查询阈值":number(r.slowCoverage().matchedSkuCount(),2)+" SKU / "+r.slowCoverage().unknownSkuCount()+" SKU";
|
||||
default -> throw invalid("不支持的导出列");
|
||||
};
|
||||
}
|
||||
static String number(BigDecimal value,int decimals) {
|
||||
if(value==null)return "—";
|
||||
BigDecimal threshold=BigDecimal.ONE.movePointLeft(decimals);
|
||||
if(value.signum()>0&&value.compareTo(threshold)<0)return "<"+threshold.setScale(decimals).toPlainString();
|
||||
var f=new java.text.DecimalFormat("#,##0"+(decimals>0?"."+"#".repeat(decimals):""),java.text.DecimalFormatSymbols.getInstance(Locale.ROOT));
|
||||
f.setRoundingMode(RoundingMode.HALF_UP);return f.format(value);
|
||||
}
|
||||
private static String total(Total t) {
|
||||
if(t.value()!=null)return number(t.value(),2);
|
||||
if(t.knownSubtotal()==null&&t.unknownSkuCount()==0)return reasons(t.reasonCodes());
|
||||
return "已知 "+number(t.knownSubtotal(),2)+";"+t.unknownSkuCount()+" SKU待核验";
|
||||
}
|
||||
private static String window(Row r,int days) {
|
||||
return r.windows().stream().filter(w->w.days()==days).findFirst().map(w->"COMPLETE".equals(w.dataStatus())?number(w.outboundQuantity(),2):"已采集 "+number(w.observedQuantity(),2)+",覆盖不足").orElse("—");
|
||||
}
|
||||
private static String volume(Volume v,boolean outbound) {
|
||||
if(v==null)return "—;数量或尺寸待确认";
|
||||
List<String> notes=new ArrayList<>();
|
||||
if(v.value()==null)notes.add(v.knownSubtotal()==null?"暂无已知小计":"已知小计 "+number(v.knownSubtotal(),6)+" m³");
|
||||
if(v.missingDimensionSkuCount()>0)notes.add("缺尺寸 "+v.missingDimensionSkuCount()+" SKU / "+number(v.missingDimensionQuantity(),2)+" 件");
|
||||
if(v.unknownQuantitySkuCount()>0)notes.add("数量待确认 "+v.unknownQuantitySkuCount()+" SKU");
|
||||
if(v.declaredDimensionQuantity()!=null&&v.declaredDimensionQuantity().signum()>0)notes.add("其中 "+number(v.declaredDimensionQuantity(),2)+" 件按商品尺寸估算");
|
||||
if(outbound)notes.add("按当前商品尺寸估算");
|
||||
else if(v.value()!=null&&v.value().signum()==0)notes.add("已知体积为0");
|
||||
else if(v.value()!=null&&(v.declaredDimensionQuantity()==null||v.declaredDimensionQuantity().signum()==0))notes.add("按仓库实测尺寸计算");
|
||||
return number(v.value(),6)+(v.value()==null?"":" m³")+";"+(notes.isEmpty()?"数量或尺寸待确认":String.join(";",notes));
|
||||
}
|
||||
private static String reasons(List<String> reasons){return reasons==null||reasons.isEmpty()?"—":String.join("、",reasons.stream().map(InventoryTurnoverExport::reason).toList());}
|
||||
private static String reason(String reason){return TurnoverExportReasons.LABELS.getOrDefault(reason==null?"":reason,"数据待核验");}
|
||||
private static String text(String value){return value==null||value.isBlank()?"—":value;}
|
||||
private static BizException invalid(String message){return new BizException(message);}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.baiyue.lastleg.selfoperated.analytics.query;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/** Business explanations retained in server-generated spreadsheets. */
|
||||
final class TurnoverExportReasons {
|
||||
private TurnoverExportReasons() {}
|
||||
static final Map<String,String> LABELS = Map.ofEntries(
|
||||
Map.entry("COMPLETE","完整"),
|
||||
Map.entry("PARTIAL","未完整"),
|
||||
Map.entry("UNKNOWN","未知"),
|
||||
Map.entry("QUEUED","排队中"),
|
||||
Map.entry("RUNNING","执行中"),
|
||||
Map.entry("WAITING","等待中"),
|
||||
Map.entry("WAITING_UPSTREAM","等待上游"),
|
||||
Map.entry("WAITING_SOURCE","等待来源数据"),
|
||||
Map.entry("RETRY_WAIT","待重试"),
|
||||
Map.entry("YIELDED","预算等待"),
|
||||
Map.entry("FAILED","失败"),
|
||||
Map.entry("DEAD","待处理"),
|
||||
Map.entry("SUCCEEDED","执行成功"),
|
||||
Map.entry("ROUTED","账号共享采集完成"),
|
||||
Map.entry("SAMPLE_DONE","测试样本结束"),
|
||||
Map.entry("PUBLISHED","已发布"),
|
||||
Map.entry("REUSED","沿用已有结果"),
|
||||
Map.entry("NOT_PUBLISHED","未发布"),
|
||||
Map.entry("PENDING","待处理"),
|
||||
Map.entry("BLOCKED","修订阻断"),
|
||||
Map.entry("OFF","关闭"),
|
||||
Map.entry("VALIDATION","验证采样"),
|
||||
Map.entry("BUSINESS","正式采集"),
|
||||
Map.entry("FRESH","有效"),
|
||||
Map.entry("STALE","过期"),
|
||||
Map.entry("INVALID","异常"),
|
||||
Map.entry("OBSERVING","观察中"),
|
||||
Map.entry("HEALTHY","正常"),
|
||||
Map.entry("STALE_HEARTBEAT","心跳过期,执行状态待确认"),
|
||||
Map.entry("UNCONFIRMED","执行状态待确认"),
|
||||
Map.entry("TERMINATED","尝试已结束"),
|
||||
Map.entry("ABORTED","已停止"),
|
||||
Map.entry("NOT_STARTED","尚未开始"),
|
||||
Map.entry("FETCHING","采集中"),
|
||||
Map.entry("FETCHED","采集完成"),
|
||||
Map.entry("VALIDATING","校验中"),
|
||||
Map.entry("BUILDING","计算中"),
|
||||
Map.entry("READY","待发布"),
|
||||
Map.entry("INITIALIZING","首采中"),
|
||||
Map.entry("COVERED","覆盖完整"),
|
||||
Map.entry("ACCOUNT_DISCOVERY","账号共享出库目录"),
|
||||
Map.entry("WAREHOUSE_SYNC","本仓采集"),
|
||||
Map.entry("VALID","有效"),
|
||||
Map.entry("CURRENT","已核对"),
|
||||
Map.entry("LAGGING","核对滞后"),
|
||||
Map.entry("MISSING","尚无可用数据"),
|
||||
Map.entry("UNCOVERED","覆盖不足"),
|
||||
Map.entry("INCOMPLETE","未完整"),
|
||||
Map.entry("REBUILD_REQUIRED","销量待重新统计发布"),
|
||||
Map.entry("NO_AVAILABLE_INVENTORY","无可售库存"),
|
||||
Map.entry("UNIT_UNKNOWN_OR_CONFLICT","数量单位未知或不一致"),
|
||||
Map.entry("SALES_UNTRUSTED","销量待核对"),
|
||||
Map.entry("COVERAGE_INSUFFICIENT","覆盖不足"),
|
||||
Map.entry("OBSERVATION_INSUFFICIENT","观察期不足"),
|
||||
Map.entry("SALES_UNKNOWN","销量未知"),
|
||||
Map.entry("NO_OUTBOUND_IN_WINDOW","所选窗口无出库"),
|
||||
Map.entry("NO_OUTBOUND_30_DAYS","近30天无出库"),
|
||||
Map.entry("INVENTORY_INVALID","库存异常"),
|
||||
Map.entry("INVENTORY_STALE","库存快照过期"),
|
||||
Map.entry("INVENTORY_UNKNOWN","库存未知"),
|
||||
Map.entry("UNASSESSABLE_SKUS","存在待评估SKU"),
|
||||
Map.entry("INVENTORY_UNAVAILABLE","库存资料不可用"),
|
||||
Map.entry("SALES_UNAVAILABLE","销量资料不可用"),
|
||||
Map.entry("PHYSICAL_INVENTORY_UNKNOWN","实物库存待核验"),
|
||||
Map.entry("AVAILABLE_INVENTORY_UNKNOWN","可售库存待核验"),
|
||||
Map.entry("SALES_QUANTITY_OR_UNIT_INVALID","出库数量或单位异常"),
|
||||
Map.entry("NO_ELIGIBLE_SKUS","暂无符合条件的SKU"),
|
||||
Map.entry("NO_OUTBOUND_30D","近30天无出库"),
|
||||
Map.entry("COVERAGE_GT_THRESHOLD","可售天数超过本次阈值"),
|
||||
Map.entry("FROZEN_STOCK","存在冻结库存"),
|
||||
Map.entry("DEFECTIVE_STOCK","存在次品实物库存"),
|
||||
Map.entry("UNASSESSABLE","数据不足/待观察"),
|
||||
Map.entry("THRESHOLD_NOT_SET","未设置查询阈值"),
|
||||
Map.entry("NO_PHYSICAL_INVENTORY","无实物库存"),
|
||||
Map.entry("METRIC_DEPENDENCY_UNKNOWN","该指标依赖资料未知"),
|
||||
Map.entry("SALES_WINDOW_UNAVAILABLE","同期完整销量不足"),
|
||||
Map.entry("SALES_WINDOW_INCOMPLETE","所选时段销量尚未采齐"),
|
||||
Map.entry("RISK_DEPENDENCY_UNKNOWN","部分SKU关注判断未知"),
|
||||
Map.entry("NO_OUTBOUND_IN_COLLECTED_HISTORY","已采集历史内未见出库"),
|
||||
Map.entry("SALES_NOT_COVERED","销量未覆盖"),
|
||||
Map.entry("SKU_HISTORY_UNKNOWN","SKU历史未知"),
|
||||
Map.entry("QUANTITY_UNIT_UNKNOWN","数量单位未知"),
|
||||
Map.entry("INVENTORY_NOT_PUBLISHED","库存尚未发布"),
|
||||
Map.entry("SALES_NOT_PUBLISHED","销量尚未发布"),
|
||||
Map.entry("OBSERVATION_INCOMPLETE","观察期不足"),
|
||||
Map.entry("SCOPE_CHANGED","配置范围已变化"),
|
||||
Map.entry("UPSTREAM_NOT_READY","账号共享上游未就绪"),
|
||||
Map.entry("TASK_NOT_RETRYABLE","任务当前不可恢复"),
|
||||
Map.entry("COLLECTION_MODE_NOT_BUSINESS","当前不是正式采集模式"),
|
||||
Map.entry("VALIDATION_NOT_RETRYABLE","验证会话不可恢复"),
|
||||
Map.entry("ACCOUNT_SHARED_TASK","账号共享任务由协调器恢复"),
|
||||
Map.entry("SOURCE_TIMEZONE_UNKNOWN","来源时区未确认"),
|
||||
Map.entry("SALES_DAY_INCOMPLETE","销量日期未完整"),
|
||||
Map.entry("SALES_NOT_COVERED_OR_SKU_HISTORY_UNKNOWN","销量未覆盖或SKU历史未知"),
|
||||
Map.entry("SCOPE_LEASE_BUSY","本仓任务正在执行"),
|
||||
Map.entry("COLLECTION_OFF","采集模式关闭"),
|
||||
Map.entry("COLLECTION_MODE_INVALID","采集模式配置无效"),
|
||||
Map.entry("BUSINESS_ENVIRONMENT_REJECTED","正式采集只允许生产环境"),
|
||||
Map.entry("EXECUTION_DISABLED","采集执行未启用"),
|
||||
Map.entry("WAREHOUSE_NOT_CONFIGURED","仓库未配置"),
|
||||
Map.entry("TIMEZONE_OR_UNIT_NOT_CONFIRMED","时间或件数口径配置不符合确认值"),
|
||||
Map.entry("ACCOUNT_PAUSED","账号采集已暂停"),
|
||||
Map.entry("ACCOUNT_COOLDOWN","账号处于冷却期"),
|
||||
Map.entry("EXCLUSIVE_GRANT_NOT_VALID","账号独占窗口授权无效"),
|
||||
Map.entry("DAILY_BUDGET_NOT_CONFIGURED","账号日额度未配置"),
|
||||
Map.entry("DAILY_BUDGET_EXHAUSTED","账号日额度已用完"),
|
||||
Map.entry("SALES_MISSING","尚无销量发布版本"),
|
||||
Map.entry("INVENTORY_MISSING","尚无库存发布版本"),
|
||||
Map.entry("SALES_BLOCKED","销量修订阻断"),
|
||||
Map.entry("SALES_STALE","销量数据过期"),
|
||||
Map.entry("REVISION_REVIEW_UNKNOWN","修订核对状态未知"),
|
||||
Map.entry("REVISION_REVIEW_LAGGING","修订核对滞后"),
|
||||
Map.entry("SALES_REBUILD_REQUIRED","销量有变动,待重新统计发布"),
|
||||
Map.entry("TODAY_INCOMPLETE","今日尚未结束,不能作为完整自然日"),
|
||||
Map.entry("SALES_SOURCE_TIMEZONE_UNKNOWN","销量来源时区未确认"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_MISSING","缺少独立库存对账记录"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_QUALITY_MISSING","独立库存品质记录不完整"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_INVALID","独立库存记录无效"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_COVERAGE_MISSING","库存对账区间流水未齐"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_MISMATCH","流水与独立库存记录不符"),
|
||||
Map.entry("INDEPENDENT_ANCHOR_FLOW_UNKNOWN","库存对账流水待核验"),
|
||||
Map.entry("ANCHOR_TIME_AMBIGUOUS","采集库存时发生变动,待稳定库存记录核验"),
|
||||
Map.entry("ANCHOR_MISSING","缺少库存锚点"),
|
||||
Map.entry("FLOW_COVERAGE_GAP","库存流水覆盖不完整"),
|
||||
Map.entry("FLOW_TYPE_UNVERIFIED","流水类型尚未核验"),
|
||||
Map.entry("CHAIN_DISCONTINUITY","批次结存不连续"),
|
||||
Map.entry("CHAIN_ORDER_AMBIGUOUS","同秒流水顺序待核验"),
|
||||
Map.entry("FLOW_QUANTITY_OR_TIME_INVALID","流水数量或时间待核验"),
|
||||
Map.entry("FLOW_QUANTITY_CONFLICT","流水变动数量不一致"),
|
||||
Map.entry("NEGATIVE_CHAIN_STOCK","批次结存出现负数"),
|
||||
Map.entry("NEGATIVE_RECONSTRUCTED_STOCK","还原库存出现负数"),
|
||||
Map.entry("UNIT_UNKNOWN","库存数量单位待确认"),
|
||||
Map.entry("ANCHOR_DUPLICATE","库存锚点重复"),
|
||||
Map.entry("ANCHOR_QUANTITY_INVALID","库存锚点数量无效"),
|
||||
Map.entry("ANCHOR_INTERVAL_INVALID","库存观测时间无效"),
|
||||
Map.entry("ANCHOR_BEFORE_DAY_END","库存锚点早于日末"),
|
||||
Map.entry("FLOW_ID_INVALID","流水标识无效"),
|
||||
Map.entry("FLOW_ID_CONFLICT","流水标识存在冲突"),
|
||||
Map.entry("UNKNOWN_QUALITY","库存品质待确认"),
|
||||
Map.entry("CHAIN_IDENTITY_MISSING","流水库位或批次缺失"),
|
||||
Map.entry("INSUFFICIENT","合格日末库存记录不足"),
|
||||
Map.entry("ZERO_AVERAGE_STOCK","期间平均日末库存为0,无法计算周转"),
|
||||
Map.entry("INVENTORY_UNKNOWN_OR_STALE","当前库存未知、异常或已过期"),
|
||||
Map.entry("NOT_APPLICABLE","货主不计算整体可售天数"),
|
||||
Map.entry("SALES_OR_STOCK_UNASSESSABLE","部分商品的销量或库存不足以判断"));
|
||||
}
|
||||
Reference in New Issue
Block a user