fix(gateway): document multi-item order intent schema

This commit is contained in:
2026-08-31 12:30:09 +08:00
parent f76c0a1518
commit f5d717dae8
2 changed files with 92 additions and 3 deletions
+79 -3
View File
@@ -6,10 +6,10 @@ intent 字段刻意保留成 `dict[str, Any]`——网关不解释下单意图
"""
from __future__ import annotations
from typing import Any
from typing import Annotated, Any
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
@@ -17,6 +17,79 @@ from app.shared.task_state import AccountQueryKind, OrderState, QueryStatus, Tas
# ---- 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):
"""上游提交下单意图
@@ -34,7 +107,7 @@ class SubmitOrderRequest(BaseModel):
description="站点标识。交易服务只覆盖乐天市场,固定 rakuten",
examples=["rakuten"],
)
intent: dict[str, Any] = Field(
intent: OrderIntent = Field(
description=(
"下单意图原文。网关不解释内容,原样存库并透传给本地 worker,结构由 trading 侧定义:"
"推荐使用 items(非空数组,每项含 item_url,及可选 quantity/variant_id/choice);"
@@ -50,6 +123,9 @@ class SubmitOrderRequest(BaseModel):
"item_url": "https://item.rakuten.co.jp/shop/code/",
"quantity": 1,
"variant_id": "1001",
}, {
"item_url": "https://item.rakuten.co.jp/shop/another-code/",
"quantity": 2,
}],
"max_total_yen": 30000,
}],
+13
View File
@@ -103,3 +103,16 @@ def test_all_refs_resolve(spec):
names = set(spec["components"]["schemas"])
refs = set(re.findall(r"#/components/schemas/([^\"]+)", json.dumps(spec)))
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