feat(wanfa): 新增路线详情参考价格配置及展示功能

- 新增数据库迁移脚本,为详情表添加四个价格相关字段
- 完善后端schema校验、序列化逻辑,增加价格相关的数据清理与验证规则
- 新增价格配置校验逻辑,设置起售价时必须选择对应的计价单位
- 在Admin UI详情编辑器中新增价格配置模块,支持配置起售价、计价单位、适用人数范围和价格说明
- 更新小程序端详情页面,支持展示配置的参考价格信息
- 补充相关测试用例,更新API文档与类型定义
This commit is contained in:
duanshuwen committed 2026-08-27 22:02:51 +08:00
1 parent f18971a430
commit feec6ab80f
20 files changed
+351 -17

No files matched your search

@@ -0,0 +1,28 @@
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
MIGRATION_PATH = Path(__file__).parents[1] / "alembic" / "versions" / "0037_detail_price_config.py"
SPEC = spec_from_file_location("detail_price_config_migration", MIGRATION_PATH)
assert SPEC and SPEC.loader
MIGRATION = module_from_spec(SPEC)
SPEC.loader.exec_module(MIGRATION)
def test_price_columns_are_added_only_when_missing():
assert MIGRATION.missing_price_columns(set()) == {
"priceStartingValue",
"priceUnit",
"pricePeopleRange",
"priceDescription",
}
assert MIGRATION.missing_price_columns({"priceStartingValue", "priceUnit"}) == {
"pricePeopleRange",
"priceDescription",
}
assert MIGRATION.missing_price_columns({
"priceStartingValue",
"priceUnit",
"pricePeopleRange",
"priceDescription",
}) == set()
+36
View File
@@ -77,6 +77,10 @@ def make_detail(**overrides):
"excluded": ["往返大交通"],
"notes": ["请准备轻便雨具。"],
"gallery": ["https://example.test/family-water.jpg"],
"priceStartingValue": 1.68,
"priceUnit": "person",
"pricePeopleRange": "2-4人",
"priceDescription": "价格以最终确认方案为准。",
"isActive": True,
"sortOrder": 0,
"conciergeAdvisorId": None,
@@ -124,11 +128,33 @@ def test_detail_schema_trims_text_and_array_items():
excluded=[" 往返大交通 "],
notes=[" 请准备雨具。 "],
gallery=[" https://example.test/family-water.jpg "],
priceStartingValue=1.68,
priceUnit="person",
pricePeopleRange=" 2-4人 ",
priceDescription=" 价格以最终确认方案为准。 ",
)
assert detail.key == "family-water"
assert detail.highlights == ["核心景观串联", "小团出行"]
assert detail.gallery == ["https://example.test/family-water.jpg"]
assert detail.priceStartingValue == 1.68
assert detail.priceUnit == "person"
assert detail.pricePeopleRange == "2-4人"
assert detail.priceDescription == "价格以最终确认方案为准。"
def test_detail_schema_rejects_unknown_price_unit():
with pytest.raises(ValidationError):
DetailCreate(
key="family-water",
eyebrow="玩法推荐",
duration="5天4晚",
title="亲子玩水",
subtitle="贵州路线",
intro="介绍",
priceStartingValue=1.68,
priceUnit="month",
)
def test_detail_schema_rejects_invalid_gallery_url():
@@ -188,6 +214,10 @@ def test_public_detail_returns_frontend_contract_without_auth():
"excluded": ["往返大交通"],
"notes": ["请准备轻便雨具。"],
"gallery": ["https://example.test/family-water.jpg"],
"priceStartingValue": 1.68,
"priceUnit": "person",
"pricePeopleRange": "2-4人",
"priceDescription": "价格以最终确认方案为准。",
"conciergeAdvisor": None,
},
}
@@ -294,6 +324,10 @@ def test_admin_detail_create_normalizes_key_and_appends():
"excluded": [],
"notes": [],
"gallery": ["https://example.test/family-water.jpg"],
"priceStartingValue": 1.68,
"priceUnit": "person",
"pricePeopleRange": "2-4人",
"priceDescription": "价格以最终确认方案为准。",
},
)
finally:
@@ -301,6 +335,8 @@ def test_admin_detail_create_normalizes_key_and_appends():
assert response.status_code == 201
assert response.json()["data"]["key"] == "family-water"
assert fake_db.added[0].priceStartingValue == 1.68
assert fake_db.added[0].priceUnit == "person"
assert fake_db.added[0].sortOrder == 3