c1638db6b2
- Save every search result to DB for later review - Add '搜索历史' tab with timeline view, load/delete records - Raise discovery search timeout from 30s to 120s (Bing Puppeteer needs ~40s) - Reduce search queries from 4 to 3 for faster response - New model: DiscoveryRecord (user_id, product, market, companies JSON) - API: POST/GET/DELETE /api/v1/discovery/records - Migration: discovery_records table
132 lines
3.6 KiB
Python
132 lines
3.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, desc
|
|
from typing import List, Optional
|
|
from app.database import get_db
|
|
from app.api.v1.deps import get_current_user_id
|
|
from app.models.discovery_record import DiscoveryRecord
|
|
from pydantic import BaseModel
|
|
from datetime import datetime
|
|
import uuid
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class SaveRecordRequest(BaseModel):
|
|
product: str
|
|
market: str = ""
|
|
companies: list
|
|
|
|
|
|
class RecordResponse(BaseModel):
|
|
id: str
|
|
product: str
|
|
market: str
|
|
companies: list
|
|
created_at: str
|
|
|
|
|
|
@router.post("/records")
|
|
async def save_record(
|
|
req: SaveRecordRequest,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
record = DiscoveryRecord(
|
|
user_id=uuid.UUID(user_id),
|
|
product=req.product,
|
|
market=req.market,
|
|
companies=req.companies,
|
|
)
|
|
db.add(record)
|
|
await db.commit()
|
|
return {"success": True, "data": {"id": str(record.id)}}
|
|
|
|
|
|
@router.get("/records")
|
|
async def list_records(
|
|
page: int = Query(1, ge=1),
|
|
size: int = Query(20, ge=1, le=50),
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
stmt = (
|
|
select(DiscoveryRecord)
|
|
.where(DiscoveryRecord.user_id == uuid.UUID(user_id))
|
|
.order_by(desc(DiscoveryRecord.created_at))
|
|
.offset((page - 1) * size)
|
|
.limit(size)
|
|
)
|
|
result = await db.execute(stmt)
|
|
records = result.scalars().all()
|
|
total_stmt = select(DiscoveryRecord).where(DiscoveryRecord.user_id == uuid.UUID(user_id))
|
|
total_result = await db.execute(total_stmt)
|
|
total = len(total_result.scalars().all())
|
|
return {
|
|
"items": [
|
|
{
|
|
"id": str(r.id),
|
|
"product": r.product,
|
|
"market": r.market,
|
|
"companies": r.companies or [],
|
|
"created_at": r.created_at.isoformat() if r.created_at else "",
|
|
}
|
|
for r in records
|
|
],
|
|
"total": total,
|
|
"page": page,
|
|
"size": size,
|
|
}
|
|
|
|
|
|
@router.get("/records/{record_id}")
|
|
async def get_record(
|
|
record_id: str,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
try:
|
|
rid = uuid.UUID(record_id)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="Invalid record ID")
|
|
result = await db.execute(
|
|
select(DiscoveryRecord).where(
|
|
DiscoveryRecord.id == rid,
|
|
DiscoveryRecord.user_id == uuid.UUID(user_id),
|
|
)
|
|
)
|
|
r = result.scalar_one_or_none()
|
|
if not r:
|
|
raise HTTPException(status_code=404, detail="Record not found")
|
|
return {
|
|
"id": str(r.id),
|
|
"product": r.product,
|
|
"market": r.market,
|
|
"companies": r.companies or [],
|
|
"created_at": r.created_at.isoformat() if r.created_at else "",
|
|
}
|
|
|
|
|
|
@router.delete("/records/{record_id}")
|
|
async def delete_record(
|
|
record_id: str,
|
|
user_id: str = Depends(get_current_user_id),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
try:
|
|
rid = uuid.UUID(record_id)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="Invalid record ID")
|
|
result = await db.execute(
|
|
select(DiscoveryRecord).where(
|
|
DiscoveryRecord.id == rid,
|
|
DiscoveryRecord.user_id == uuid.UUID(user_id),
|
|
)
|
|
)
|
|
r = result.scalar_one_or_none()
|
|
if not r:
|
|
raise HTTPException(status_code=404, detail="Record not found")
|
|
await db.delete(r)
|
|
await db.commit()
|
|
return {"success": True}
|