Add admin-frontend and user-frontend standalone projects, certification/invoice/discovery features, fix auth header and theme consistency

This commit is contained in:
TradeMate Dev
2026-05-22 18:35:30 +08:00
parent 18c6cf5406
commit 52dba37f22
79 changed files with 10333 additions and 248 deletions
+62
View File
@@ -6,6 +6,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.admin import AdminService
from app.services.translation_quota import TranslationQuotaService
from app.services.certification import CertificationService
from app.services.invoice import InvoiceService
from app.api.v1.deps import get_current_user
router = APIRouter()
@@ -212,3 +214,63 @@ async def reset_translation_quota(
if not result:
raise HTTPException(status_code=404, detail="Quota not found")
return result
@router.get("/certifications")
async def admin_list_certifications(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
status: Optional[str] = Query(None),
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
service = CertificationService(db)
return await service.list_all(page, size, status)
@router.post("/certifications/{cert_id}/review")
async def admin_review_certification(
cert_id: str,
data: dict,
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
_validate_uuid(cert_id)
service = CertificationService(db)
action = data.get("action")
if action not in ("approve", "reject"):
raise HTTPException(status_code=400, detail="Action must be 'approve' or 'reject'")
result = await service.review(cert_id, action, data.get("reason"))
if not result:
raise HTTPException(status_code=404, detail="Certification not found")
return result
@router.get("/invoices")
async def admin_list_invoices(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
status: Optional[str] = Query(None),
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
service = InvoiceService(db)
return await service.list_all(page, size, status)
@router.post("/invoices/{invoice_id}/process")
async def admin_process_invoice(
invoice_id: str,
data: dict,
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
_validate_uuid(invoice_id)
service = InvoiceService(db)
action = data.get("action")
if action not in ("issue", "reject"):
raise HTTPException(status_code=400, detail="Action must be 'issue' or 'reject'")
result = await service.process(invoice_id, action, data.get("reason"))
if not result:
raise HTTPException(status_code=404, detail="Invoice not found")
return result
+41
View File
@@ -0,0 +1,41 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel
from typing import Optional
from app.database import get_db
from app.api.v1.deps import get_current_user_id
from app.services.certification import CertificationService
router = APIRouter()
class CertSubmitRequest(BaseModel):
cert_type: str
personal_name: Optional[str] = None
personal_id: Optional[str] = None
company_name: Optional[str] = None
tax_id: Optional[str] = None
business_license_url: Optional[str] = None
@router.post("/submit")
async def submit_certification(
data: CertSubmitRequest,
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
service = CertificationService(db)
result = await service.submit(user_id, data.model_dump())
if "error" in result:
raise HTTPException(status_code=400, detail=result["error"])
return {"success": True, "data": result}
@router.get("/status")
async def get_certification_status(
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
service = CertificationService(db)
cert = await service.get_user_cert(user_id)
return {"success": True, "data": cert}
+61
View File
@@ -0,0 +1,61 @@
from fastapi import APIRouter, HTTPException
from typing import Optional, Dict, Any
from pydantic import BaseModel
from app.services.discovery import DiscoveryService
router = APIRouter()
class SearchRequest(BaseModel):
product_description: str
target_market: str = "US"
class AnalyzeRequest(BaseModel):
company_url: str
product_description: str
class OutreachRequest(BaseModel):
company: Dict[str, Any]
product: Dict[str, Any]
@router.post("/search")
async def search_leads(req: SearchRequest):
if not req.product_description.strip():
raise HTTPException(status_code=400, detail="请填写产品描述")
svc = DiscoveryService()
try:
result = await svc.search(req.product_description, req.target_market)
return {"success": True, "data": result}
except Exception as e:
raise HTTPException(status_code=500, detail=f"搜索失败: {str(e)}")
@router.post("/analyze")
async def analyze_company(req: AnalyzeRequest):
if not req.company_url.strip():
raise HTTPException(status_code=400, detail="请填写公司网址")
if not req.product_description.strip():
raise HTTPException(status_code=400, detail="请填写产品描述")
svc = DiscoveryService()
try:
result = await svc.analyze(req.company_url, req.product_description)
return {"success": True, "data": result}
except Exception as e:
raise HTTPException(status_code=500, detail=f"分析失败: {str(e)}")
@router.post("/outreach")
async def generate_outreach(req: OutreachRequest):
if not req.company.get("name"):
raise HTTPException(status_code=400, detail="请填写公司名称")
if not req.product.get("name"):
raise HTTPException(status_code=400, detail="请填写产品名称")
svc = DiscoveryService()
try:
result = await svc.outreach(req.company, req.product)
return {"success": True, "data": result}
except Exception as e:
raise HTTPException(status_code=500, detail=f"生成失败: {str(e)}")
+39
View File
@@ -0,0 +1,39 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel
from typing import Optional
from app.database import get_db
from app.api.v1.deps import get_current_user_id
from app.services.invoice import InvoiceService
router = APIRouter()
class InvoiceApplyRequest(BaseModel):
invoice_type: str
title: str
tax_id: Optional[str] = None
amount: float
@router.post("/apply")
async def apply_invoice(
data: InvoiceApplyRequest,
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
service = InvoiceService(db)
result = await service.apply(user_id, data.model_dump())
if "error" in result:
raise HTTPException(status_code=400, detail=result["error"])
return {"success": True, "data": result}
@router.get("/list")
async def list_invoices(
user_id: str = Depends(get_current_user_id),
db: AsyncSession = Depends(get_db),
):
service = InvoiceService(db)
items = await service.list_user(user_id)
return {"success": True, "data": items}