This commit is contained in:
2026-10-14 11:02:49 +08:00
parent b768145b22
commit 8d6db2787b
7 changed files with 140 additions and 42 deletions
@@ -14,10 +14,18 @@ Lastleg `baiyue.self-operated-analytics.continuous-business-collection-enabled`
BUSINESS 的 `budgetDate` 随北京时间跨日更新,但 `reservedHttp/confirmedHttp` 保留累计值,不将新日期误解为当日新增次数或零点清零;根计数同样累计。有限 VALIDATION 日额度按原日边界换日,冷却、暂停和未确认 pending 跨日保留。查询和页面对 BUSINESS 显示累计请求消耗,不能按该日期把累计量标为当日消耗。
持续 BUSINESS 库存扫描在收到完整响应后遇到重复库存键、扫描唯一条数不符或分页总数变化,可按 `maxAttempts` 限制失败次数并指数退避重试(首次10秒,上限1小时);主动让出不计失败。每次失败保留原尝试证据并清空分页检查点,新尝试从第一页完整读取,以新批次隔离失败数据,禁止去重补齐或拼接后发布。达到上限转 DEAD 并清空下次重试时间;已有 DEAD 不自动复活。该例外不放开出库任务、鉴权、限流、身份错误、未知响应、传输结果未知及账号暂停保护。重试保留原定时任务和计划日;已开始执行的库存任务可在次日继续到期重试或中断续跑,仍受原失败次数、任务终态和出口保护约束;尚未开始的历史计划不补跑。只有同计划日成功的合格观测可成为日代表,跨日重试仍可更新当前库存但不能回填历史日样本。
平台授权人员显式恢复持续计划任务时,沿现有 `POST sync-tasks` 的 `RETRY`、独立 sync 权限和幂等审计。TEST 在持续 BUSINESS 开关开启且持久化计划身份严格匹配时允许恢复,不能继续按旧的“仅人工根”条件拒绝;开关关闭、旧定时槽位或伪造计划身份仍拒绝。恢复沿用原任务、计划日期和请求计数,不把计划任务改成手工补采,也不跳过原业务停止原因、冷却、租约及执行保护。
发布必须先确认旧 cutover 的固定版本部署及续接状态,再在自然日完成、两仓发布、无活动根/未完成出口请求的边界,由总控指定准确候选提交和发布操作。两服务新代码部署并验证后,配对配置再显式开启,避免新旧代码混用;旧本机 helper 只读,未经核对和明确指令不得修改、停止或与其并行发布。发布完成不等于真实验收通过。
验收分别提供代码门禁、实际镜像与配对配置、真实 HTTP/任务池证据:完整日期集合、无重复或漏日、已完成日不重采、每日 00:05 与优先顺序、旧截止过去仍有效持续、重启续跑和出口保护。两仓完成与独立完整日覆盖按原发布条件证明;库存观测不倒推历史、未采日不填零。危险的 429/鉴权/fence/未知响应分支用离线定向测试证明,不为验收主动制造供应商限流或破坏真实任务。
库存全量扫描使用独立配置 `baiyue.self-operated-analytics.inventory-page-size`,默认 1000,允许 1~1000;出库目录仍使用 `page-size`,默认 100。库存不再受出库页大小限制,以减少分页请求和完整扫描耗时;账号统一 1200ms 间隔、全仓全货主全品质及零库存保留规则不变。大页不等于稳定快照,跨页重复、总数变化及唯一键计数校验仍必须通过,失败不能去重后发布。
显式重试持续库存任务时,若原计划身份有效、持久化停止原因为空,且最近一次真实尝试仅因重复键、总数变化或唯一键计数失败,可恢复原 DEAD 根并在同一事务创建重试审计。不能把这种扫描失败自动写成整个会话已停止;恢复前后计划日、失败尝试和请求计数保留。已有持久化停止原因、限流、鉴权和结果未知仍拒绝;被拒绝的恢复命令不新增会话停止原因。此项运行是否生效见工程主契约,不以源码测试代替真实恢复验收。
## 库存周转视图新增范围(2026-10-13)
用户要求收集开发讨论并交总控开发“库存周转”,新增视图、接口、指标与验收以[库存周转视图设计](库存周转视图设计.md)为唯一维护入口:库存分析普通进入默认该视图,7/15/30 个完整日出库同屏,SKU 默认、货主汇总可选;原库存明细 10/30 天查询及总览严格下钻保持明确入口。此处仅允许新视图的货主汇总,不恢复销量分析或经营总览的独立货主控件。
@@ -146,7 +154,7 @@ OMP 的 `customerCode` 是外部货主编码,必须保存,不创建虚构客
| 出库明细 | `POST /openapi/v2/order/delivery/detail` | 从列表带入 `deliveryNo`、`customerCode`、`whCode`;以 `skuVOList.productSku` 与 `actualDeliveryQty` 核对实际出库量,`deliveryQty` 为预报量。见[出库详情](https://apidoc-omp.xlwms.com/reference/post_openapi-v2-order-delivery-detail-1) |
| 当前产品库存 | `POST /openapi/v2/stock/stockSku/page` | 全量不传品质过滤、`isHideInventory=0`;保存 `stkQty` 实物、`freezeQty` 冻结、`useStkQty` 可用、`transitQty` 在途。计算可售量时只取 `productQuality=0` 正品。见[产品库存](https://apidoc-omp.xlwms.com/reference/post_openapi-v2-stock-stocksku-page-1) |
出库列表分页使用 `current`、`size`,默认每页 10 条、最大 1000 条;可用 `customerCodes` 筛货主,公开入参没有 `whCode`。账号发现完成后根据返回 `whCode` 分仓,记录每仓应处理的目录集合与详情完成情况;只有共享目录窗口完整且该仓所需详情完整,才推进该仓覆盖。不能把某页未出现某仓解释为该仓该窗口零出库。返回时间按用户确认的北京时间解释;其他仓/时间类型的边界和查询窗口限制仍需核验。当前已出库单样本验证起点、终点均包含,具体条件见手册,不能从其他产品接口搬用约束。
出库列表分页使用 `current`、`size`,默认每页 10 条、最大 1000 条;可用 `customerCodes` 筛货主,公开入参没有 `whCode`。账号发现完成后根据返回 `whCode` 分仓,记录每仓应处理的目录集合与订单数量完成情况;只有共享目录窗口完整且该仓所需订单数量完整,才推进该仓覆盖。不能把某页未出现某仓解释为该仓该窗口零出库。返回时间按用户确认的北京时间解释;其他仓/时间类型的边界和查询窗口限制仍需核验。当前已出库单样本验证起点、终点均包含,具体条件见手册,不能从其他产品接口搬用约束。
### 最小字段映射
@@ -155,7 +163,7 @@ OMP 的 `customerCode` 是外部货主编码,必须保存,不创建虚构客
| 出库单标识 | 来源账号+货主 `customerCode`+`deliveryNo`;另存 `sourceNo` 供业务展示和对照,不把两类单号混用,仓库变化必须复核 |
| SKU 标识 | 来源账号+货主 `customerCode`+原始 `productSku`;仓级统计再加 `whCode`,不按名称归并或随意改大小写 |
| 外部唯一键 | `uniqueKey4tenant` 仅作为来源证据保存;业务关联使用明确的账号、货主、仓库、SKU、品质字段,不拆字符串推断身份 |
| 出库件数 | 已出库详情的 `skuVOList.actualDeliveryQty`;同单同 SKU 先归并,不叠加包裹层重复数量,不以预报 `deliveryQty` 替代。列表 `productList.qty` 的实际/预报语义需样本核对,不直接作为正式销量 |
| 出库件数 | 按当前确认采用列表优先:已出库列表 `productList` 的 SKU 与 `qty` 均完整时直接使用 `qty`;仅单笔订单缺明细、SKU 或数量时补详情并使用整单 `skuVOList.actualDeliveryQty`。真实数值零不触发补取,空目录不拉详情。同单同 SKU 先归并,不叠加包裹层数量,不混加列表与详情,不使用预报 `deliveryQty`。此为用户确认的采集取值策略,未证明列表数量与实际出库数量在所有订单上语义等价 |
| 出库日期 | `outboundTime` 已是北京时间,校验并解析后直接取其日期作为业务日;不再转为美国时间。缺失或无法解析时进入异常,不用创建时间或拉取时间补值 |
| 在库数量 | `stkQty` 实物数量;在途 `transitQty` 单列,不相加充当在库 |
| 可售数量 | 自营授权货主、指定仓库、正品 `productQuality=0` 的 `useStkQty`;不再次扣减冻结量,也不使用实物量自行推算 |
@@ -530,10 +538,10 @@ OMP 列表支持按出库时间查询,但公开 `timeType` 未列订单更新
| `order_id` | `BIGINT` | 对应外部出库事实 |
| `sku_code` | `VARCHAR(128)` | 原始 `productSku` |
| `product_name` | `VARCHAR(255)`,可空 | 来源商品名称,仅作展示 |
| `actual_quantity` | `DECIMAL(20,6)` | 详情 `actualDeliveryQty`,必须为非负有效数值 |
| `actual_quantity` | `DECIMAL(20,6)` | 完整列表 `qty`,缺项整单补取时用详情 `actualDeliveryQty`;必须为非负有效数值 |
| `quantity_unit` | `VARCHAR(32)` | 已核准业务单位;未确认单位不发布正式数量统计 |
唯一键 `uk_selfop_order_sku(order_id,sku_code)`;索引 `idx_selfop_item_sku(sku_code,order_id)`。同一详情中同 SKU 的有效行先按相同单位合计,再整单替换;包裹层数量仅核对,不再次累加。新旧内容相同时不替换、不增加修订号;数量缺失、解析失败或单位冲突保留在暂存并拒绝该批归并,不能用零替换旧事实。
唯一键 `uk_selfop_order_sku(order_id,sku_code)`;索引 `idx_selfop_item_sku(sku_code,order_id)`。所选整单数量来源中同 SKU 的有效行先按相同单位合计,再整单替换;包裹层数量仅核对,不再次累加。新旧内容相同时不替换、不增加修订号;数量缺失、解析失败或单位冲突保留在暂存并拒绝该批归并,不能用零替换旧事实。
### 7. `ll_selfop_inventory_snapshot`:产品库存全量批次明细
@@ -732,17 +740,17 @@ SKU 每日出库销量
实物量为同 SKU 全部已核验品质的 `physical_quantity` 之和;冻结量按各品质 `frozen_quantity` 独立汇总,是实物中的受限数量,不再加到实物总量;次品实物也是实物的一部分。可售量只取正品 `available_quantity`;在途单列,不视为当前占仓。未知品质可保留来源实物,但不推断它是正品或次品。任何数量缺失或异常均按其指标依赖传播,不能把一个数值有效误当该 SKU 所有指标均有效。实物件数仅表示数量,不表示立方米、托盘或库位占用。
关注条件使用下列明确语义;所需库存必须有效且不过期,销售资格沿用完整 30 天及本仓观察依据。标签可并存,不将冻结/次品与其他标签的命中数简单相加:
关注条件使用下列明确语义;所需库存必须有效且不过期,销售资格沿用所选完整窗口及本仓观察依据(经营总览及严格下钻的窗口以[经营总览图表设计](经营总览图表设计.md)为准)。标签可并存,不将冻结/次品与其他标签的命中数简单相加:
| `attention` | 判定及输出 |
| --- | --- |
| `ALL` | 不按关注原因过滤,保留未知、零库存及待观察候选 |
| `NO_OUTBOUND_30D` | 全品质实物量大于零、30 天出库资格完整且数量为零;冻结/次品即使正品可售为零也可命中。标签是“有实物库存且近 30 天无出库”,不是实际库龄或从未售出 |
| `NO_OUTBOUND_IN_WINDOW` | 全品质实物量大于零、所选窗口出库资格完整且数量为零;冻结/次品即使正品可售为零也可命中。标签是“有实物库存且所选时段无出库”,不是实际库龄或从未售出 |
| `COVERAGE_GT_THRESHOLD` | 正品可售量和日均出库均大于零、正式可售天数严格大于 `coverageDaysThreshold`;使用未四舍五入的计算值比较。阈值是用户本次查询输入的正整数天,不内置未确认的 90/180 天经营政策 |
| `FROZEN_STOCK`/`DEFECTIVE_STOCK` | 对应有效冻结件数/次品实物量大于零;不依赖销量窗口,单独提示受限或次品占仓 |
| `UNASSESSABLE` | 影响基本出库/可售天数判断的库存异常、过期、销量覆盖不足或观察不足;零销量且数据完整属于“无出库”,不是待观察 |
有库存零出库时可售天数仍为空,不当作无穷大,也不混入“超过阈值”的数值排序;零库存返回零天不代表货主优质。`NO_OUTBOUND_30D` 和 `COVERAGE_GT_THRESHOLD` 是不同关注集合,前者不可因分母为零被遗漏。现有 slow Top 100 仍限定正品可售库存大于零,不用它替代上述全品质实物关注。
有库存零出库时可售天数仍为空,不当作无穷大,也不混入“超过阈值”的数值排序;零库存返回零天不代表货主优质。`NO_OUTBOUND_IN_WINDOW` 和 `COVERAGE_GT_THRESHOLD` 是不同关注集合,前者不可因分母为零被遗漏。现有 slow Top 100 仍限定正品可售库存大于零,不用它替代上述全品质实物关注。
货主概览的总量来自完整候选集,不能加总当前页或 Top 100。每个指标分别返回正式总量、已知有效小计及未知数量:同一指标任一候选依赖未知,正式总量为空,已知小计明确标为部分资料;完全无可靠行时小计也为空。统计中明确的零值不算未知。库存候选/缺行资格沿用固定 profile 与完整库存快照,数据范围未发布不能输出空集零总量。
@@ -896,7 +904,7 @@ SKU 观察依据与候选资格也只取这两个版本的不可变快照,不
| daily 唯一商品 | `customerCode,sku` 必填并精确匹配固定候选;缺失不能合并同名 SKU,候选不存在则返回明确无记录。返回商品身份、区间汇总及每天一点的 `points` |
| 数值与状态 | 汇总和每个 point 包含 `outboundQuantity,observedQuantity,dataStatus,reasonCodes,quantityUnit`;point 另含 `businessDate` 和当日已证明覆盖的起止(无覆盖可空),汇总含 `completeDayCount,partialDayCount,unknownDayCount`。区间总量必须与同一 SKU daily 的日点按上述规则相加一致 |
| 覆盖与时效 | 返回 `salesCoveredFrom,salesCoveredUntil`、完整自然日范围、销量发布时间、修订待核对状态,以及可用时的库存观测起止;批次完整状态与查询范围完整性分字段,不能互相代替 |
| 其他指标的窗口 | `hot-skus` 只接受 `windowDays=7/30`,默认 30;`slow-skus`、`inventory-coverage` 固定 30,返回 `calculationWindowDays=30` 和实际起止。三者均以固定 `queryAsOf` 所属业务日的昨日为最后完整日,不接收图表的 `rangePreset`,误传明确拒绝 |
| 其他指标的窗口 | `hot-skus` 独立入口使用 `windowDays=7/30`,总览下钻的 CUSTOM 日期合同见[经营总览图表设计](经营总览图表设计.md);`slow-skus` 仍使用截至查询基准昨日的30日。普通 `inventory-coverage` 使用 salesWindowDays=10/30(默认30)及含当日的 salesWindowEndDate(默认查询基准昨日且须早于查询当日),返回实际起止和 calculationWindowDays;总览严格下钻改用原图CUSTOM区间,禁止混用两组日期参数,详见同一设计入口 |
调用示例为 `GET .../sku-sales-summary?sourceAccount=demo&warehouseCode=WH-DEMO&rangePreset=THIS_MONTH&current=1&size=20`,账号和仓库仅为占位值。首屏解析本月范围后,选择一条货主/SKU 调用 daily,传相同范围与返回的版本上下文即可得到日趋势。任何范围切换都不触发 Integration 调用,不创建新的领星统计接口;不要把本地预设名、日期范围或业务时区直接透传给外部产品库存接口。
@@ -911,7 +919,7 @@ SKU 观察依据与候选资格也只取这两个版本的不可变快照,不
| 概览总量 DTO | 数量指标采用 `overallTotal,knownSubtotal,unknownSkuCount,dataStatus,reasonCodes`;缺失与部分不得扁平化为 0。货主销量总量按销售覆盖判断,SKU 观察期不足另体现在风险统计,不混为同一个未知原因 |
| 关注占比 DTO | 无出库与超阈值各返回 `matchedSkuCount,matchedPhysicalQuantity,physicalQuantityRatio`;存在影响判断的未知时三个正式值为空,另给 `knownMatchedSkuCount,knownMatchedPhysicalQuantity,unassessableSkuCount,unassessableKnownPhysicalQuantity,reasonCodes`。比例为 0~1,前端转百分比;分母及其状态同时返回。已确认无命中时可为 0,范围未知不能为 0;未设阈值用 `THRESHOLD_NOT_SET` |
| SKU 明细筛选 | `/inventory-coverage` 保留可选精确 `customerCode,sku`,新增单值 `attention`,默认 ALL,枚举按前文。仅 `COVERAGE_GT_THRESHOLD` 必须传正整数 `coverageDaysThreshold`,其他关注可携带阈值用于行标签。所有货主/SKU/关注条件在完整候选上生效,然后统计 total、排序、分页;不得先取全仓 Top 100 再筛货主 |
| SKU 排序 | `sortBy=AVAILABLE_DESC/COVERAGE_DAYS_DESC/PHYSICAL_DESC/OUTBOUND_30D_ASC`,默认 AVAILABLE_DESC;按未舍入值排序,未知末尾;同值按 SKU、货主编码升序稳定排序。零销量的空可售天数不强行变为极大数,另用 NO_OUTBOUND_30D 查看 |
| SKU 排序 | `sortBy=AVAILABLE_DESC/COVERAGE_DAYS_DESC/PHYSICAL_DESC/OUTBOUND_ASC`,默认 AVAILABLE_DESC;按未舍入值排序,未知末尾;同值按 SKU、货主编码升序稳定排序。零销量的空可售天数不强行变为极大数,另用 NO_OUTBOUND_IN_WINDOW 查看 |
| 明细与标签 | 复用既有数量、覆盖字段,补 `outboundQuantity30d,defectivePhysicalQuantity,lastObservedOutboundDate,attentionReasons` 及各自质量依据;最近出库是日粒度,不冒充精确时间或真实库龄。不存在可靠最后日期时为空,附已采集历史起点/原因;正式值与小计、样本不足分开 |
| 页面一致性 | 概览命中 SKU 与同版本/阈值的全量筛选结果一致;不能用本页求和代替概览。前端只渲染服务端公式结果,不自行重算评级/占比或连接不同版本 |
@@ -997,7 +1005,7 @@ Integration 仅提供 `/internal/integrations/lingxing/...` 内部查询,沿
一期继续采用固定事实修订号生成本仓完整日版本,保持前文发布屏障和原子切换;**全历史重算发生在后台,不在每次页面查询时发生**。每仓同一时刻只构建一个销量候选,按数据库聚合与有界批量写入处理,不把整仓全部订单加载进 Java 内存。
正常 08:00/20:00 逻辑批次就绪后合并构建,不按每页、每条订单触发全历史重算。首采/补采有大量切片时,设计默认将常规阶段构建合并到至少 30 分钟一次,以最近 `build_started_at_utc` 计时;只有已经完成逻辑归并且通过屏障的部分可以进入候选。修订后的必要重建和失败恢复按任务优先级及退避执行,不为凑够间隔跳过修订。事实修订号、覆盖、范围、时区与 profile 依据均未改变时,不重复生成相同销量版;即使销量为零,只要覆盖推进仍需发布新的覆盖结论。库存每天的真实观测版本不因数量未变而跳过。
正常逻辑批次就绪后构建,不按每页、每条订单触发全历史重算。历史首采/补采按完整自然日推进:当天目录完整、订单校验及分仓逻辑归并完成并通过屏障后,立即进入候选构建和发布,不再等待固定 30 分钟间隔。完整列表订单每批最多 100 单在同一事务保存有效暂存、连续观测序号和任务检查点,避免逐单重写整天清单;缺项、较新修订和已存在的恢复引用仍逐单核验。中断只从已提交检查点续跑,不因批量处理放宽租约、暂停、完整性或发布屏障。修订后的必要重建和失败恢复按任务优先级及退避执行,不为凑够间隔跳过修订。事实修订号、覆盖、范围、时区与 profile 依据均未改变时,不重复生成相同销量版;即使销量为零,只要覆盖推进仍需发布新的覆盖结论。库存每天的真实观测版本不因数量未变而跳过。
无变化任务须在核验后以 `SUCCEEDED` 结束,将 `result_published_run_id` 指向复用的已发布版本,本次执行以 `REUSED` 终结且不成为统计版本头。合并构建承接的多个任务,只有新版本实际包含各自已归并事实及应声明的覆盖后才分别成功,均记录同一结果版本;负责发布的 run 为 `PUBLISHED`,其他复用尝试为 `REUSED`。尚未完整承接或仍处于 `APPLYING/BLOCKED` 的任务不能提前成功。此结果落账与版本关联必须可恢复,避免完成构建后任务永久未结束、重复恢复或永久保护旧输入。
@@ -1060,7 +1068,7 @@ Integration 仅提供 `/internal/integrations/lingxing/...` 内部查询,沿
| 同仓同货主 A 库存 3,000、30 日出库 300;B 库存 30、出库 30,均正品有效且观察完整,查询阈值 90 | 日均分别 10/1,可售 300/30 天;超阈值只命中 A。命中 SKU 实物 3,000、货主实物 3,030,占比 3,000÷3,030;不能按出库量小优先认定 B 更积压 | 合成 SQL/DTO 数值及排序测试 |
| 全仓热销榜前 100 无货主 B,但 B 有 3 个有销量 SKU;库存列表多页 | 筛 B 后热销候选仍有 3 个;B 概览与 B 的全量结果一致,不随页码改变,不从全仓前 100 或当前页二次筛选 | 过滤顺序、跨页与概览一致性测试 |
| 正品实物 120(冻结 20)、次品实物 8、可售 100、在途 30 | SKU/货主实物为 128、可售 100;冻结不叠加,次品不重复相加,在途不算占仓;品质连接不能复制销量。另测全冻结正品可售为零仍显示实物和冻结关注 | 品质聚合及受限库存测试 |
| 有实物库存且完整 30 日零出库;另一个正库存 SKU 观察不足,第三个库存未知 | 前者 NO_OUTBOUND_30D、天数为空,不冒充超过阈值;后两者保留待判断。风险正式数量/占比为空,已确认命中小计仍展示;库存完整且销量覆盖完整时,仅观察不足不使货主实际出库总量变空 | 零分母、资格与未知传播测试 |
| 有实物库存且完整 30 日零出库;另一个正库存 SKU 观察不足,第三个库存未知 | 前者 NO_OUTBOUND_IN_WINDOW、天数为空,不冒充超过阈值;后两者保留待判断。风险正式数量/占比为空,已确认命中小计仍展示;库存完整且销量覆盖完整时,仅观察不足不使货主实际出库总量变空 | 零分母、资格与未知传播测试 |
| 无阈值、阈值 0/负数、实际可售 90.04 天显示 90.0 天且阈值 90 | 未设置不计算超阈值指标;非法值拒绝;90.04 按未舍入值命中。没有任何实物库存时占比为空,不给优秀评级 | 入参、精度及无库存测试 |
| 同日计划库存先失败后成功,再有一次成功;另测并发发布和发布后恢复 | 选择同日第一次成功发布 run 为日代表,同日后续不覆盖;账号/仓/日唯一,失败及 VALIDATION 无日标记,恢复不重复标记 | 发布事务、幂等与唯一键测试 |
| 昨日计划跨午夜扫描或今日补跑;另测今日与昨日数量完全相同 | 不回填昨日或冒充今日正常日样本;数量相同仍保留真实今日观测。缺口留空不沿用旧值;日代表 run/snapshot/profile 不受销量版 7 天清理规则影响 | 时区边界、缺口和保留合同测试 |