refactor: 清理废弃模块并重构管理端与全端代码
- 移除所有废弃的旧首页配置模块,包括destinationHero、demand相关、HomeExperience、vehicleService等表结构与代码,新增数据库迁移删除遗留表 - 重构管理端后台布局为标准RuoYi风格,新增Sidebar、Navbar、SettingsDrawer等组件,替换旧的过渡菜单实现 - 调整移动端响应断点为768px,更新布局相关测试用例,新增布局组件测试 - 删除OperationsTools发布/重置页面,移除/tools菜单入口,清理对应的API调用与测试代码 - 重构玩法模块编辑器:替换旧的图片手动输入框为图片上传组件,移除冗余的取消按钮 - 新增线索查询offset参数支持,完善查询参数处理逻辑 - 优化rbac菜单构建逻辑,新增可见性控制参数 - 修正管家编辑器提示文案,优化系统资源编辑器表单初始化逻辑 - 清理小程序端废弃的体验推荐组件与相关测试代码 - 更新文档与种子脚本,匹配新的API契约与使用说明
This commit is contained in:
1 parent
f6c5ad589f
commit
fbf2c772a8
63 files changed
+2272
-1991
No files matched your search
@@ -7,7 +7,7 @@ from pydantic import ValidationError
|
||||
from app.auth import create_customer_token, hash_password, require_admin
|
||||
from app.database import get_db
|
||||
from app.main import create_app
|
||||
from app.models import AdminUser, Customer, DemandFeatureCard, DemandForm, DemandHero, DestinationHero, HeroSlide, Lead, MediaAsset, VehicleOption
|
||||
from app.models import AdminUser, Customer, HeroSlide, Lead, MediaAsset, VehicleOption
|
||||
from app.routers import admin as admin_router
|
||||
from app.routers import public as public_router
|
||||
from app.routers.shared import site_config
|
||||
@@ -113,62 +113,6 @@ def make_vehicle_option(**overrides):
|
||||
)
|
||||
|
||||
|
||||
def make_destination_hero(**overrides):
|
||||
return DestinationHero(
|
||||
id=overrides.get("id", "destination-hero-test"),
|
||||
title=overrides.get("title", "目的地页主视觉"),
|
||||
kicker=overrides.get("kicker", "贵州小包团目的地"),
|
||||
image=overrides.get("image", "/assets/destination-hero.jpg"),
|
||||
sortOrder=overrides.get("sortOrder", 0),
|
||||
isActive=overrides.get("isActive", True),
|
||||
createdAt=datetime(2026, 1, 1),
|
||||
updatedAt=datetime(2026, 1, 2),
|
||||
)
|
||||
|
||||
|
||||
def make_demand_hero(**overrides):
|
||||
return DemandHero(
|
||||
id=overrides.get("id", "demand-hero-test"),
|
||||
title=overrides.get("title", "告诉我们日期、人数和想法"),
|
||||
kicker=overrides.get("kicker", "3步定制"),
|
||||
description=overrides.get("description", "管家会按同行人、预算和体力强度,重新组合酒店、用车和景点节奏。"),
|
||||
steps=overrides.get("steps", ["提交需求", "管家沟通", "确认方案"]),
|
||||
sortOrder=overrides.get("sortOrder", 0),
|
||||
isActive=overrides.get("isActive", True),
|
||||
createdAt=datetime(2026, 1, 1),
|
||||
updatedAt=datetime(2026, 1, 2),
|
||||
)
|
||||
|
||||
|
||||
def make_demand_feature_card(**overrides):
|
||||
return DemandFeatureCard(
|
||||
id=overrides.get("id", "demand-feature-card-test"),
|
||||
title=overrides.get("title", "动线"),
|
||||
description=overrides.get("description", "按天数顺路排"),
|
||||
sortOrder=overrides.get("sortOrder", 0),
|
||||
isActive=overrides.get("isActive", True),
|
||||
createdAt=datetime(2026, 1, 1),
|
||||
updatedAt=datetime(2026, 1, 2),
|
||||
)
|
||||
|
||||
|
||||
def make_demand_form(**overrides):
|
||||
return DemandForm(
|
||||
id=overrides.get("id", "demand-form-test"),
|
||||
destinationLabel=overrides.get("destinationLabel", "目的地/玩法"),
|
||||
destinationPlaceholder=overrides.get("destinationPlaceholder", "例如:贵州、黄果树、西江苗寨"),
|
||||
phoneLabel=overrides.get("phoneLabel", "联系方式"),
|
||||
phonePlaceholder=overrides.get("phonePlaceholder", "手机号 / 微信号"),
|
||||
noteLabel=overrides.get("noteLabel", "补充说明"),
|
||||
notePlaceholder=overrides.get("notePlaceholder", "出行日期、人数、酒店偏好、预算范围"),
|
||||
submitLabel=overrides.get("submitLabel", "提交出行需求"),
|
||||
chips=overrides.get("chips", ["贵州", "黄果树", "荔波小七孔", "西江苗寨", "梵净山", "万峰林"]),
|
||||
isActive=overrides.get("isActive", True),
|
||||
createdAt=datetime(2026, 1, 1),
|
||||
updatedAt=datetime(2026, 1, 2),
|
||||
)
|
||||
|
||||
|
||||
def make_admin_user():
|
||||
return AdminUser(
|
||||
id="admin-test",
|
||||
@@ -349,43 +293,10 @@ def test_admin_login_returns_token_and_user():
|
||||
("module", "payload", "expected"),
|
||||
[
|
||||
("heroSlides", {"title": "新轮播", "image": None, "targetType": None}, {"title": "新轮播", "image": None}),
|
||||
("destinationHero", {"title": "目的地页主视觉", "kicker": "贵州小包团目的地", "image": None}, {"title": "目的地页主视觉", "kicker": "贵州小包团目的地", "image": None}),
|
||||
("vehicleOptions", {"title": "5座舒适用车", "description": "用车文案", "image": None}, {"title": "5座舒适用车", "description": "用车文案", "image": None}),
|
||||
(
|
||||
"demandHero",
|
||||
{"title": "需求页主视觉", "kicker": "3步定制", "description": "说明", "steps": ["提交", "沟通", "确认"]},
|
||||
{"title": "需求页主视觉", "kicker": "3步定制", "description": "说明", "steps": ["提交", "沟通", "确认"]},
|
||||
),
|
||||
(
|
||||
"demandFeatureCards",
|
||||
{"title": "动线", "description": "按天数顺路排"},
|
||||
{"title": "动线", "description": "按天数顺路排"},
|
||||
),
|
||||
(
|
||||
"demandForm",
|
||||
{"submitLabel": "提交出行需求", "destinationLabel": "目的地/玩法", "chips": [" 贵州 ", "", "黄果树"]},
|
||||
{"submitLabel": "提交出行需求", "destinationLabel": "目的地/玩法", "chips": ["贵州", "黄果树"]},
|
||||
),
|
||||
(
|
||||
"vehicleService",
|
||||
{
|
||||
"introTitle": "旅游专属用车",
|
||||
"intro": "提交需求后由管家联系确认。",
|
||||
"serviceSections": [{"title": "包车", "description": "行程灵活"}],
|
||||
"advantages": ["车型匹配", "服务省心"],
|
||||
"processSteps": [{"title": "提交需求", "description": "填写日期和地点"}],
|
||||
},
|
||||
{
|
||||
"introTitle": "旅游专属用车",
|
||||
"intro": "提交需求后由管家联系确认。",
|
||||
"serviceSections": [{"title": "包车", "description": "行程灵活"}],
|
||||
"advantages": ["车型匹配", "服务省心"],
|
||||
"processSteps": [{"title": "提交需求", "description": "填写日期和地点"}],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_site_config_create_modules_defaults_fields_and_audits(module, payload, expected):
|
||||
def test_site_config_create_supported_modules_defaults_fields_and_audits(module, payload, expected):
|
||||
fake_db = FakeDb(scalar_values=[4])
|
||||
app = authenticated_app(fake_db)
|
||||
|
||||
@@ -399,10 +310,7 @@ def test_site_config_create_modules_defaults_fields_and_audits(module, payload,
|
||||
for key, value in expected.items():
|
||||
assert body[key] == value
|
||||
assert body["isActive"] is True
|
||||
if module in {"demandForm", "vehicleService"}:
|
||||
assert "sortOrder" not in body
|
||||
else:
|
||||
assert body["sortOrder"] == 5
|
||||
assert body["sortOrder"] == 5
|
||||
assert fake_db.committed
|
||||
assert fake_db.added[-1].action == "create"
|
||||
|
||||
@@ -465,70 +373,6 @@ def test_admin_site_config_hero_slides_use_dedicated_contract_without_targets():
|
||||
assert "targetValue" not in hero
|
||||
|
||||
|
||||
def test_admin_site_config_includes_destination_page_modules():
|
||||
fake_db = FakeDb(scalar_results=[[], [], [], [], [], [], [], [], [], [], []])
|
||||
app = authenticated_app(fake_db)
|
||||
|
||||
try:
|
||||
response = TestClient(app).get("/api/admin/site-config")
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()["data"]
|
||||
assert "destinations" not in body
|
||||
assert "ctaBanners" not in body
|
||||
assert "campaigns" not in body
|
||||
assert "hotelGroups" not in body
|
||||
assert "map" not in body
|
||||
assert "themes" not in body
|
||||
assert body["destinationHero"] == []
|
||||
assert "destinationRegions" not in body
|
||||
|
||||
|
||||
def test_admin_site_config_includes_demand_page_modules():
|
||||
fake_db = FakeDb(scalar_results=[[], [], [], [], [], [], [], [], [], [], [], [], [], [], []])
|
||||
app = authenticated_app(fake_db)
|
||||
|
||||
try:
|
||||
response = TestClient(app).get("/api/admin/site-config")
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()["data"]
|
||||
assert body["demandHero"] == []
|
||||
assert body["demandFeatureCards"] == []
|
||||
assert body["demandForm"] == []
|
||||
|
||||
|
||||
def test_public_site_config_includes_destination_page_modules():
|
||||
destination_hero = make_destination_hero()
|
||||
fake_db = FakeDb(scalar_results=[[], [destination_hero], [], [], [], [], [], [], [], [], [], []])
|
||||
|
||||
result = site_config(fake_db, active_only=True)
|
||||
|
||||
assert "destinations" not in result
|
||||
assert "ctaBanners" not in result
|
||||
assert "campaigns" not in result
|
||||
assert "hotelGroups" not in result
|
||||
assert "map" not in result
|
||||
assert "themes" not in result
|
||||
assert result["destinationHero"] == [
|
||||
{
|
||||
"id": "destination-hero-test",
|
||||
"title": "目的地页主视觉",
|
||||
"kicker": "贵州小包团目的地",
|
||||
"image": "/assets/destination-hero.jpg",
|
||||
"sortOrder": 0,
|
||||
"isActive": True,
|
||||
"createdAt": "2026-01-01T00:00:00",
|
||||
"updatedAt": "2026-01-02T00:00:00",
|
||||
}
|
||||
]
|
||||
assert "destinationRegions" not in result
|
||||
|
||||
|
||||
def test_site_config_create_hero_slide_ignores_target_fields_and_returns_dedicated_contract():
|
||||
fake_db = FakeDb(scalar_values=[0])
|
||||
app = authenticated_app(fake_db)
|
||||
@@ -558,21 +402,6 @@ def test_site_config_create_hero_slide_ignores_target_fields_and_returns_dedicat
|
||||
assert created.targetValue is None
|
||||
|
||||
|
||||
def test_site_config_create_demand_form_rejects_duplicate_singleton():
|
||||
fake_db = FakeDb(scalar_results=[[make_demand_form()]])
|
||||
app = authenticated_app(fake_db)
|
||||
|
||||
try:
|
||||
response = TestClient(app).post("/api/admin/site-config/demandForm", json={"submitLabel": "提交"})
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 409
|
||||
assert response.json()["errorCode"] == "MODULE_CONFIG_SINGLETON_EXISTS"
|
||||
assert not fake_db.added
|
||||
assert not fake_db.committed
|
||||
|
||||
|
||||
def test_site_config_patch_hero_slide_ignores_target_fields_and_returns_dedicated_contract():
|
||||
hero_slide = make_hero_slide(targetType="search", targetValue="search-test")
|
||||
fake_db = FakeDb(get_result=hero_slide)
|
||||
@@ -734,7 +563,7 @@ def test_admin_leads_endpoint_accepts_source_keyword_and_created_range_filters()
|
||||
assert response.json() == {
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {"items": []},
|
||||
"data": {"items": [], "total": 0, "offset": 0, "take": 50},
|
||||
}
|
||||
stmt = str(fake_db.scalar_statements[0])
|
||||
assert '"Lead".status' in stmt
|
||||
|
||||
Reference in new issue
Block a user