diff --git a/测试环境单机部署与配置契约.md b/测试环境单机部署与配置契约.md index e71d796..68df725 100644 --- a/测试环境单机部署与配置契约.md +++ b/测试环境单机部署与配置契约.md @@ -2,6 +2,26 @@ ## 自营库存周转 TEST 发布与验收(2026-10-15) +历史库存还原实现已在本地完成并提交:后端 `99215bbb796248d4e3f6ad7897982678b363c072`,Admin `5e8c17c82fc6bef7340cb078ddc7660fc68dba10`;尚未推送、执行三表DDL、开启流水后台任务或发布本批代码。后端受影响257项测试通过(252项整组回归+新增5项针对性回归),前端38项测试、类型检查与构建及5项路由检查通过;独立DDL、Lastleg源基线和海外统一基线的三表定义完全一致,所有新字段有中文注释。端到端H2验证覆盖真实Mapper、任务/分页暂存、修订归并、独立锚点对账及原子日版本切换;重启从新尝试第一页开始、分页异常拆片、失效旧版、负库存/未知类型/缺覆盖拒绝、货主整体资格和固定版本导出均纳入检查。未以H2通过替代MySQL8真实执行、全仓容量或浏览器人工验收。 + +本地真实响应回放用新Java算法处理既有77条流水,三个独立库存观测点全部一致,30个日末结果与逐日核验记录一致;平均日末库存95.6件,同期出库29件,周转率30.334728%。这一回放零新增领星请求、零共享库写入,只证明该样本。新增类型及两个仓库的全量覆盖仍必须随实际受控采集逐SKU/日期核验。证据在后端 `target/selfop-history-final-tests.log`、`selfop-history-final-hardening.log`、`selfop-history-context-final.log` 与本地 `HistoryRealResponseReplay`;原始数据和真实主体标识不入Git。 + +待授权批次:仅在 `baiyue_lastleg` 执行 `lastleg-service/src/main/resources/db/selfop-inventory-history.sql` 新建三表,保留全部销量/原始库存;按精确提交依次构建部署 TEST Integration、Lastleg、Admin。Lastleg 新增配置 `baiyue.self-operated-analytics.inventory-history-enabled=true`、`inventory-history-days=60`,只在表结构及服务健康验证通过后开启,先30日再60日;业务账号/仓范围沿已有配置。流水每页共享原账号出口,现有1200ms、冷却和人工停止保持,销量/当日库存优先。版本发布、首个整仓片覆盖、至少一个SKU实际日末及查询/排序/导出一致性、次日自动续跑分别验收;不能把发布成功当两仓历史补齐。 + +12:18—12:24 按用户“先在本地核验再实现”的授权追加25次有界只读请求,其中24次为既定小范围核验,另1次仅追查已命中的品质转换业务单。22次流水查询、3次产品库存查询,均HTTP200/业务码200,最小请求起始间隔7.138秒;每次前后只读检查账号无待执行/运行任务、活动租约、暂停、冷却或pending请求,12:26收尾检查仍为空闲。没有停止原采集、改配置、部署、SQL写入或业务导入。本地诊断请求以私有intent/response/summary单独记账,未计入平台任务计数。 + +本批234条去重流水:DO153、IMO32、PA2、IAT35、RI12;其中正品228条、次品6条。已核验包含端点及前后排除、原32条SKU流水分页等价、跨两个货主的完整小片10条在不同页大小下集合一致。IAT完整窗口35条,7正28负;10组同品质移库净额0。额外定向取得一个完整IMO单12条,6个SKU正品−17/次品+17、总实物净变化0,证明该样本是品质转换。六次响应出现空首分页size10;定向零库存样本在隐藏开关0时明确返回零、1时不返回。US02只读首分页及空片,不记为全仓覆盖验收;BO/OBIAT未命中,不解释为永久不存在。同链同秒样本存在,未据此建立ID顺序合同。 + +本地Java回放复现了现有流水适配对合法空页报 `SIZE_MISMATCH`,随后开始最小实现:只为流水合法空首分页归一化sourceSize10,保留非空、页码、页数与未知页大小拒绝,库存和其他出库页大小策略不放宽。含此前字符串ID修复的72项定向测试、5项路由检查、实际空响应及原77条响应回放通过;回放没有新增供应商请求。当时历史三表与日发布尚未实现,后续本地实现见本节顶部,TEST尚未更新。脱敏核验为后端 `target/selfop-flow-semantics-audit.json`,测试为 `target/selfop-flow-semantics-tests.log`;原始响应与真实主体标识仅保留私有目录。字段与实施限制已更新接口手册和设计主文档。 + +11:46 按用户对精确提交 `d81d9cbda4c80dcc65001c45e83d8234495c92a5` 的授权,完成库存流水诊断补丁:Integration预构建#114、backend-java #701 SUCCESS(digest `sha256:98bbef793085a47f368bc2ad59e4f263ab7abbc10ac241b0c117ac9db41f03b0`);Lastleg预构建#322、backend-java #702 SUCCESS(digest `sha256:76b7ec8bfd7ed8d91bd0ec04df1e78c049a12f97af28012a8884a1fd035101d6`)。两服务按顺序各部署一次,实际digest、健康、唯一供应商出口及BUSINESS/continuousEnabled=true/1200ms均核验通过。无DDL、SQL写入、Admin或Nacos更新。发布前2条13号WAITING_SOURCE旧标记不在调度due集合;实际READY/RUNNING/RETRY_WAIT、有效租约及pending HTTP为0,未处理或改写这两条旧记录。 + +首次平台单SKU诊断请求已发出且确认1次,任务 `LXFLOW-B4D8C1EC69C54FAAAFA1D8DB89F4DCDA` 终止为DEAD/FAILED,错误 `LINGXING_WMS_INVALID_RESPONSE`,未发布库存或日代表;账号未暂停、未冷却、无在途请求。代码误把供应商流水ID校验为数字,且失败诊断未保留具体阶段;不能把这次发布成功说成真实对账通过。 + +11:52—11:54 用户随后明确授权在本地调用该SKU、可增加同SKU样本,改为本地两次固定时间窗口请求,未再次部署。本地Key与TEST运行Key仅做内存指纹匹配,凭据不输出;每次调用前后只读确认采集空闲。这2次诊断不在平台任务请求计数中,另以私有请求意图/原始响应记账,不能宣称平台计数覆盖它们。两个窗口分别返回32、45条,均单页完整;合计77条唯一流水、5组库位/批次/品质链,75条DO、2条PA、品质0。跨月结存119→74→144连续;三个已保存库存观测值96/145/144全部吻合,最近30日逐日出库与已发布日销量一致、合计29件。仅该SKU的PA/DO链得到验证,整仓分页、其他类型、同秒与时间边界、历史修订仍未关闭。 + +ID字符串修复已在本地准备,69项Integration相关测试、5项网关检查通过;77条实际原始响应经本地HTTP回放由修正后的Java适配器全部解析通过,回放未请求供应商。该修复尚未提交或发布,TEST仍是上述d81d9cbda版本。按用户“先处理方案”要求,暂停进一步发布,完整拉取/持久化/历史日库存/查询切换方案维护于[库存周转视图设计](自营业务/库存周转视图设计.md#历史库存还原与周转率修正方案2026-10-15实施合同)。未建历史表、未回写历史日库存、未更改页面周转率。证据:后端target下 `selfop-flow-probe-runtime-after-r1.json`、`selfop-flow-probe-after.tsv`、`selfop-flow-local-reconciliation.json`、`selfop-flow-real-response-replay.log`;原始响应在私有环境目录,不进入Git。 + 11:17 按用户本轮实现并一起上线授权,发布货主多选、当前显示列全量 CSV 导出与期间周转率:Lastleg `e19d303a769bc0e59084a58c5d88f111d4772e17`(预构建 #321、backend-java #700 SUCCESS,digest `sha256:9322351f4053afa4681886d2ed81d0178a83d6a9213b8badfcf13b0e86179244`);Admin `50b06dca1082ebc5c38455b35d9991efc4b2b41d`(admin-web-test #611 SUCCESS,imageId `sha256:f0f8e71d797ef73762877efb770db94f77a2804f689c7ff66bd08bdcf102aa07`)。Admin Job 已恢复 release/test、lightweight=true。Integration 未更新,无 SQL 写入或采集配置变更;部署前无待办及活动任务,部署后健康、唯一供应商出口、BUSINESS/continuousEnabled=true/1200ms 保持。经营总览两个文件的其他未提交改动未纳入本批。 验证:后端74项测试及5项网关检查、前端41项定向测试、374文件3086项全量测试、类型检查与构建通过。真实 HTTP 26次请求核验两仓多货主过滤、汇总、图表、去重、清空恢复,以及固定快照全量分页(所选货主 US01 704条、US02 312条);前端资源与运行镜像核验通过。CSV 分页、当前列、百分比、转义、取消及版本变化检查通过本地测试,人工1920×1080、100%视觉与浏览器下载验收仍待确认。证据位于后端 target:`selfop-multi-owner-http-proof.json`、`selfop-multi-owner-runtime-after.json`、`selfop-multi-owner-web-runtime.json` 及本批构建/发布回执。 diff --git a/自营业务/库存周转视图设计.md b/自营业务/库存周转视图设计.md index 49158e4..4200213 100644 --- a/自营业务/库存周转视图设计.md +++ b/自营业务/库存周转视图设计.md @@ -4,6 +4,121 @@ 本设计整理自 2026-10-13 [优化数据采集页面状态提示中的业务讨论](codex://threads/01a134a1-0ccd-7b61-add0-945d47eec5de)。用户已要求整理方案并交开发总控安排。本文件是新增视图、指标扩展、查询合同与验收的维护入口;基础数据、采集及权限沿用[一期方案](领星一件代发数据统计一期方案.md),总览原有下钻沿用[经营总览图表设计](经营总览图表设计.md)。设计交付不表示功能已实现、部署或通过验收。 +## 历史库存还原与周转率修正方案(2026-10-15,实施合同) + +用户已确认按本方案实现,先完成本地验证,再统一更新测试环境。本节是该扩展的唯一实施合同;`FLOW_EOD_V1` 整体替换期间周转的分母来源,原始日观测继续留作对账证据,不能混用两套日库存口径。代码完成不表示已建表、补齐两仓历史或部署,运行与验收事实只维护在 TEST 主契约。 + +### 1. 目标与已知边界 + +- 先补 US01、US02 截至昨日的最近30个完整日,校验通过的 SKU 可以显示期间件数周转率;再扩展到60日。现有两个月销量复用,不重拉出库详情,也不要求等待30天自然积累。 +- 数量周转率=**同期已发布出库件数 ÷ 同期平均每日实物库存 ×100%**。出库件数继续使用现行列表优先、缺项补详情口径,不能借本次库存流水核验宣称所有列表量已经证明等于最终实出量。期间次数与百分比来自同一个未舍入比值;不是金额/成本财务周转率。 +- 分母采用北京时间每日结束前的全品质实物库存,规则名 `FLOW_EOD_V1`。日期 D 对应 `[D 00:00, D+1 00:00)`,D+1零点事件归次日。平均数为N个完整日末库存之和÷N;不把08:00快照直接当日末,不与旧日代表混合,不用当前库存替代缺失的日库存。该口径与查询合同一并冻结。 +- 已验证一条 SKU 的两个月77条流水、5组库位/批次/品质结存链;三个真实库存观测点和最近30天逐日销量一致,支持此 SKU 的还原。后续本地扩展样本补齐DO/PA/IMO/IAT/RI、正次品转换、包含端点、空页及小范围多页对照;这不等于新增SKU都已完成独立库存锚点对账,也不证明两仓全量历史完整。真实执行与验收证据统一见[TEST主契约](../测试环境单机部署与配置契约.md),字段实测统一见[接口手册](领星WMS接口联调手册.md#库存流水有界诊断2026-10-15)。 + +### 2. 拉取什么、如何分页 + +| 数据 | 首次补齐 | 后续增量 | 用途 | +| --- | --- | --- | --- | +| 已发布 SKU 日销量 | 读取已有完整版本及日覆盖,不重新采集 | 继续现行销量增量 | 周转率分子、DO差异诊断 | +| 产品库存快照 | 复用合格的已保存快照;需要新锚点时通过既有库存任务采一轮 | 继续每日计划库存 | 当前库存展示、独立库存锚点 | +| `stock/stockCellBook/page` 流水 | 目标起日00:00到有效锚点之后的必要尾段;冻结上界后分页 | 采新增时间片,重查近期窗口捕获迟到变化 | 历史实物增减、批次链与日末库存 | + +1. 开发阶段先在本地回放已保存响应,补样本时继续限定账号、仓库、货主/SKU,不通过反复发布试探类型。全量上线必须回到 Integration 的唯一受控出口,共用账号锁、permit、请求计数、冷却和至少1200ms间隔;本地直连只用于本轮明确授权的诊断,不能变成长驻的第二个采集器。 +2. 生产采集计划按**仓库+固定时间片**,优先整仓分页覆盖全部货主/SKU,不按几千个 SKU 逐个请求。官方参数允许货主/SKU筛选;整仓不传这两个条件的实际范围及权限尚须小页验证。若整仓范围不能证明,明确改为已核验的“货主+时间片”计划,不能静默认为返回的是全仓。 +3. 首补从最近日向前推进,一天一片、每页1000条,端点支持情况经本地小范围确认。片内 `startTime/endTime`、排序、账号/仓范围固定;单片过大时按小时确定性拆分,不改变已开始翻页的条件。请求次数按各片 `ceil(total/1000)` 估算,首片取得总数后再估算耗时,不承诺“几分钟全补完”。 +4. 已核验来源时间参数在样本处两端包含;请求允许边界重叠取回,入库按流水ID去重,本地严格归属半开区间。小范围分页一致不替代每次完整性校验,同秒事件排序仍无永久合同;不能仅凭时间戳大于上次最大值跳过同秒或迟到记录。`createTime` 用已确认北京时间解析,原字符串同时保留。合法空首分页的size10特殊响应按接口手册严格归一化,不因空片误暂停整仓。 +5. 页响应、暂存及下一页检查点同事务保存;HTTP不在数据库长事务里。累计返回数、唯一ID数、源total/pages、每页范围及摘要均校验。总数变化、同页/跨页异常重复、排序漂移导致可能漏页时,不用“去重后条数差不多”判完整;缩小到固定历史片重新形成一次完整尝试,保留失败证据。重叠时间片的边界重复与片内分页异常分别处理。 +6. 只有全部页验证通过,时间片才获得“请求范围覆盖完整”的证据;这个结论不等于供应商永久保留、不可修改或历史绝无漏记。跨重启恢复若不能证明原分页集合稳定,重读当前小时间片,不从旧页码盲接新集合。 +7. 流水HTTP每页完成后释放账号锁,让销量及当日库存优先执行。流水归并、历史重建和版本发布交给本地工作队列;当前 `SelfopSchedulePlanner.tick → SelfopExecutionService.execute` 同步执行路径必须调整,否则仅释放账号锁仍会被同一个调度线程的计算阻塞。采集与本地计算分开调度,本地同仓重建有持久化状态与互斥,发布只持有短锁。 + +### 3. 必须保存的字段与三张新表 + +数据归 `baiyue_lastleg`。复用 `ll_selfop_sync_task/run` 记录请求、尝试、失败和检查点,复用 `ll_selfop_sync_stage` 保存每次拉取的白名单原始字段,新增 `INVENTORY_FLOW` 对象类型;原始响应中姓名、备注、操作者、地址等与数量无关字段不入业务库。日志不保存原始响应、Key或签名。 + +| 新表 | 关键内容 | 唯一性与用途 | +| --- | --- | --- | +| `ll_selfop_inventory_flow` | source_account、warehouse_code、customer_code、sku_code、source_flow_id、cell_no、inner_batch_no、product_quality;bill_no、source_no、bill_type;book_qty、book_change_qty_raw、delta_quantity、close_quantity;source_create_time、event_at_utc、business_date、quantity_unit;content_hash、revision_no、first/last_run_id、last_observed_at、validation_status/reason | 来源账号+仓库+来源流水ID唯一,ID按字符串保存;同ID异身份拒绝,不按单号或时间去重。保存最新确认事实,旧载荷/版本证据留在不可变stage中。ID作用域核验后再冻结唯一键,不能假定其全球唯一。 | +| `ll_selfop_inventory_day_version` | account/warehouse/business_date/scope_hash/rule_version、版本号、build_run_id、publication_group_id、固定销量版引用、锚点库存run及观测区间、已完成流水片与事实修订号、输入摘要;READY/PUBLISHED/SUPERSEDED/FAILED、发布/替代时刻、有效/未知SKU数、差异摘要 | 每仓每日一个已发布头;历史版本不覆盖。唯一约束配合仓scope短锁切换;同一校正组涉及的全部日头在一个事务内切换。它不是原库存快照的发布指针。 | +| `ll_selfop_inventory_daily` | day_version_id、customer_code、sku_code、product_quality、physical_quantity、quality_status/reason、anchor_reference、evidence_hash、source=`FLOW_RECONSTRUCTED` | 每日版本+货主+SKU+品质唯一;数量用DECIMAL(20,6),未知NULL、有效零为0。日末实物分品质保存,查询再合计一次;不储存重复的冻结/可售/在途推算值。 | + +原有 `ll_selfop_inventory_snapshot` 和已发布 `sync_run` 日标记原样保留。不能把人工快照改成历史定时日代表,不能把还原数据塞回原始快照,也不能更新旧库存时间。客户/商品名称继续关联已有主档,不在流水复制第二套主数据。 + +校正组清单与执行状态放入对应重建 `task/run` 的专用结构,禁止复用原库存日代表的发布标记;组清单冻结后不可改,输入再次变化则放弃候选并生成新清单。日版本另存 `invalidated_at/reason/rebuild_run_id` 以支持查询资格检查,失效标记不可因重建失败而清除;旧日行和证据仍保留。事实修订归并与所影响旧版的失效标记同事务提交,避免出现新事实已接受、查询仍把已知错误旧值判有效的间隙。失效范围尚不能精确确定时,保守覆盖该仓保留的历史日版本,不能漏标;已知细粒度范围记录在重建清单中。 + +流水至少按 `(account,warehouse,customer,sku,event_at,source_flow_id)`、`(account,warehouse,business_date)` 建查询索引;每日表按 `(day_version_id,customer,sku,quality)` 唯一及必要身份索引查询。日版本按仓/日期/发布有效区间查头;查询不得全表读取大JSON。删除策略需保护被日版本引用的流水证据、锚点和版本;本期不自动清理这批历史证据,也不直接套用销量计算副本7日淘汰规则。 + +字段实现时对齐现有长度:账号/仓库/货主64、SKU128、来源ID64、原始时间64;来源ID不转BIGINT,日期用DATE、UTC观测时刻用DATETIME(3),原始变动字符串与数值分开保留。基线在 `lastleg-service/src/main/resources/db/lastleg.sql` 与独立 `selfop-inventory-history.sql` 同步,部署SQL索引记录三表及已有stage/任务字段语义变更;本方案不执行DDL。 + +### 4. 历史每日库存如何还原 + +以 `账号+仓库+货主+SKU` 为统计身份,内部链进一步按 `库位+内部批次+品质` 区分。同名SKU不跨货主合并,跨仓独立。 + +1. **变动量**用有符号 `bookChangQty` 解析为DECIMAL;PA/DO样本中它与 `bookQty` 一致。两字段保留,不相加、不因业务类型再取负号。`bookQty` 不是期初量。其他类型是否同义由类型样本验证,未知语义不能用于正式日库存。 +2. **链校验**:同一链前一笔结存+当前变动=当前结存;首笔前结存=首笔结存-首笔变动。`closeQty` 只对该库位/批次/品质有效,不能把多笔结存直接相加或取最新一笔当SKU总量。分组键、同秒顺序未确认时保留歧义,不仅按字符串ID排序猜时间。 +3. **SKU锚点**:使用已保存、身份与单位明确的SKU分品质实物快照作为独立锚点,按同品质完整流水反推,再合计全品质。某批次在窗口内无变动就可能完全不出现在流水中,因此不能要求“出现过的链结存之和=SKU快照”,也不能把两者的差额自动判为漏账。例如活动批次10件、无变动批次90件,SKU锚点仍是100件;90件保留在锚点余额内。链连续性检查只约束实际观察到的链;只有另有完整同刻批次清单,才做全链总额对账。缺品质快照不能把总量任意拆分;快照缺行只有在供应商已核验的完整返回/零库存省略规则下才能认定零,否则未知。 + 库存扫描不是原子历史快照,`observed_at` 也不保证就是供应商扣账时刻;保存真实页面/整次扫描观测区间。若该SKU在区间内发生变动而无法确定先后,标记 `ANCHOR_TIME_AMBIGUOUS`,补稳定锚点或缩小对账区间,不能把秒级流水与毫秒响应时间机械相减。用其他独立库存观测点检验还原结果;仅从同一个锚点正推、反推互相吻合不能充当独立正确性证明。 +4. **反推公式与尾段**:在已确认稳定的锚点状态A下,日期D日末为 `I_D = I_A − ΣΔ`,求和范围从 `D+1 00:00`(含)到锚点状态已包含的最后事件,不能漏掉次日零点至早晨锚点之间的流水。来源端点未核验或无法确认事件是否已包含于锚点时,不发布该SKU。目标日期片与锚点尾段分别冻结边界并记录覆盖;尾段必须覆盖锚点整个观测区间以检查歧义,不以“昨日片已完成”替代。跨片重复按来源ID只计一次,恰好零点的事件属于新一天。也可从已核验期初正推逐笔核对;没有变动的日子只有在完整流水范围与有效锚点支撑下才继承余额,这属于结存推导,不是缺日补零。 +5. **品质转换和移库**:同SKU同仓移库的出/入两端合计应为0,但不能仅按业务单号删掉两条。已实测IMO也承载正品转次品,必须分品质分别记增减后合计,全品质总量不重复;不能只取某一品质或把全部IMO直接丢弃。跨仓移出入分别影响两仓。冻结/解冻若不改变实物不计入实物增减。调整已有正负样本,退件已见正增量,拦截/出库差异尚无实际样本;未知类型影响到的SKU/日期保持未知。上述增减不自动加入销量分子。 +6. **历史售空与新商品**:候选全集是当前库存、目标窗销量、窗口流水、已有历史日库存身份的并集,包含当前已售空SKU。无流水且有可靠库存锚点及完整范围,可证明恒定存量;缺锚点不能把缺行当0。新SKU首次入库前,必须有可靠零期初和完整流水才能确认历史零值;仅“第一次看见”不够。 +7. **资格逐SKU逐日判断**:范围缺页、重复身份、链断裂、负库存、单位未知、未知业务语义、锚点不符均保留NULL和原因。数值为0也要证据。对账容差按来源数量精度固定,不用任意百分比误差将不符样本判通过。 + +### 5. “回写”写到哪里、什么时候可见 + +这里的回写仅指写入平台自己的历史事实和日表,**不调用领星库存调整接口、不改变领星库存或已有销量事实**。 + +流程为:`固定片拉取 → stage落地 → 覆盖校验 → 幂等归并flow事实 → 固定输入构建day_version/daily → 锚点及链校验 → 原子发布日版本 → 查询读取`。 + +- 相同流水ID、相同内容重复采集只更新本次已见证据,不重复增减库存;同ID内容变化生成本地修订号,保留旧stage、标记受影响日版本需重算。同ID异身份先隔离冲突并使可能受影响范围待核验,不能自动搬移事实;只有身份更正得到确认后,才按新旧依赖并集重建。不能用 `ON DUPLICATE KEY` 累加delta,也不因为某一页没返回就删除历史流水。 +- 每日候选在独立版本下批量写入;写入中断只留下未发布候选,页面读不到半批数据。输入流水修订号、锚点、范围、候选身份全集、规则版本及校验摘要冻结,指向可重放的不可变stage修订,不能只存摘要后再读flow表最新值。归并事实修订与发布校验共用同仓版本互斥;发布短事务内核对输入代次未变并切换日头,变化则重建,不能在校验和切换之间放入新的修订。 +- **首次补齐或新增日期,一天构建和校验通过即可发布一天**,不设30分钟等待,不等60天全跑完。版本包含全候选的有效/未知状态;个别SKU异常不阻塞其他SKU出结果,但未知不能被省略而使货主合计看似完整。若本次也修正已发布日期或改变既有候选全集,按下述校正组发布,不能借“逐日发布”跳过跨日一致性。 +- 同一SKU历史变动修订会影响多个日余额,必须按实际计算依赖确定受影响日期,不能只重算发生当日或机械地重算“事件日至锚点”。以较晚锚点反推时,某笔delta修订影响求和范围包含它的更早日末;以较早锚点正推时则影响其后的日末。时间、身份、品质或锚点变化按新旧依赖范围并集失效重建;新发现的售空SKU也要重新核对候选全集。按依赖重建保留范围内的全部受影响日期,无需重拉其他未变化的历史销量。无修改时间或删除契约时,旧记录缺失只进入冲突核验,不自动抹账。 +- **校正组原子发布**:一个重建任务保存不可变的组清单(publication_group_id、受影响日期、预期旧头、新候选ID、输入代次)。全部候选就绪后,在一个短事务内校验旧头和输入代次、切换全部日头及有效区间、标记整组完成;失败全部回滚。批量计算与daily写入在事务外提前完成,切换只涉及头记录;同仓重建互斥避免并发覆盖。查询可读完整旧组或完整新组,不能读到一半新、一半旧;重启用组清单确认整组是否已提交,不逐日猜测完成状态。 +- 已确认旧值有问题而新候选尚未就绪时,当前查询返回对应范围“校正中”,期间周转率暂不展示;首次拉取失败但尚未证明旧值错误不自动撤销旧版本。新查询须在同一个数据库一致性读事务内取得组状态、各日头和销量版本并固化准确版本ID映射;不在多个普通读请求间拼装日头,也不能仅靠墙钟asOf重新推导版本。已打开的上下文及导出继续引用固定证据,同时检查其版本是否被标为需校正,命中则要求刷新/暂不输出该周转率,不能继续称为有效正式结果。已发布日行不可就地修改。 +- 归并与重建按日期、货主/SKU分批,流式读取有序流水,不把两仓两个月的JSON一次载入内存。批量写入使用短事务;输入版本未变时复用本地结果,不为生成新日版本再次请求领星。数值计算用DECIMAL,不能在JavaScript浮点或前端做最终业务求和。 + +### 6. 周转查询、货主汇总与导出 + +- 改 `InventoryTurnoverContext` 固定新的日版本映射、校正组与 `FLOW_EOD_V1`,改 `InventoryTurnoverSql` 的日库存来源为每日表。上下文在一致性读中同时确定输入版本;后续查询按准确ID读取,并检查校正失效标记,避免迟提交事务被墙钟时间误归入旧上下文。当前 `inventoryRunId` 仍服务当前库存、可售天数和立方数;历史日库存版本独立。规则切换后旧上下文明确要求刷新,不保留隐式混算分支。 +- 对每个SKU,分子日期完整且N个有效日末库存齐全、平均库存>0,才输出周转次数及百分比。平均库存>0且销量0,结果为0%;均库存为0则仍未知并说明原因,不给无限大。补到7日就可计算7日,30日不足仍不缩短分母。 +- 货主周转率=该货主同期总出库÷该货主每日总实物库存的均值×100%,不是SKU百分比的算术平均。历史售空SKU继续参加分子和分母;“隐藏0库存”只控制当前列表展示,不偷偷改变货主历史统计总体。 +- 货主范围内存在缺失SKU/日期,正式总周转率未知,同时返回可计算SKU数、未知数及已知库存小计;不能只挑完整SKU算出一个看似完整的货主值。其他已齐的货主可独立正常展示。 +- 列表、排序、导出共用后端计算和同一固定销量版/日库存版。导出包含期间、期间出库、平均日末实物库存、周转率%、库存来源“流水还原”、可用日数及缺失原因;不在浏览器用当前库存重算百分比。主表不重新塞回每行0/30,说明和证据留在现有入口。 + +### 7. 以后如何自动保持正确 + +1. 首补30日优先,后续60日;时间片永久记录边界和完成证据,已完成片不在每次启动时重新全跑。现有销量、当日库存继续优先执行。 +2. 每天北京时间00:05创建昨日流水片,完成后立即尝试使用已有合格锚点构建;若证据不足,则等待**当天08:00**计划库存,并追加覆盖“当天00:00至该锚点观测区间结束”的固定尾段任务。仅在昨日片、所有必要尾段、锚点稳定性共同校验通过后发布昨日;有可用证据即可提前发布,不固定等08:00。锚点在翻页后发生变化,不能移动在途请求上界,另建固定片补齐再选新锚点。00:05是启动时间,不承诺所有SKU此时都已具备证据;尾段未齐显示待核验,不把任务完成当库存已完整。 +3. 固定冻结上界,日常重查最近3个完整日捕获迟到记录;每周分片复核近30日、保持低优先级且每页让出账号。因为该接口目前只见 `createTime`,不能宣称它支持更新时间增量或能发现任意旧记录变更。超过复核窗口的更正通过显式指定日期重算;若供应商确认有更新/删除通知或游标,再替换复核方案。 +4. 每天与真实库存对账,差异影响对应SKU与日期资格;保持原数据、错误码和业务任务号,不能为“恢复”自动清库、重置采集或更改历史时间。 +5. 429遵守账号冷却与暂停;HTTP结果未知先查审计,不重复发同次请求。固定历史片在明确收到响应且可重读时才有限重试。新增流水采集没有额外HTTP总额度上限,仍受现有安全出口和人工停止控制。 + +### 8. 实施批次与验收退出条件 + +| 批次 | 交付 | 通过条件 | +| --- | --- | --- | +| A 本地语义核验 | 保存白名单字段、时间/品质/变动/结存对照;冻结日末口径 | 已有77条实响应回放;另补移库/调整/退货/品质等出现的真实类型样本、同秒与端点证据。未出现类型列出未验证,不假造真实通过。 | +| B 持久化与受控拉取 | 三张新表基线、stage/任务类型、整仓分页合同、断点/去重/修订及调度分离 | 分页变化不假完整;重启不重算delta;本地构建不占供应商锁或阻塞销量线程;无重复任务头。 | +| C 单SKU→单货主还原 | 日末重建、原子校正组、独立锚点对账 | 样本三锚点一致;次日尾段、静止批次、连续日链、空日、售空、新品、负库存、未知字段与重复/修订场景通过;正推与反推的修订依赖分别验证;至少一个货主完整候选汇总对齐。 | +| D 两仓30日与查询 | 近30日分片补齐,页面/排序/导出改读新日版本 | 已覆盖完整且合格的SKU显示数值;未知有原因;货主算总量比值;当前库存/销量原数据不变;首次逐日发布、校正组事务回滚/重启、并发读、旧上下文失效均通过,不出现跨日新旧混合。 | +| E 增量与60日 | 昨日自动任务、近期复核、低优先级更早历史 | 无人操作仍生成任务并继续;停机恢复、迟到流水、冲突与日版本重建有证据;不以首轮成功替代持续能力验收。 | + +代码入口为 Lastleg `selfoperated/analytics/history/`。`INVENTORY_FLOW_HISTORY` 请求任务和 `INVENTORY_HISTORY_BUILD` 本地构建任务使用独立类型及账号任务角色,不进入原始库存/销量任务执行器;构建结果使用独立日版本,原库存发布指针保持。两个独立调度执行器分别负责每页HTTP和有界本地归并/构建。Lastleg 配置 `inventoryHistoryEnabled` 默认 false;启用前必须完成三表与两服务发布,`inventoryHistoryDays` 默认60、首批30日。SQL、发布、开启两仓采集按仓库要求明确授权;单SKU本地诊断不扩大成全量本地直连。 + +### 9. 设计复核与必须保留的反例 + +以下是设计推演和已有样本证据,不代替待实现代码的测试或上线验收: + +| 场景 | 必须得到的结果 | +| --- | --- | +| 已核验样本:次日早晨库存144、零点至锚点出库变动−1 | 前一日日末为145;漏采尾段时禁止把144发布为日末。 | +| 活动批次10、窗口内无流水批次90、完整SKU锚点100 | 无变动批次保留在锚点余额中;不能因已观察链只合计10而判缺90或把总库存算成10。 | +| 两个已发布日均为100,校正后均为110 | 新查询只能看到完整旧组、校正中或完整新组;不允许110/100混合得到105的均值。 | +| 较晚锚点固定为100、区间内仅一笔事件,其delta由−1修订为−2 | 该笔事件之前的日末应从101变102,事件之后的日末不因该笔修订改变;失效范围沿计算依赖确定。 | +| 校正组发布到一半异常、或计算后输入被再次修订 | 日头全事务回滚,或拒绝过期候选;重启不重复发布部分日期。 | +| 零点事件、尾段边界重叠、观测期间恰有库存变动 | 零点归新日,重叠ID只计一次,锚点先后不明则未知;禁止通过毫秒时间精度假装先后已确认。 | + +第二轮复核除前三项外,已补齐反推修订向更早日期传播、输入冻结与发布竞争、旧查询/导出失效检查。随后有界本地核验结果已纳入接口手册;BO/OBIAT、全仓独立覆盖对照、缺行零库存语义、永久稳定排序及历史修改/删除规则仍未关闭。基础存储、已验证字段适配和资格校验可进入实现,正式日库存仍须逐SKU/日期满足证据门槛;不能把未验证类型当0跳过,也不能把有限样本当作所有历史数据完整的证明。 + ## 业务目标与本次边界 业务需要回答四个问题:哪些货现在存得多;哪些 SKU 近期不动或消化慢;哪些货主的库存需要优先复核;哪些商品持续流转,可以进一步评估仓储支持或成本优惠。 @@ -63,7 +178,7 @@ SKU/商品/货主 | 实物/可售 | 7日/15日/30日出库 | 日均 | 图表约 220~260px 高,可折叠且记住本页偏好,表格仍是比较明细的主区。四张指标卡和两图显示当前仓库及精确货主范围的整体情况,不随 SKU 关键字、关注筛选及翻页改变;区块标明“本仓概览”或“本货主概览”。列表上方单独显示筛选结果条数,不能让全仓卡片被误读为当前页合计。 -指标卡主数值只展示正式总量,未知总量显示“—”;已知小计(含0)与待核验SKU数放辅助说明,不用已知小计冒充完整总量。库存过期须明确显示最近观测完成时间及受影响指标;同仓采集失败独立显示原因、真实任务号与状态观测时间,沿现有数据采集入口查看。整体刷新同时更新只读采集状态,状态请求失败须说明,不能继续把旧失败当最新状态或影响已成功返回的经营数据。样本证据明确区分出库历史和每日库存观测,补采/验证不计日代表,截止日只允许已结束的北京时间日期,因此今天采集成功的合格计划库存样本最早次日查询时才能纳入窗口;当天样本数仍为0不代表采集未成功。 +指标卡主数值只展示正式总量,未知总量显示“—”;已知小计(含0)与待核验SKU数放辅助说明,不用已知小计冒充完整总量。库存过期须明确显示最近观测完成时间及受影响指标;同仓采集失败独立显示原因、真实任务号与状态观测时间,沿现有数据采集入口查看。整体刷新同时更新只读采集状态,状态请求失败须说明,不能继续把旧失败当最新状态或影响已成功返回的经营数据。样本证据明确区分出库历史、原始库存观测和流水还原的日末库存;截止日只允许已结束的北京时间日期。当天08:00库存仅作为独立锚点,尾段和对账齐全后可还原并发布昨日库存,不需要再等一天。 货主 Top10 按有效全品质实物件数排序,未知货主不按零参与排名,图旁显示未能完整计算的货主数;标题必须是“实物件数”,不写“占仓面积/占仓最多”。相同件数按货主编码稳定排序。点击条目切入本仓该货主的 SKU 视图,显示可清除的货主条件,保留上下文;不足十个按实际数展示。 @@ -94,15 +209,15 @@ SKU 列表是只读分析表,无须新增“操作”列。第一组身份列 三列出库量始终同屏;`baseWindowDays` 决定日均、可售天数、无出库、快慢查询和期间周转的计算基准,默认 30,不会偷偷把未齐的 30 天换成 7 天。每窗独立返回完整量、已采集量、完整/部分/未知日数、实际日期及原因。部分窗口显示“已采集 X,覆盖不足”,所有日均与派生判断不采用该部分量。 -SKU 窗口出库、日均、可售天数及无出库判断与库存明细使用相同资格:仓库固定销量版可信且日期覆盖完整、商品及实际出库数量单位有效时,缺销量行可确认为窗口内零出库,不再要求商品首次观察早于窗口起点。单日存在无效单位或异常出库数量时必须撤销该日资格,不能由缺行补零掩盖。首次观察时间继续服务于历史件数周转等证据判断,历史库存代表缺口仍不得补零。卡片汇总与列表筛选使用同一结果,真正未知的库存或出库继续保留已知小计和待核验数。 +SKU 窗口出库、日均、可售天数及无出库判断与库存明细使用相同资格:仓库固定销量版可信且日期覆盖完整、商品及实际出库数量单位有效时,缺销量行可确认为窗口内零出库,不再要求商品首次观察早于窗口起点。单日存在无效单位或异常出库数量时必须撤销该日资格,不能由缺行补零掩盖。首次观察时间保留为证据;历史周转以合格日末库存资格为准,不再要求观察起点早于整个窗口,历史库存缺口仍不得补零。卡片汇总与列表筛选使用同一结果,真正未知的库存或出库继续保留已知小计和待核验数。 | 指标 | 计算与命名 | 资格 | | --- | --- | --- | | N 日日均出库 | 同期完整实际出库件数 ÷ N,含真实零出库日 | 使用固定销量版日事实及完整日规则;不只除以有出库的天数 | | 预计可售天数 | 当前正品可售量 ÷ N 日日均出库 | 沿用库存有效性、可信窗口完整性及数量单位资格,不以 SKU 首次观察时间阻断;描述正品可售库存,不代表全部实物消化时间 | -| N 日件数周转次数 | 同期完整实际出库件数 ÷ 同期每日全品质实物观测均值 | N 个合格同口径日代表、范围/单位/商品身份可靠、同期销量完整;显示“次/N日”,不叫财务周转率 | +| N 日件数周转次数 | 同期完整实际出库件数 ÷ 同期每日全品质日末实物均值 | N 个合格同口径日代表、范围/单位/商品身份可靠、同期销量完整;显示“次/N日”,不叫财务周转率 | -期间平均库存为 `N 个每日代表的实物量之和 ÷ N`,沿用主方案 `DAILY_SCHEDULED_FIRST_V1`:每天指定的首次合格计划采集,不改用每天最新快照,不使用期初/期末二点平均,不是日终或时间加权平均。每个日代表的商品库存先按品质聚合一次,冻结和次品是实物子集,在途不加入。出库分子为同商品同单位全部实际出库,不以某个库存品质过滤重复累加订单。 +期间平均库存为 `N 个合格日末实物量之和 ÷ N`,统一采用本节 `FLOW_EOD_V1` 流水还原规则,不使用日内首次快照、期初/期末二点平均或时间加权平均。每个日版本的商品库存先按品质聚合一次,冻结和次品是实物子集,在途不加入。出库分子为同商品同单位全部实际出库,不以某个库存品质过滤重复累加订单。 当前库存和历史期间独立:更改截止日只改变销量及平均库存的历史区间,预计可售天数仍使用查询时固定的当前库存,页面必须保留库存观测时间。历史周转有完整样本时,不因当前库存过期而抹去历史结果;当前可售天数仍须失效。不同 N 的周转次数不是同一期间,不能直接比较次数得出速度变快。 @@ -118,10 +233,10 @@ SKU 窗口出库、日均、可售天数及无出库判断与库存明细使用 | 当前库存有效且可售为 0 | 可售天数 0 并标“无可售库存”;不进入消化较快,不代表库存健康或货主优秀 | | 当前库存未知/异常/过期 | 可售天数为空并说明;可靠历史出库及历史周转独立展示 | | 7 天完整、15/30 天缺失 | 7 天可独立输出正式值,其他窗口显示已采集量/缺口;默认 30 日基准的日均及天数仍为空 | -| N 日日代表齐全且平均实物大于零、出库为零 | 件数周转次数为 0;仍要满足商品观察与销量完整性 | +| N 日日代表齐全且平均实物大于零、出库为零 | 件数周转次数为 0;仍要满足历史日末库存与销量完整性 | | 平均实物为零 | 周转次数为空;有出库时说明“每日观测未捕获存量”,不输出无穷大或直接认定数据造假 | -| 有 30 天出库,但库存只有 2 个合格日样本 | 显示 2/30、缺口和积累起点;周转次数为空,不缩小分母、不用当前库存倒推历史 | -| 首采以前、人工 TEST 库存、日代表缺商品行或品质/单位异常 | 沿原规则保留未知/缺口;不补零,不把人工批次事后标成计划日代表 | +| 有 30 天出库,但库存只有 2 个合格日样本 | 证据弹框显示 2/30、缺口和历史覆盖起点;周转次数为空,不缩小分母、不用当前库存直接替代历史 | +| 缺少独立锚点、流水不完整、日版本缺商品行或品质/单位异常 | 保留未知/缺口;不补零,不把原始快照事后改成历史日末记录 | 最近已观测出库日期取固定销量版的有效正出库最大日期,不以任意首采日充当。没有可靠日期显示“已采集历史内未见出库”;只有该日期至截止日连续覆盖且商品存在证据可靠时,才可显示“距最近出库 N 天”。不得将有限观察窗口内未见出库写成“从未售出”或真实库龄。 @@ -170,7 +285,7 @@ SKU 数值表头支持实物件数、正品可售、7/15/30 日出库、所 一次响应返回 `context,summary,charts,rows,total`。首次/整体刷新返回概览与两图,普通列表翻页用同一上下文及 `includeOverview=false` 省去重复汇总;筛选 SKU 或关注不改变概览范围。切仓、截止日、基准、精确货主或分组变化时,重新请求对应概览;不得把新上下文的行与旧图拼屏。 -`context` 固定账号、仓库、销量版、当前库存版、queryAsOf、有效期、三个真实窗口、当前库存观测起止、历史日样本规则与最多30个日期到代表批次的映射摘要。历史样本只纳入首次上下文时已经发布且合格的代表;后续样本补齐不能悄悄改变旧查询。实际代表映射须可由服务端验证/重建,缺失日期也冻结,不只是返回一个不可校验的哈希。受引用版本按现有保留策略保护,到期或无法重建时明确要求整体刷新。 +`context` 固定账号、仓库、销量版、当前库存版、queryAsOf、有效期、三个真实窗口、当前库存观测起止、历史日末规则与最多366个日期到独立日版本的准确映射及摘要。历史样本只纳入首次上下文时已经发布且合格的代表;后续样本补齐不能悄悄改变旧查询。实际代表映射须可由服务端验证/重建,缺失日期也冻结,不只是返回一个不可校验的哈希。受引用版本按现有保留策略保护,到期或无法重建时明确要求整体刷新。 每窗指标至少返回 `days,startDate,endDateExclusive,outboundQuantity,observedQuantity,averageDailyOutboundQuantity,completeDayCount,partialDayCount,unknownDayCount,dataStatus,reasonCodes`;行另外返回 `inventoryCoverageDays,averagePhysicalQuantity,quantityTurnoverCount,qualifiedSampleDays,requiredSampleDays,attentionReasons` 及各自状态。不能用一个行级 COMPLETE 掩盖部分指标未知。内部版本标识用于程序,不作为用户可见业务编号。 @@ -180,17 +295,17 @@ SKU 数值表头支持实物件数、正品可售、7/15/30 日出库、所 | --- | --- | | 三窗实际出库 | `ll_selfop_sku_daily_sales`,按固定销量版、仓库及最长max(30,N)日读取,一次条件聚合三窗;仓级概览优先复用 `ll_selfop_warehouse_daily_sales` 及原覆盖证据 | | 当前实物/可售/在途库存 | `ll_selfop_inventory_snapshot`+其固定 profile,同 SKU 全品质先聚合,避免与日销量多行连接后乘倍 | -| 历史平均库存 | `ll_selfop_sync_run.inventory_daily_sample_date/inventory_sample_rule_version` 对应的 snapshot、profile、范围与发布证据;最多366个不可变代表批次 | +| 历史平均库存 | `ll_selfop_inventory_day_version`+`ll_selfop_inventory_daily`,固定最多366个不可变日版本,检查范围、规则和已知失效标记 | | 商品观察与缺日 | 固定版本 profile+销量覆盖证明+各库存代表的资格;可信完整销量覆盖内缺销量行可计零,缺库存代表行仍不得补零,不读取可变最新档案污染旧上下文 | -| 查询上下文 | 沿现有版本上下文机制扩展日代表集合;可用服务端签名令牌或既有上下文存储,但冻结字段及权限行为一致,实施前总控确定唯一方式 | +| 查询上下文 | 压缩JSON+HMAC签名令牌固定准确日版本集合;服务进程重启后旧令牌要求整体刷新,当前单机单实例部署不依赖跨实例共享密钥 | -本轮不新增 7/15/30 天销量表、不复制库存日明细、不存一张会被实时覆盖的“SKU 周转结果表”,也不新增采集调度。若实际执行计划证明需要索引或有界聚合缓存,由总控在唯一 SQL 基线中评审;不凭设计先引入缓存平台。没有历史代表时,周转值为空即可交付页面,不能为得到数值额外扫描领星或修改原样本标记。 +不新增 7/15/30 天销量表、不存一张会被实时覆盖的“SKU 周转结果表”;历史流水、独立日版本与还原日明细及专用调度按本文件首节实现。若实际执行计划证明需要索引或有界聚合缓存,由总控在唯一 SQL 基线中评审;不凭设计先引入缓存平台。没有历史代表时,周转值为空即可交付页面,不能为得到数值额外扫描领星或修改原样本标记。 ## 查询性能与一致性 所有查询读取本地已发布数据,不查原始订单明细,不调用 Integration。禁止前端逐 SKU/逐货主请求、把第一页求和当全仓汇总、先取 Top100 后筛选、为三窗执行三套完整扫描。 -候选身份是本次固定当前库存版 profile/snapshot、固定销量版所选最长max(30,N)日事实及相关 profile、同期合格日代表 profile/snapshot 中货主+SKU 的并集;不读取后续可变档案。仅历史存在而当前缺行的商品保留为当前库存未知,不能补零。rows、total、summary 和图表使用同一候选定义,再分别应用各自已声明的范围与列表筛选。 +候选身份是本次固定当前库存版 profile/snapshot、固定销量版所选最长max(30,N)日事实及相关 profile、同期合格历史日版本的 daily 中货主+SKU 的并集;不读取后续可变档案。仅历史存在而当前缺行的商品保留为当前库存未知,不能补零。rows、total、summary 和图表使用同一候选定义,再分别应用各自已声明的范围与列表筛选。 默认实物排序且无销量/天数/周转等依赖筛选时,可先对完整候选身份分页,再补该页三窗和历史样本,不能只从当前 snapshot 选页而丢失未知/售空商品。依赖销量、可售天数、周转次数的筛选/排序必须先在完整候选计算资格,再排序分页。货主聚合与图表独立按完整范围计算;复用现有性能方案中已验证的路径,不原样重复已无收益的优化实验。 @@ -203,10 +318,10 @@ SKU 数值表头支持实物件数、正品可售、7/15/30 日出库、所 1. 总控冻结新增接口、窗口/零值规则、历史样本上下文和文件写权;明确 SKU 默认与货主可选视图是本轮新增例外,同步最近模块指引及操作矩阵,不恢复其他页面货主控件。 2. 指标查询工作包实现一次三窗聚合、库存资格、分页排序、全范围汇总与有资格的期间周转;样本不足作为正常业务状态,补定向公式/覆盖/版本测试。 3. 前端工作包实现默认打开、表格两视图、紧凑图表、筛选与既有趋势/证据弹框;先按冻结 DTO 并行,随后联调。共享类型与导航由总控协调唯一写者。 -4. 采集工作包只核对已有日代表选择、保留及查询证据是否满足合同,不默认改采集或放大请求;必要缺口另交总控精确派工。 +4. 采集实现按首节完成受控流水分页、本地重建、独立库存对账和原子日版本发布;保留现有销量优先级及统一出口约束。 5. 总控完成接口、SQL 基线、权限及受影响门禁,安排人工页面验收。实现、自动测试、部署、人工验收分别记录;运行状态仍只维护正式工程契约。 -优先交付能立即使用的默认查询、三窗出库、日均/可售天数、关注筛选与货主汇总;期间周转的合成样本测试同期完成,真实数值等待合格日样本自然积累,不让积累期阻断功能开发。不得以“已有可售天数”宣称正式周转已完成。 +优先交付能立即使用的默认查询、三窗出库、日均/可售天数、关注筛选与货主汇总;期间周转的合成样本测试同期完成,真实数值由已验证流水还原历史日末库存后产生,无证据的日期继续保持缺口。不得以“已有可售天数”宣称正式周转已完成。 ## 验收清单 @@ -233,7 +348,7 @@ SKU 数值表头支持实物件数、正品可售、7/15/30 日出库、所 本轮评审已明确:7/15/30是完整滚动窗口;四页日期统一最近3/7/15/30天及自定义,原明细继续有入口;货主汇总仅在新视图可选;件数不是空间;可售天数不是库龄或期间周转;历史样本不足不造数;新视图查询不增加领星请求。待后续业务确认的是正式仓租/优惠阈值、空间数据和成本主体,均不阻断本次只读功能开发。 -自定义长区间的翻页上下文携带逐日样本映射的SHA-256摘要,服务端仍按固定查询时刻重建完整max(30,N)日映射(含缺失日期)后比较摘要;完整日历保留在响应中。不能仅相信客户端摘要,也不能读取当前最新样本替代原版本。 +自定义长区间的签名翻页上下文携带准确日版本ID集合及SHA-256摘要,服务端按固定ID读取并重建max(30,N)日历(含缺失日期),不再通过墙钟时刻推定某个版本当时可见。已知修订使所引用旧版本失效时明确要求刷新;普通新版本发布不改变旧查询。完整日历留在响应,日版本字段为dayVersionId。 ### 货主多选、导出与期间周转率(2026-10-15确认) diff --git a/自营业务/领星WMS接口联调手册.md b/自营业务/领星WMS接口联调手册.md index e3a0495..c5ea1f5 100644 --- a/自营业务/领星WMS接口联调手册.md +++ b/自营业务/领星WMS接口联调手册.md @@ -21,8 +21,35 @@ 当前 Java 已有五个内部 POST 入口,共用 `/internal/integrations/lingxing/wms` 前缀并要求内部服务令牌:`/warehouses/query`、`/outbound-orders/query`、`/outbound-orders/locate`、`/outbound-orders/detail`、`/product-inventory/query`。出库发现与按单定位接口同时传递 `productList[].productSku/productName/qty`;列表字段完整时直接用于统计,缺项时才补该单详情,数量为零不属于缺项。接口职责和字段见[一期方案的接口约定](领星一件代发数据统计一期方案.md#平台查询接口和权限建议)。外部直接 HTTP 联调或源码核对不代表 Java 入口已运行验收。[本地启动说明](../../baiyue-firstleg/integration-service/README.md) +## 库存流水有界诊断(2026-10-15) + +官方 OMP [库存流水分页接口](https://apidoc-omp.xlwms.com/reference/post_openapi-v2-stock-stockcellbook-page-1.md) 为 `POST /openapi/v2/stock/stockCellBook/page`,支持 `whCode`、`customerCode`、精确 SKU `barcodeAccurate`、`startTime/endTime`、`sortName=createTime`、`sortOrder=asc`。返回库位、批次、业务类型、`bookQty`、字符串 `bookChangQty`、`closeQty` 和时间。文档品质为1正品/2次品、ID为数字,但2026-10-15授权本地样本实际为字符串ID、品质0正品/1次品;其他品质仍未确认,不照搬文档枚举。另有批次流水接口 `/openapi/v2/stock/batch/flow/page`,本轮不混采两套流水。 + +实测字段合同:分页为 `records,total,size,current,pages`;流水ID保持字符串,禁止经JS Number再转回而丢精度。样本中 `bookQty` 是有符号增减量,与字符串 `bookChangQty` 一致,不是期初库存;`closeQty` 是同库位/内部批次/品质链的结存,不是SKU全仓总库存。三锚点及逐日销量可在原单SKU样本范围内对齐;本轮扩大字段核验并不表示新增SKU已完成独立锚点对账。原始响应仅存私有诊断目录,业务设计见[库存周转历史还原方案](库存周转视图设计.md#历史库存还原与周转率修正方案2026-10-15实施合同)。 + +### 本地扩展核验后的接口约束 + +| 核验项 | 已观察到的合同 | 实现边界 | +| --- | --- | --- | +| 时间端点 | 同一已知流水放在startTime、endTime均返回;严格前后相邻秒窗口均不返回 | 来源窗口两端含,平台按ID去重并按北京时间半开区间归属。午夜归属需离线测试,不宣称已取得恰在午夜的真实事件。 | +| 仓范围与分页 | 不传货主/SKU可返回多个货主;一个完整小时间片10条,size100/20/6两页的ID与载荷一致;原单SKU32条以size20两页重读也一致 | 支持按仓时间片计划,不等于已核验两仓全量或永久稳定排序。每次正式片仍做覆盖、数量、身份、重复和漂移校验。 | +| 合法空流水页 | 实测返回 `current=1,size=10,total=0,pages=0,records=[]`,请求页大小可能为20或100 | 只有首个空页且全部条件吻合才归一化,保留sourceSize;非空页、页码错误、pages非0和其他size不放宽。现有库存接口策略不随流水修复一起改变。 | +| 数量/品质 | 234条去重流水中,bookQty均与bookChangQty一致;出现DO/PA/IMO/IAT/RI,品质0正品/1次品 | 不再按业务类型二次取负;不把未见BO/OBIAT自动映射为其他类型。 | +| 调整与退件 | 完整窗口IAT35条:7正、28负;RI12条均为正 | 符号来自实际delta;退件和调整不自动成为销量分子。单独的链段仍需完整窗口及锚点校验。 | +| 移库与品质转换 | 10组同品质移出/移入净额0;另一个完整单12条、6个SKU,正品合计−17、次品+17,总量0,业务类型同为IMO | 不能按IMO忽略全部行,也不能按单号去重掉一端。分品质记账后合计总实物;一个品质过滤结果不能用于全品质历史还原。 | +| 同链同秒 | 观察到同一链同一秒两笔−1,结存分别38、37 | 此样本可由数量关系区分顺序,不证明ID或接口同秒排序有永久顺序合同;存在多种可行链序时保持未知。 | +| 零库存 | 一条定向产品库存,isHideInventory=0明确返回stkQty=0,改为1则空页 | 明确零行可作为零锚点;缺行仍不能推定0,不能由该样本证明所有历史售空SKU都会返回。 | + +源数据保留期限、历史更正/删除通知、跨页一致性保证和不可见历史主体无法由有限只读请求证明;这些仍需供应商契约或独立业务对账。当前实现准入为“已观察字段可开发,逐SKU/日期证据合格才发布”,不是“整个仓库历史完整已证明”。请求次数、实际时段、账号状态及本地测试结果统一见[TEST主契约](../测试环境单机部署与配置契约.md#自营库存周转-test-发布与验收2026-10-15)。 + +最小诊断源码入口为平台 `POST /api/platform/v1/last-leg/self-operated-analytics/inventory-flow-probe` → Integration `/inventory-flow/query`。只允许 TEST BUSINESS、配置仓库、平台非代登录且有 sync 权限;一次限定一个货主/SKU、最多31日、第一页最多1000行、最多一次供应商请求。同键成功回读结果,异参拒绝,失败/未知不自动重试;完整分页只证明这次返回完整,不证明供应商历史无缺失。请求共用原账号 lease/fence、短 permit、间隔、冷却和计数,独立任务类型 `INVENTORY_FLOW_PROBE` 不进入自动调度,SAMPLE_DONE/DEAD 只作为诊断终态。源11008“无接口权限”仅终止这个可选接口,其他凭据错误、429及未知结果仍按原账号保护处理。 + +诊断记录保存在现有任务/尝试中,不新建业务表,不写库存日代表,不发布经营结果。实施顺序:验证受控入口及失败隔离 → 经授权部署诊断版本 → 单SKU真实流水与已保存观测时点对账 → 决定是否具备全量历史重建条件。当前 key 权限、历史完整性和重建可行性不能由离线测试或文档推定;部署事实仍只维护在测试环境主契约。 + ## 认证和请求格式 +历史库存自动采集使用独立内部入口 `POST /internal/integrations/lingxing/wms/inventory-flow-page/query`,保持服务令牌及单次出口许可验证,不暴露为平台可任意调用的接口。冻结仓库、北京时间起止(最长一天)、排序和每页1000条;`current`必须匹配任务持久化下一页。Integration 的 Long 类型 `total` 按统一Jackson合同输出十进制字符串,Lastleg 严格解析整数并验证页数及累计条数。来源ID、货主、SKU和仓库按字符串保存;只持久化数量还原所需白名单。该内部路径与仅单SKU的诊断入口并存,各自有独立权限/任务边界;真实部署与覆盖状态见TEST主契约。 + 基础地址为 `https://api.xlwms.com`;所有请求使用 HTTPS POST,`Content-Type: application/json; charset=UTF-8`,响应按 JSON 解析。认证参数放在请求体中,不需要 OAuth 获取 Token 的前置调用。 | 包络字段 | 类型 | 约束 |