fix(gateway): document multi-item order intent schema
This commit is contained in:
+79
-3
@@ -6,10 +6,10 @@ intent 字段刻意保留成 `dict[str, Any]`——网关不解释下单意图
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Annotated, Any
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, WithJsonSchema, field_validator
|
||||||
|
|
||||||
from app.shared.task_state import AccountQueryKind, OrderState, QueryStatus, TaskStatus
|
from app.shared.task_state import AccountQueryKind, OrderState, QueryStatus, TaskStatus
|
||||||
|
|
||||||
@@ -17,6 +17,79 @@ from app.shared.task_state import AccountQueryKind, OrderState, QueryStatus, Tas
|
|||||||
# ---- POST /api/orders ----
|
# ---- POST /api/orders ----
|
||||||
|
|
||||||
|
|
||||||
|
# The gateway intentionally keeps intent open-ended at runtime. This schema documents
|
||||||
|
# the stable trading fields without preventing future fields from being passed through.
|
||||||
|
OrderIntent = Annotated[
|
||||||
|
dict[str, Any],
|
||||||
|
WithJsonSchema(
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": True,
|
||||||
|
"description": (
|
||||||
|
"下单意图。推荐使用 items;旧版 item_url 等同级字段继续兼容。"
|
||||||
|
),
|
||||||
|
"properties": {
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"description": "本次购买的商品列表,按顺序加入同一购物车",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": True,
|
||||||
|
"properties": {
|
||||||
|
"item_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "商品页 URL",
|
||||||
|
},
|
||||||
|
"quantity": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"default": 1,
|
||||||
|
},
|
||||||
|
"variant_id": {"type": "string"},
|
||||||
|
"choice": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["item_url"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "商品页 URL(简写)",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"item_url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "旧版单商品商品页 URL",
|
||||||
|
},
|
||||||
|
"quantity": {"type": "integer", "minimum": 1, "default": 1},
|
||||||
|
"variant_id": {"type": "string"},
|
||||||
|
"choice": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "array", "items": {"type": "string"}},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"max_total_yen": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "本次订单允许的最高应付金额(日元)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SubmitOrderRequest(BaseModel):
|
class SubmitOrderRequest(BaseModel):
|
||||||
"""上游提交下单意图
|
"""上游提交下单意图
|
||||||
|
|
||||||
@@ -34,7 +107,7 @@ class SubmitOrderRequest(BaseModel):
|
|||||||
description="站点标识。交易服务只覆盖乐天市场,固定 rakuten",
|
description="站点标识。交易服务只覆盖乐天市场,固定 rakuten",
|
||||||
examples=["rakuten"],
|
examples=["rakuten"],
|
||||||
)
|
)
|
||||||
intent: dict[str, Any] = Field(
|
intent: OrderIntent = Field(
|
||||||
description=(
|
description=(
|
||||||
"下单意图原文。网关不解释内容,原样存库并透传给本地 worker,结构由 trading 侧定义:"
|
"下单意图原文。网关不解释内容,原样存库并透传给本地 worker,结构由 trading 侧定义:"
|
||||||
"推荐使用 items(非空数组,每项含 item_url,及可选 quantity/variant_id/choice);"
|
"推荐使用 items(非空数组,每项含 item_url,及可选 quantity/variant_id/choice);"
|
||||||
@@ -50,6 +123,9 @@ class SubmitOrderRequest(BaseModel):
|
|||||||
"item_url": "https://item.rakuten.co.jp/shop/code/",
|
"item_url": "https://item.rakuten.co.jp/shop/code/",
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"variant_id": "1001",
|
"variant_id": "1001",
|
||||||
|
}, {
|
||||||
|
"item_url": "https://item.rakuten.co.jp/shop/another-code/",
|
||||||
|
"quantity": 2,
|
||||||
}],
|
}],
|
||||||
"max_total_yen": 30000,
|
"max_total_yen": 30000,
|
||||||
}],
|
}],
|
||||||
|
|||||||
@@ -103,3 +103,16 @@ def test_all_refs_resolve(spec):
|
|||||||
names = set(spec["components"]["schemas"])
|
names = set(spec["components"]["schemas"])
|
||||||
refs = set(re.findall(r"#/components/schemas/([^\"]+)", json.dumps(spec)))
|
refs = set(re.findall(r"#/components/schemas/([^\"]+)", json.dumps(spec)))
|
||||||
assert not refs - names
|
assert not refs - names
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_order_intent_schema_documents_multi_item_and_legacy_fields(spec):
|
||||||
|
"""intent 保持透传对象,同时在 OpenAPI 中明确展示新旧两种商品格式。"""
|
||||||
|
intent = spec["components"]["schemas"]["SubmitOrderRequest"]["properties"]["intent"]
|
||||||
|
properties = intent["properties"]
|
||||||
|
assert intent["additionalProperties"] is True
|
||||||
|
assert properties["items"]["type"] == "array"
|
||||||
|
assert properties["items"]["minItems"] == 1
|
||||||
|
item_object = properties["items"]["items"]["oneOf"][0]
|
||||||
|
assert item_object["required"] == ["item_url"]
|
||||||
|
assert "item_url" in properties
|
||||||
|
assert "quantity" in properties
|
||||||
|
|||||||
Reference in New Issue
Block a user