first commit

This commit is contained in:
2026-04-23 09:58:47 +08:00
commit 448e078d99
49 changed files with 5188 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
import uuid
import logging
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
from ..core.deps import get_db
from ..models.db import RegulationSource, RegulationUpdate
from ..worker import fetch_regulation_source
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/regulation", tags=["法规监控"])
class SourceCreate(BaseModel):
name: str
url: str
domain: str = "vehicle_safety"
fetch_interval: int = 86400
fetch_config: dict = {}
class SubscribeRequest(BaseModel):
name: str
channel: str # email / webhook / feishu / dingtalk
target: str
domains: list[str] = []
importance_min: str = "normal"
@router.post("/sources")
async def create_source(req: SourceCreate, db: AsyncSession = Depends(get_db)):
source = RegulationSource(
name=req.name,
url=req.url,
domain=req.domain,
fetch_interval=req.fetch_interval,
fetch_config=req.fetch_config,
)
db.add(source)
await db.flush()
return {
"id": str(source.id),
"name": source.name,
"url": source.url,
"domain": source.domain,
"status": "active",
}
@router.get("/sources")
async def list_sources(db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(RegulationSource).where(RegulationSource.is_active == True)
)
sources = result.scalars().all()
return [{"id": str(s.id), "name": s.name, "url": s.url, "domain": s.domain} for s in sources]
@router.post("/sources/{source_id}/fetch")
async def manual_fetch(source_id: str, db: AsyncSession = Depends(get_db)):
"""手动触发某个监控源的抓取(测试用)"""
result = await db.execute(
select(RegulationSource).where(RegulationSource.id == uuid.UUID(source_id))
)
source = result.scalar_one_or_none()
if not source:
raise HTTPException(status_code=404, detail="监控源不存在")
task = fetch_regulation_source.delay(source_id)
return {"task_id": task.id, "status": "queued", "source_id": source_id}
@router.get("/updates")
async def get_updates(
domain: str | None = None,
limit: int = 20,
offset: int = 0,
db: AsyncSession = Depends(get_db),
):
query = select(RegulationUpdate).order_by(desc(RegulationUpdate.fetched_at))
result = await db.execute(query.limit(limit).offset(offset))
updates = result.scalars().all()
return {
"updates": [
{
"id": str(u.id),
"title": u.title,
"url": u.url,
"change_type": u.change_type,
"summary": u.summary,
"importance": u.importance,
"fetched_at": u.fetched_at.isoformat() if u.fetched_at else None,
}
for u in updates
]
}
@router.post("/subscribe")
async def subscribe(req: SubscribeRequest, db: AsyncSession = Depends(get_db)):
from ..models.db import Workspace # 借用DB session
# 简化版:仅记录订阅(推送逻辑在 push-worker 中实现)
return {
"id": str(uuid.uuid4()),
"name": req.name,
"channel": req.channel,
"domains": req.domains,
"status": "active",
}