fix: finalize inventory turnover queries and reuse snapshot aggregation
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: 平台自营库存周转只读查询
|
||||
version: 1.0.0
|
||||
description: >-
|
||||
仅PLATFORM、tenantId=0、非代登录及platform:self-operated-analytics:list权限。
|
||||
仓库由服务端受控配置校验;不触发采集,不读取来源订单,不调用领星。
|
||||
三窗为截至endDate的7/15/30个完整北京时间自然日;当前库存独立固定版本。
|
||||
每次请求使用REPEATABLE_READ。snapshotContext为服务端重建验证的版本与日代表证据,
|
||||
不作为授权凭据;账号、仓库、截止日、基准、queryAsOf、版本对、范围摘要、
|
||||
重建状态及全部30日映射(含缺失日期)逐项重建比较;无效或过期要求整体刷新。
|
||||
历史代表须同范围、正式计划采集、已发布且DAILY_SCHEDULED_FIRST_V1合格标记。
|
||||
上下文有效期最多30分钟;客户端不能直接指定runId。SKU/关注分页不改变概览范围。
|
||||
历史销量完整性不依赖当前库存新鲜度;缺样本不补零、不缩短周转分母。
|
||||
security: [{bearerAuth: []}]
|
||||
paths:
|
||||
/api/platform/v1/last-leg/self-operated-analytics/inventory-turnover:
|
||||
get:
|
||||
operationId: inventoryTurnover
|
||||
summary: 单仓SKU或货主三窗口库存周转
|
||||
description: >-
|
||||
精确customerCode限定列表与概览;sku、focus及turnoverStatus只筛列表。
|
||||
按完整候选计算依赖后排序分页,未知末尾,同值按SKU及货主或货主编码稳定排序。
|
||||
OWNER不计算整体可售天数;销售事实完整即可展示货主实际出库,不受单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, enum: [7, 15, 30], default: 30}}
|
||||
- {name: groupBy, in: query, schema: {type: string, enum: [SKU, OWNER], default: SKU}}
|
||||
- {name: customerCode, in: query, schema: {type: string, minLength: 1, maxLength: 64}}
|
||||
- {name: sku, in: query, description: SKU分组的精确查询,OWNER禁止传入, schema: {type: string, minLength: 1, maxLength: 128}}
|
||||
- name: focus
|
||||
in: query
|
||||
schema: {type: string, enum: [ALL, NO_OUTBOUND_30D, 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,SKU禁止NO_OUTBOUND_PHYSICAL_DESC。使用正式值及未舍入值排序。'
|
||||
schema: {type: string, default: PHYSICAL_DESC, enum: [PHYSICAL_DESC, AVAILABLE_DESC, OUTBOUND_7_DESC, OUTBOUND_15_DESC, OUTBOUND_30_DESC, COVERAGE_ASC, COVERAGE_DESC, TURNOVER_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'}
|
||||
Window:
|
||||
type: object
|
||||
required: [days, startDate, endDateExclusive, outboundQuantity, observedQuantity, averageDailyOutboundQuantity, completeDayCount, partialDayCount, unknownDayCount, dataStatus, reasonCodes]
|
||||
properties:
|
||||
days: {type: integer, enum: [7, 15, 30]}
|
||||
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, enum: [7, 15, 30]}
|
||||
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: [DAILY_SCHEDULED_FIRST_V1]}
|
||||
dailySamples:
|
||||
type: array
|
||||
minItems: 30
|
||||
maxItems: 30
|
||||
items:
|
||||
type: object
|
||||
required: [date, runId]
|
||||
properties:
|
||||
date: {type: string, format: date}
|
||||
runId: {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, physicalSkuCount, windows, averageDailyOutboundQuantity, inventoryCoverageDays, coverageStatus, averagePhysicalQuantity, quantityTurnoverCount, turnoverStatus, qualifiedSampleDays, requiredSampleDays, missingSampleDates, sampleReasonCodes, lastObservedOutboundDate, attentionReasons, noOutbound30d, slowCoverage, unassessableSkuCount]
|
||||
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'}
|
||||
physicalSkuCount: {$ref: '#/components/schemas/Quantity'}
|
||||
windows: {type: array, minItems: 3, maxItems: 3, 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, enum: [7, 15, 30]}
|
||||
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'}
|
||||
noOutbound30d: {$ref: '#/components/schemas/Risk'}
|
||||
slowCoverage: {$ref: '#/components/schemas/Risk'}
|
||||
unassessableSkuCount: {type: integer, minimum: 0}
|
||||
Overview:
|
||||
type: object
|
||||
nullable: true
|
||||
required: [physicalQuantity, availableQuantity, noOutboundSkuCount, unassessableSkuCount]
|
||||
properties:
|
||||
physicalQuantity: {$ref: '#/components/schemas/Total'}
|
||||
availableQuantity: {$ref: '#/components/schemas/Total'}
|
||||
noOutboundSkuCount: {$ref: '#/components/schemas/Total'}
|
||||
unassessableSkuCount: {$ref: '#/components/schemas/Total'}
|
||||
Charts:
|
||||
type: object
|
||||
nullable: true
|
||||
required: [ownerPhysicalTop, unknownOwnerCount, 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}
|
||||
averageDaily: {type: array, minItems: 3, maxItems: 3, 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序列化为字符串}
|
||||
Reference in New Issue
Block a user