feat: add customer travel records and browse history

This commit is contained in:
duanshuwen committed 2026-08-27 14:19:03 +08:00
1 parent 0139fe6382
commit 6d9a546918
79 files changed
+12362 -112

No files matched your search

+158 -4
View File
@@ -1,13 +1,14 @@
from datetime import datetime, time
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import func, select
from sqlalchemy.orm import Session, selectinload
from .shared import site_config
from ..auth import create_customer_token, optional_customer, require_customer
from ..api_response import success_response
from ..database import get_db
from ..models import ConciergeAdvisor, Customer, DetailRecord, HomeTeamBuilding, HomeWanfaRecommendation, HomeWildArchive, Lead, WanfaCategory
from ..schemas import LeadCreateIn, PhoneLoginIn
from ..models import ConciergeAdvisor, Customer, CustomerBrowseHistory, DetailRecord, HomeTeamBuilding, HomeWanfaRecommendation, HomeWildArchive, Lead, WanfaCategory, WanfaRoute, utc_now
from ..schemas import BrowseHistoryCreateIn, LeadCreateIn, PhoneLoginIn
from ..media_urls import resolve_media_url
from ..serializers import (
public_concierge_advisor_dict,
public_home_team_building_dict,
@@ -35,6 +36,52 @@ def public_customer_dict(customer: Customer) -> dict:
return {"id": customer.id, "phoneMasked": mask_phone(customer.phone)}
def public_vehicle_demand_dict(lead: Lead) -> dict:
return {
"id": lead.id,
"status": lead.status,
"contactName": lead.contactName,
"phoneMasked": mask_phone(lead.phone),
"destination": lead.destination,
"travelDate": lead.travelDate.isoformat() if lead.travelDate else None,
"peopleCount": lead.peopleCount,
"note": lead.note,
"vehicleDemand": lead.vehicleDemand or {},
"createdAt": lead.createdAt.isoformat(),
"updatedAt": lead.updatedAt.isoformat(),
}
def public_browse_history_dict(item: CustomerBrowseHistory) -> dict:
return {
"id": item.id,
"itemType": item.itemType,
"itemId": item.itemId,
"title": item.title,
"image": resolve_media_url(item.image) or "",
"visitedAt": item.visitedAt.isoformat(),
}
def browse_source(item_type: str, item_id: str, db: Session):
models = {
"wanfa-route": WanfaRoute,
"team-building": HomeTeamBuilding,
"wild-archive": HomeWildArchive,
}
model = models.get(item_type)
if model is None:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="浏览内容类型不支持")
conditions = [model.id == item_id]
if hasattr(model, "isActive"):
conditions.append(model.isActive.is_(True))
source = db.scalar(select(model).where(*conditions))
if not source:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="浏览内容不存在")
return source
@router.get("/health")
def health():
return success_response({"ok": True, "service": "miniapp-api"})
@@ -170,6 +217,113 @@ def current_customer(customer: Customer = Depends(require_customer)):
return success_response(public_customer_dict(customer))
@router.get("/api/public/customer/vehicle-demands")
def list_customer_vehicle_demands(
pageNum: int = Query(default=1, ge=1),
pageSize: int = Query(default=20, ge=1, le=50),
customer: Customer = Depends(require_customer),
db: Session = Depends(get_db),
):
filters = [Lead.customerId == customer.id, Lead.leadType == "vehicle"]
leads = db.scalars(
select(Lead)
.where(*filters)
.order_by(Lead.createdAt.desc())
.offset((pageNum - 1) * pageSize)
.limit(pageSize)
).all()
total = db.scalar(select(func.count()).select_from(Lead).where(*filters)) or 0
return success_response(
{
"items": [public_vehicle_demand_dict(lead) for lead in leads],
"total": total,
"pageNum": pageNum,
"pageSize": pageSize,
}
)
@router.get("/api/public/customer/vehicle-demands/{lead_id}")
def get_customer_vehicle_demand(
lead_id: str,
customer: Customer = Depends(require_customer),
db: Session = Depends(get_db),
):
lead = db.scalar(
select(Lead).where(
Lead.id == lead_id,
Lead.customerId == customer.id,
Lead.leadType == "vehicle",
)
)
if not lead:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用车记录不存在")
return success_response(public_vehicle_demand_dict(lead))
@router.get("/api/public/customer/browse-history")
def list_customer_browse_history(
pageNum: int = Query(default=1, ge=1),
pageSize: int = Query(default=20, ge=1, le=50),
customer: Customer = Depends(require_customer),
db: Session = Depends(get_db),
):
filters = [CustomerBrowseHistory.customerId == customer.id]
items = db.scalars(
select(CustomerBrowseHistory)
.where(*filters)
.order_by(CustomerBrowseHistory.visitedAt.desc())
.offset((pageNum - 1) * pageSize)
.limit(pageSize)
).all()
total = db.scalar(select(func.count()).select_from(CustomerBrowseHistory).where(*filters)) or 0
return success_response(
{
"items": [public_browse_history_dict(item) for item in items],
"total": total,
"pageNum": pageNum,
"pageSize": pageSize,
}
)
@router.post("/api/public/customer/browse-history", status_code=status.HTTP_201_CREATED)
def save_customer_browse_history(
body: BrowseHistoryCreateIn,
customer: Customer = Depends(require_customer),
db: Session = Depends(get_db),
):
source = browse_source(body.itemType, body.itemId, db)
item = db.scalar(
select(CustomerBrowseHistory).where(
CustomerBrowseHistory.customerId == customer.id,
CustomerBrowseHistory.itemType == body.itemType,
CustomerBrowseHistory.itemId == body.itemId,
)
)
now = utc_now()
if item:
item.title = source.title
item.image = source.image or ""
item.visitedAt = now
item.updatedAt = now
else:
item = CustomerBrowseHistory(
customerId=customer.id,
itemType=body.itemType,
itemId=body.itemId,
title=source.title,
image=source.image or "",
visitedAt=now,
createdAt=now,
updatedAt=now,
)
db.add(item)
db.commit()
db.refresh(item)
return success_response(public_browse_history_dict(item), status_code=status.HTTP_201_CREATED)
@router.post("/api/public/leads", status_code=status.HTTP_201_CREATED)
def create_lead(
body: LeadCreateIn,