Add admin-frontend and user-frontend standalone projects, certification/invoice/discovery features, fix auth header and theme consistency
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}
|
||||
@@ -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)}")
|
||||
@@ -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}
|
||||
Reference in New Issue
Block a user