244 lines
17 KiB
YAML
244 lines
17 KiB
YAML
openapi: 3.0.3
|
||
info:
|
||
title: 平台自营库存周转只读查询
|
||
version: 1.0.0
|
||
description: >-
|
||
仅PLATFORM、tenantId=0、非代登录及platform:self-operated-analytics:list权限。
|
||
仓库由服务端受控配置校验;不触发采集,不读取来源订单,不调用领星。
|
||
所选窗口为截至endDate的1~366个完整北京时间自然日,并提供7/15/30天辅助对比;当前库存独立固定版本。
|
||
每次请求使用REPEATABLE_READ,一致性读固定销量、当前库存及历史日版本映射。
|
||
snapshotContext保存签名压缩的准确版本清单与缺失日期,不作为授权凭据;后续页按准确ID校验
|
||
账号、仓库、范围及失效标记,不用墙钟时刻重新推导日头。服务重启、校正失效、规则变更要求整体刷新。
|
||
历史分母只使用FLOW_EOD_V1日末全品质实物库存,以完整流水及独立库存锚点还原;不与旧计划样本混算。
|
||
上下文有效期最多30分钟;客户端不能直接指定runId。SKU/关注分页不改变概览范围。
|
||
历史销量完整性不依赖当前库存新鲜度;缺日保持未知、不缩短周转分母。sampleReasonCodes包含流水或独立库存对账失败原因。dailySamples.dayVersionId表示历史日版本标识,非原始库存run。
|
||
security: [{bearerAuth: []}]
|
||
paths:
|
||
/api/platform/v1/last-leg/self-operated-analytics/inventory-turnover:
|
||
get:
|
||
operationId: inventoryTurnover
|
||
summary: 单仓SKU或货主三窗口库存周转
|
||
description: >-
|
||
customerCodes按所选货主集合限定列表、概览及图表,省略或空集合表示全部;sku、focus、turnoverStatus及hideZeroInventory只筛列表。
|
||
按完整候选计算依赖后排序分页,未知末尾,同值按SKU及货主或货主编码稳定排序。
|
||
OWNER不计算整体可售天数;销售事实完整即可展示货主实际出库,不受单SKU观察期影响。
|
||
SKU出库及无出库判断同样不受首次观察时间限制:可信完整覆盖的缺销量行可计零;实际异常单位或未覆盖日期保持未知。
|
||
当前可售天数仍要求有效库存和完整销量窗口;期间件数周转仍要求合格每日库存样本及商品观察证据。
|
||
首屏必须current=1且返回概览;后续携带snapshotContext,includeOverview=false省略概览。
|
||
不接受attention、salesWindowDays、版本ID或其他视图参数。
|
||
parameters:
|
||
- {name: sourceAccount, in: query, required: true, schema: {type: string, minLength: 1, maxLength: 64}}
|
||
- {name: warehouseCode, in: query, required: true, schema: {type: string, minLength: 1, maxLength: 64}}
|
||
- {name: endDate, in: query, description: '含截止日,默认北京时间昨天,不接受今日或未来', schema: {type: string, format: date}}
|
||
- {name: baseWindowDays, in: query, schema: {type: integer, minimum: 1, maximum: 366, default: 30}}
|
||
- {name: groupBy, in: query, schema: {type: string, enum: [SKU, OWNER], default: SKU}}
|
||
- {name: customerCodes, in: query, style: form, explode: true, description: '货主编码多选,重复键传输;同一编码不重复计数,最多100个', schema: {type: array, maxItems: 100, items: {type: string, minLength: 1, maxLength: 64}}}
|
||
- {name: sku, in: query, description: SKU分组的精确查询,OWNER禁止传入, schema: {type: string, minLength: 1, maxLength: 128}}
|
||
- {name: hideZeroInventory, in: query, description: 默认隐藏当前全品质实物件数明确为0的SKU或货主汇总行,保留未知;全量筛选后分页,不改变概览及图表, schema: {type: boolean, default: true}}
|
||
- name: focus
|
||
in: query
|
||
schema: {type: string, enum: [ALL, NO_OUTBOUND_IN_WINDOW, COVERAGE_GT, COVERAGE_LE, FROZEN, DEFECTIVE, UNASSESSABLE], default: ALL}
|
||
- {name: coverageDaysThreshold, in: query, description: 'GT/LE必填,其余可选;无默认业务阈值', schema: {type: integer, minimum: 1}}
|
||
- {name: turnoverStatus, in: query, schema: {type: string, enum: [INSUFFICIENT]}}
|
||
- name: sortBy
|
||
in: query
|
||
description: 'OWNER禁止COVERAGE_ASC/DESC及UNIT_VOLUME_ASC/DESC,SKU禁止NO_OUTBOUND_PHYSICAL_DESC。全量筛选后按正式未舍入值排序再分页,未知值始终末尾,同值按SKU及货主升序。AVERAGE_DAILY及OUTBOUND_VOLUME使用baseWindowDays指定的窗口;体积按正式值排序,不使用已知小计。'
|
||
schema: {type: string, default: PHYSICAL_DESC, enum: [PHYSICAL_ASC, PHYSICAL_DESC, AVAILABLE_ASC, AVAILABLE_DESC, OUTBOUND_7_ASC, OUTBOUND_7_DESC, OUTBOUND_15_ASC, OUTBOUND_15_DESC, OUTBOUND_30_ASC, OUTBOUND_30_DESC, AVERAGE_DAILY_ASC, AVERAGE_DAILY_DESC, COVERAGE_ASC, COVERAGE_DESC, TRANSIT_ASC, TRANSIT_DESC, FROZEN_ASC, FROZEN_DESC, DEFECTIVE_ASC, DEFECTIVE_DESC, AVERAGE_PHYSICAL_ASC, AVERAGE_PHYSICAL_DESC, TURNOVER_ASC, TURNOVER_DESC, INVENTORY_VOLUME_ASC, INVENTORY_VOLUME_DESC, OUTBOUND_VOLUME_ASC, OUTBOUND_VOLUME_DESC, UNIT_VOLUME_ASC, UNIT_VOLUME_DESC, NO_OUTBOUND_PHYSICAL_DESC]}
|
||
- {name: current, in: query, schema: {type: integer, minimum: 1, default: 1}}
|
||
- {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
|
||
- {name: includeOverview, in: query, schema: {type: boolean, default: true}}
|
||
- {name: snapshotContext, in: query, description: 首屏返回的完整不可拆分上下文;翻页必填, schema: {type: string, minLength: 1, maxLength: 16384}}
|
||
responses:
|
||
'200':
|
||
description: 统一ApiResponse;参数或上下文业务错误沿现有异常响应,不伪装成空结果
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code: {type: string}
|
||
message: {type: string}
|
||
data: {$ref: '#/components/schemas/Response'}
|
||
'400': {description: HTTP参数类型或日期格式不合法}
|
||
'401': {description: 无可信登录身份}
|
||
'403': {description: 非平台、代登录或缺少独立只读权限}
|
||
components:
|
||
securitySchemes:
|
||
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
|
||
schemas:
|
||
Quantity:
|
||
type: number
|
||
nullable: true
|
||
description: PCS件数或对应指标值;未知为null,派生值六位小数,不把未知当0
|
||
Reasons:
|
||
type: array
|
||
items: {type: string}
|
||
Total:
|
||
type: object
|
||
required: [value, knownSubtotal, unknownSkuCount, reasonCodes]
|
||
properties:
|
||
value: {$ref: '#/components/schemas/Quantity'}
|
||
knownSubtotal: {$ref: '#/components/schemas/Quantity'}
|
||
unknownSkuCount: {type: integer, minimum: 0}
|
||
reasonCodes: {$ref: '#/components/schemas/Reasons'}
|
||
Risk:
|
||
type: object
|
||
description: 命中为已确认小计;只有整个实物及风险判断完整、分母大于0时才提供件数比例。未设慢消化阈值则数量与比例均null。
|
||
required: [matchedSkuCount, knownPhysicalQuantity, unknownSkuCount, physicalQuantityRatio]
|
||
properties:
|
||
matchedSkuCount: {$ref: '#/components/schemas/Quantity'}
|
||
knownPhysicalQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
unknownSkuCount: {type: integer, minimum: 0}
|
||
physicalQuantityRatio: {$ref: '#/components/schemas/Quantity'}
|
||
Volume:
|
||
type: object
|
||
description: >-
|
||
m³,按SKU先计算再汇总,不对分页行求和。优先完整有效的仓库实测尺寸,否则完整商品申报尺寸;不混搭长宽高。
|
||
支持mm/cm/m/in/ft,未知单位及非正尺寸无效。商品资料固定为queryAsOf之前最近完整已发布批次,按账号+货主+SKU关联。
|
||
库存按全品质实物件数,不计在途,不代表库位占用;历史出库按当前商品尺寸估算,不代表历史包裹实测体积。
|
||
数量已知为0可得0,无需尺寸;数量未知或正数量缺有效尺寸时正式值null,已知小计单独返回。
|
||
required: [value, knownSubtotal, missingDimensionQuantity, missingDimensionSkuCount, unknownQuantitySkuCount, declaredDimensionQuantity, reasonCodes]
|
||
properties:
|
||
value: {type: number, nullable: true, description: 全部数量和所需尺寸可确认时的体积,m³}
|
||
knownSubtotal: {type: number, nullable: true, description: 可计算部分的体积小计,m³,不冒充总量}
|
||
missingDimensionQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
missingDimensionSkuCount: {type: integer, minimum: 0}
|
||
unknownQuantitySkuCount: {type: integer, minimum: 0}
|
||
declaredDimensionQuantity: {allOf: [{$ref: '#/components/schemas/Quantity'}], description: 使用商品申报尺寸估算的件数}
|
||
reasonCodes: {type: array, items: {type: string, enum: [DIMENSIONS_MISSING_OR_INVALID, QUANTITY_UNAVAILABLE, DECLARED_DIMENSIONS_ESTIMATE]}}
|
||
VolumeWindow:
|
||
type: object
|
||
required: [days, startDate, endDateExclusive, volume]
|
||
properties:
|
||
days: {type: integer, minimum: 1, maximum: 366}
|
||
startDate: {type: string, format: date}
|
||
endDateExclusive: {type: string, format: date}
|
||
volume: {$ref: '#/components/schemas/Volume'}
|
||
Window:
|
||
type: object
|
||
required: [days, startDate, endDateExclusive, outboundQuantity, observedQuantity, averageDailyOutboundQuantity, completeDayCount, partialDayCount, unknownDayCount, dataStatus, reasonCodes]
|
||
properties:
|
||
days: {type: integer, minimum: 1, maximum: 366}
|
||
startDate: {type: string, format: date}
|
||
endDateExclusive: {type: string, format: date}
|
||
outboundQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
observedQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
averageDailyOutboundQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
completeDayCount: {type: integer, minimum: 0}
|
||
partialDayCount: {type: integer, minimum: 0}
|
||
unknownDayCount: {type: integer, minimum: 0}
|
||
dataStatus: {type: string, enum: [COMPLETE, PARTIAL, UNKNOWN]}
|
||
reasonCodes: {$ref: '#/components/schemas/Reasons'}
|
||
Context:
|
||
type: object
|
||
required: [snapshotContext, sourceAccount, warehouseCode, endDate, baseWindowDays, queryAsOf, queryExpiresAt, analyticsContext, inventoryObservedFrom, inventoryObservedUntil, sampleRuleVersion, dailySamples, missingSampleDates, accumulationStartedOn]
|
||
properties:
|
||
snapshotContext: {type: string}
|
||
sourceAccount: {type: string}
|
||
warehouseCode: {type: string}
|
||
endDate: {type: string, format: date}
|
||
baseWindowDays: {type: integer, minimum: 1, maximum: 366}
|
||
queryAsOf: {type: string, format: date-time}
|
||
queryExpiresAt: {type: string, format: date-time}
|
||
analyticsContext: {$ref: './self-operated-analytics-v1.yaml#/components/schemas/Context'}
|
||
inventoryObservedFrom: {type: string, format: date-time, nullable: true}
|
||
inventoryObservedUntil: {type: string, format: date-time, nullable: true}
|
||
sampleRuleVersion: {type: string, enum: [FLOW_EOD_V1]}
|
||
dailySamples:
|
||
type: array
|
||
minItems: 30
|
||
maxItems: 366
|
||
items:
|
||
type: object
|
||
required: [date, dayVersionId]
|
||
properties:
|
||
date: {type: string, format: date}
|
||
dayVersionId: {type: string, nullable: true, description: 内部版本证据,不作为用户可见编号}
|
||
missingSampleDates: {type: array, items: {type: string, format: date}}
|
||
accumulationStartedOn: {type: string, format: date, nullable: true}
|
||
Row:
|
||
type: object
|
||
required: [customerCode, customerName, sku, productName, physicalQuantity, availableQuantity, frozenQuantity, defectiveQuantity, transitQuantity, physicalSkuCount, windows, averageDailyOutboundQuantity, inventoryCoverageDays, coverageStatus, averagePhysicalQuantity, quantityTurnoverCount, turnoverStatus, qualifiedSampleDays, requiredSampleDays, missingSampleDates, sampleReasonCodes, lastObservedOutboundDate, attentionReasons, noOutboundInWindow, slowCoverage, unassessableSkuCount, unitVolumeM3, dimensionSource, inventoryVolumeM3, outboundVolumesM3]
|
||
properties:
|
||
customerCode: {type: string}
|
||
customerName: {type: string, nullable: true}
|
||
sku: {type: string, nullable: true}
|
||
productName: {type: string, nullable: true}
|
||
physicalQuantity: {$ref: '#/components/schemas/Total'}
|
||
availableQuantity: {$ref: '#/components/schemas/Total'}
|
||
frozenQuantity: {$ref: '#/components/schemas/Total'}
|
||
defectiveQuantity: {$ref: '#/components/schemas/Total'}
|
||
transitQuantity:
|
||
allOf: [{$ref: '#/components/schemas/Total'}]
|
||
description: 当前固定库存版本的全品质在途件数;独立展示,不计入实物、可售或周转分母;缺失、异常或过期保留未知。
|
||
physicalSkuCount: {$ref: '#/components/schemas/Quantity'}
|
||
windows: {type: array, minItems: 3, maxItems: 4, items: {$ref: '#/components/schemas/Window'}}
|
||
averageDailyOutboundQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
inventoryCoverageDays: {$ref: '#/components/schemas/Quantity'}
|
||
coverageStatus: {type: string, enum: [VALID, NOT_APPLICABLE, INVENTORY_UNKNOWN_OR_STALE, NO_AVAILABLE_STOCK, UNASSESSABLE, NO_OUTBOUND_IN_WINDOW]}
|
||
averagePhysicalQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
quantityTurnoverCount: {$ref: '#/components/schemas/Quantity'}
|
||
turnoverStatus: {type: string, enum: [VALID, INSUFFICIENT, ZERO_AVERAGE_STOCK, SALES_WINDOW_UNAVAILABLE]}
|
||
qualifiedSampleDays: {type: integer, minimum: 0, maximum: 30}
|
||
requiredSampleDays: {type: integer, minimum: 1, maximum: 366}
|
||
missingSampleDates: {type: array, items: {type: string, format: date}}
|
||
sampleReasonCodes: {$ref: '#/components/schemas/Reasons'}
|
||
lastObservedOutboundDate: {type: string, format: date, nullable: true}
|
||
attentionReasons: {$ref: '#/components/schemas/Reasons'}
|
||
noOutboundInWindow: {$ref: '#/components/schemas/Risk'}
|
||
slowCoverage: {$ref: '#/components/schemas/Risk'}
|
||
unassessableSkuCount: {type: integer, minimum: 0}
|
||
unitVolumeM3: {type: number, nullable: true, description: SKU单件体积m³,货主汇总不提供}
|
||
dimensionSource: {type: string, nullable: true, enum: [WAREHOUSE_MEASURED, PRODUCT_DECLARED, MISSING], description: SKU尺寸来源,货主汇总不提供}
|
||
inventoryVolumeM3: {$ref: '#/components/schemas/Volume'}
|
||
outboundVolumesM3: {type: array, minItems: 3, maxItems: 4, items: {$ref: '#/components/schemas/VolumeWindow'}}
|
||
Overview:
|
||
type: object
|
||
nullable: true
|
||
required: [physicalQuantity, availableQuantity, noOutboundSkuCount, unassessableSkuCount, inventoryVolumeM3, outboundVolumesM3]
|
||
properties:
|
||
physicalQuantity: {$ref: '#/components/schemas/Total'}
|
||
availableQuantity: {$ref: '#/components/schemas/Total'}
|
||
noOutboundSkuCount: {$ref: '#/components/schemas/Total'}
|
||
unassessableSkuCount: {$ref: '#/components/schemas/Total'}
|
||
inventoryVolumeM3: {$ref: '#/components/schemas/Volume'}
|
||
outboundVolumesM3: {type: array, minItems: 3, maxItems: 4, items: {$ref: '#/components/schemas/VolumeWindow'}}
|
||
Charts:
|
||
type: object
|
||
nullable: true
|
||
required: [ownerPhysicalTop, unknownOwnerCount, ownerVolumeTop, unknownVolumeOwnerCount, averageDaily]
|
||
properties:
|
||
ownerPhysicalTop:
|
||
type: array
|
||
maxItems: 10
|
||
items:
|
||
type: object
|
||
required: [customerCode, customerName, physicalQuantity]
|
||
properties:
|
||
customerCode: {type: string}
|
||
customerName: {type: string, nullable: true}
|
||
physicalQuantity: {$ref: '#/components/schemas/Quantity'}
|
||
unknownOwnerCount: {type: integer, minimum: 0}
|
||
ownerVolumeTop:
|
||
type: array
|
||
maxItems: 10
|
||
description: 当前库存正式体积降序,同体积按货主编码稳定排序;排除体积未知货主,不受列表筛选及分页影响。
|
||
items:
|
||
type: object
|
||
required: [customerCode, customerName, inventoryVolumeM3]
|
||
properties:
|
||
customerCode: {type: string}
|
||
customerName: {type: string, nullable: true}
|
||
inventoryVolumeM3: {$ref: '#/components/schemas/Volume'}
|
||
unknownVolumeOwnerCount: {type: integer, minimum: 0, description: 因缺尺寸或库存数量未知未参与体积排名的货主数}
|
||
averageDaily: {type: array, minItems: 3, maxItems: 4, items: {$ref: '#/components/schemas/Window'}}
|
||
Response:
|
||
type: object
|
||
required: [context, summary, charts, rows, total]
|
||
properties:
|
||
context: {$ref: '#/components/schemas/Context'}
|
||
summary: {$ref: '#/components/schemas/Overview'}
|
||
charts: {$ref: '#/components/schemas/Charts'}
|
||
rows: {type: array, maxItems: 100, items: {$ref: '#/components/schemas/Row'}}
|
||
total: {type: string, pattern: '^[0-9]+$', description: 服务统一将Long序列化为字符串}
|