feat: 新增用车需求服务及线索管理全功能

- 新增后台用车服务配置模块,支持维护服务简介、优势与使用流程
- 新增需求线索管理页面,支持筛选、查看与更新用车线索状态
- 新增小程序用车需求页面,优化登录路径与回跳逻辑
- 优化车型卡片跳转与用车需求提交功能
- 新增服务端API与数据处理逻辑,完善权限校验
- 更新全站配置与API文档,新增相关测试用例
This commit is contained in:
duanshuwen committed 2026-08-22 21:48:08 +08:00
1 parent b7a81964c9
commit d42f69bd47
36 files changed
+1290 -33

No files matched your search

+18 -1
View File
@@ -366,6 +366,23 @@ def test_admin_login_returns_token_and_user():
{"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):
@@ -382,7 +399,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 == "demandForm":
if module in {"demandForm", "vehicleService"}:
assert "sortOrder" not in body
else:
assert body["sortOrder"] == 5
+42
View File
@@ -1,6 +1,48 @@
import pytest
from pydantic import ValidationError
from app.schemas import LeadCreateIn
def test_lead_phone_is_normalized():
lead = LeadCreateIn(phone=" 187 8617 4929 ")
assert lead.phone == "187 8617 4929"
def test_vehicle_lead_normalizes_nested_demand():
lead = LeadCreateIn(
leadType="vehicle",
contactName=" Damon ",
phone=" 13800000000 ",
vehicleDemand={
"serviceType": "charter",
"charterDuration": "fullDay",
"travelDate": "2026-08-25",
"pickupTime": "09:00",
"pickupLocation": " 贵阳北站 ",
"dropoffLocation": " 黄果树景区 ",
"peopleCount": 5,
"luggageCount": 3,
"vehicleOptionId": "vehicle-option-uuid",
"vehicleOptionTitle": "多人商务车",
"specialRequirements": " 需要儿童座椅 ",
},
sourcePage="home-vehicle",
)
assert lead.leadType == "vehicle"
assert lead.contactName == "Damon"
assert lead.vehicleDemand.travelDate.isoformat() == "2026-08-25"
assert lead.vehicleDemand.pickupLocation == "贵阳北站"
assert lead.vehicleDemand.specialRequirements == "需要儿童座椅"
def test_vehicle_lead_requires_vehicle_demand():
with pytest.raises(ValidationError):
LeadCreateIn(leadType="vehicle", phone="13800000000")
def test_generic_lead_remains_guest_compatible():
lead = LeadCreateIn(phone="13800000000", destination="贵州")
assert lead.leadType == "general"
assert lead.vehicleDemand is None
@@ -0,0 +1,98 @@
from datetime import datetime
from fastapi.testclient import TestClient
from app.auth import optional_customer
from app.database import get_db
from app.main import create_app
from app.models import Customer, Lead
class FakeDb:
def __init__(self):
self.added = []
self.committed = False
def add(self, item):
self.added.append(item)
def commit(self):
self.committed = True
def refresh(self, item):
if not item.id:
item.id = "lead-vehicle-test"
def test_vehicle_lead_requires_customer_token():
db = FakeDb()
app = create_app()
app.dependency_overrides[get_db] = lambda: db
app.dependency_overrides[optional_customer] = lambda: None
try:
response = TestClient(app).post(
"/api/public/leads",
json={
"leadType": "vehicle",
"phone": "13800000000",
"vehicleDemand": {
"serviceType": "transfer",
"travelDate": "2026-08-25",
"pickupLocation": "贵阳北站",
"dropoffLocation": "黄果树景区",
"peopleCount": 5,
},
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 401
assert response.json()["data"] is None
def test_vehicle_lead_persists_customer_and_summary_fields():
db = FakeDb()
customer = Customer(id="customer-test", phone="13800000000")
app = create_app()
app.dependency_overrides[get_db] = lambda: db
app.dependency_overrides[optional_customer] = lambda: customer
try:
response = TestClient(app).post(
"/api/public/leads",
json={
"leadType": "vehicle",
"contactName": "Damon",
"phone": "13800000000",
"note": "需要儿童座椅",
"sourcePage": "home-vehicle",
"vehicleDemand": {
"serviceType": "charter",
"charterDuration": "fullDay",
"travelDate": "2026-08-25",
"pickupTime": "09:00",
"pickupLocation": "贵阳北站",
"dropoffLocation": "黄果树景区",
"peopleCount": 5,
"luggageCount": 3,
"vehicleOptionId": "vehicle-option-1",
"vehicleOptionTitle": "多人商务车",
"specialRequirements": "需要儿童座椅",
},
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 201
lead = next(item for item in db.added if isinstance(item, Lead))
assert lead.customerId == "customer-test"
assert lead.destination == "黄果树景区"
assert lead.peopleCount == 5
assert lead.travelDate == datetime(2026, 8, 25)
assert lead.vehicleDemand["vehicleOptionTitle"] == "多人商务车"
assert response.json() == {
"code": 201,
"msg": "success",
"data": {"id": lead.id, "status": "new"},
}