下单接口支持其他店铺

/api/order/get_shipping_fee 与购物车加入逻辑此前只认官方自营详情页,加盟店运费分桶
仅靠自由文本 supplier 判断,未与其他店铺列表的 tenpo_cd 打通。补充 OrderItem/
CargoItemRequest 的 tenpo_cd/branch_number 字段,运费计算优先按 tenpo_cd 分桶
(无则回退 supplier 保持兼容),cargo_service 加购 URL 同步带上店铺参数。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 22:31:03 +08:00
parent fe2a3f3768
commit 118667d01f
6 changed files with 139 additions and 5 deletions
+68
View File
@@ -0,0 +1,68 @@
"""/api/order/get_shipping_fee 中"店铺标识"分桶逻辑的单元测试:
- 店铺标识优先取 tenpo_cd(与 other_shop_list 一致),未提供时回退取 supplier
- 同 tenpo_cd(或同 supplier)视为同一家加盟店,只收一次 800 日元
"""
from __future__ import annotations
import pytest
from app.api.routes.scrape import order_get_shipping_fee
from app.models.scrape import OrderItem, OrderShippingFeeRequest
@pytest.mark.asyncio
async def test_official_only_uses_official_ladder() -> None:
request = OrderShippingFeeRequest(item_list=[OrderItem(goods_id="1", goods_number=1, goods_price=800)])
resp = await order_get_shipping_fee(request)
assert resp.data.shipping_fee == 440
assert resp.data.goods_amount == 800
@pytest.mark.asyncio
async def test_tenpo_cd_groups_same_franchise_store_once() -> None:
request = OrderShippingFeeRequest(
item_list=[
OrderItem(goods_id="1", goods_number=1, goods_price=1000, tenpo_cd="400451"),
OrderItem(goods_id="2", goods_number=1, goods_price=2000, tenpo_cd="400451", branch_number="0003"),
]
)
resp = await order_get_shipping_fee(request)
assert resp.data.shipping_fee == 800 # 同一 tenpo_cd 只收一次加盟店运费
assert resp.data.goods_amount == 3000
@pytest.mark.asyncio
async def test_different_tenpo_cd_charged_separately() -> None:
request = OrderShippingFeeRequest(
item_list=[
OrderItem(goods_id="1", goods_number=1, goods_price=1000, tenpo_cd="400451"),
OrderItem(goods_id="2", goods_number=1, goods_price=1000, tenpo_cd="400999"),
]
)
resp = await order_get_shipping_fee(request)
assert resp.data.shipping_fee == 1600 # 两家不同加盟店各收 800
@pytest.mark.asyncio
async def test_tenpo_cd_takes_precedence_over_supplier() -> None:
request = OrderShippingFeeRequest(
item_list=[
OrderItem(goods_id="1", goods_number=1, goods_price=1000, supplier="山口大学前店", tenpo_cd="400451"),
OrderItem(goods_id="2", goods_number=1, goods_price=1000, supplier="其他名称", tenpo_cd="400451"),
]
)
resp = await order_get_shipping_fee(request)
assert resp.data.shipping_fee == 800 # 尽管 supplier 文本不同,tenpo_cd 相同仍视为同一家
@pytest.mark.asyncio
async def test_falls_back_to_supplier_when_tenpo_cd_missing() -> None:
request = OrderShippingFeeRequest(
item_list=[
OrderItem(goods_id="1", goods_number=1, goods_price=1000, supplier="老加盟店"),
OrderItem(goods_id="2", goods_number=1, goods_price=1000, supplier="老加盟店"),
]
)
resp = await order_get_shipping_fee(request)
assert resp.data.shipping_fee == 800 # 无 tenpo_cd 时按 supplier 分桶,保持向后兼容
+50
View File
@@ -59,3 +59,53 @@ class TestFetchOtherShops:
client = _make_client()
with pytest.raises(Exception):
await client.fetch_other_shops(DetailRequest(id=None))
class TestFetchDetailIncludeOtherShops:
"""DetailRequest.include_other_shops 控制 fetch_detail 是否顺带抓取 other_shop_list。"""
MINIMAL_DETAIL_HTML = '<html><body><h1 id="item_title">测试商品</h1></body></html>'
@pytest.mark.asyncio
async def test_flag_false_skips_other_shop_fetch(self, monkeypatch: pytest.MonkeyPatch) -> None:
client = _make_client()
monkeypatch.setattr(client, "_session_manager", _FakeSessionManager(self.MINIMAL_DETAIL_HTML))
called = False
async def fake_fetch_other_shop_list(goods_id: str) -> list[OtherShopItem]:
nonlocal called
called = True
return []
monkeypatch.setattr(client, "_fetch_other_shop_list", fake_fetch_other_shop_list)
detail = await client.fetch_detail(DetailRequest(id="220035970"))
assert called is False
assert detail.other_shop_list == []
@pytest.mark.asyncio
async def test_flag_true_includes_other_shop_list(self, monkeypatch: pytest.MonkeyPatch) -> None:
client = _make_client()
monkeypatch.setattr(client, "_session_manager", _FakeSessionManager(self.MINIMAL_DETAIL_HTML))
fake_items = [OtherShopItem(tenpo_cd="400451", shop_name="駿河屋 山口大学前店", price=1370)]
async def fake_fetch_other_shop_list(goods_id: str) -> list[OtherShopItem]:
assert goods_id == "220035970"
return fake_items
monkeypatch.setattr(client, "_fetch_other_shop_list", fake_fetch_other_shop_list)
detail = await client.fetch_detail(DetailRequest(id="220035970", include_other_shops=True))
assert detail.other_shop_list == fake_items
class _FakeSessionManager:
def __init__(self, html: str) -> None:
self._html = html
async def fetch_html(self, url: str):
return self._html, None