2026-04-08 20:04:40 +08:00
|
|
|
from fastapi import APIRouter, File, Form, Query, UploadFile
|
2026-03-20 21:47:30 +08:00
|
|
|
|
2026-04-08 20:04:40 +08:00
|
|
|
from app.api.analysis_schemas import AnalysisChartResponse, AnalysisReportResponse
|
|
|
|
|
from app.api.capital_image_schemas import (
|
|
|
|
|
CapitalImageListResponse,
|
|
|
|
|
CapitalImageRecordDetail,
|
|
|
|
|
CapitalImageUploadResponse,
|
|
|
|
|
)
|
|
|
|
|
from app.api.main_capital_flow_schemas import (
|
|
|
|
|
MainCapitalFlowCreateRequest,
|
|
|
|
|
MainCapitalFlowCreateResponse,
|
|
|
|
|
MainCapitalFlowDeleteResponse,
|
|
|
|
|
MainCapitalFlowListResponse,
|
|
|
|
|
MainCapitalFlowRecognizeResponse,
|
|
|
|
|
MainCapitalFlowRecordDetail,
|
|
|
|
|
)
|
2026-03-20 21:47:30 +08:00
|
|
|
from app.api.schemas import (
|
|
|
|
|
AShareIndexFlowResponse,
|
|
|
|
|
AShareSectorFlowResponse,
|
2026-04-08 20:04:40 +08:00
|
|
|
EtfRealtimeResponse,
|
2026-03-20 21:47:30 +08:00
|
|
|
HealthResponse,
|
|
|
|
|
HistoryResponse,
|
|
|
|
|
MetaResponse,
|
|
|
|
|
OverviewResponse,
|
|
|
|
|
PushRecord,
|
|
|
|
|
PushRecordsResponse,
|
|
|
|
|
RulesResponse,
|
|
|
|
|
SourceDiagnosticsResponse,
|
|
|
|
|
)
|
2026-04-08 20:04:40 +08:00
|
|
|
from app.services.analysis_service import analysis_service
|
2026-03-20 21:47:30 +08:00
|
|
|
from app.services.ashare_flow_service import ashare_flow_service
|
|
|
|
|
from app.services.alert_service import alert_service
|
2026-04-08 20:04:40 +08:00
|
|
|
from app.services.capital_image_service import capital_image_service
|
|
|
|
|
from app.services.etf_monitor_service import etf_monitor_service
|
|
|
|
|
from app.services.main_capital_flow_service import main_capital_flow_service
|
2026-03-20 21:47:30 +08:00
|
|
|
from app.services.monitoring_service import monitoring_service
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/health", response_model=HealthResponse)
|
|
|
|
|
def health() -> HealthResponse:
|
|
|
|
|
return HealthResponse(status="ok")
|
|
|
|
|
|
|
|
|
|
|
2026-04-08 20:04:40 +08:00
|
|
|
@router.get("/capital-images", response_model=CapitalImageListResponse)
|
|
|
|
|
def capital_image_list(
|
|
|
|
|
trade_date: str | None = Query(default=None),
|
|
|
|
|
subject: str | None = Query(default=None),
|
|
|
|
|
) -> CapitalImageListResponse:
|
|
|
|
|
return CapitalImageListResponse(**capital_image_service.list_records(trade_date, subject))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/capital-images/{record_id}", response_model=CapitalImageRecordDetail)
|
|
|
|
|
def capital_image_detail(record_id: str) -> CapitalImageRecordDetail:
|
|
|
|
|
return CapitalImageRecordDetail(**capital_image_service.get_record(record_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/capital-images/upload", response_model=CapitalImageUploadResponse)
|
|
|
|
|
async def capital_image_upload(
|
|
|
|
|
image: UploadFile = File(...),
|
|
|
|
|
trade_date: str | None = Form(default=None),
|
|
|
|
|
subject: str | None = Form(default=None),
|
|
|
|
|
) -> CapitalImageUploadResponse:
|
|
|
|
|
return CapitalImageUploadResponse(
|
|
|
|
|
**await capital_image_service.create_record(image, trade_date=trade_date, subject=subject)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/main-capital-flows", response_model=MainCapitalFlowListResponse)
|
|
|
|
|
def main_capital_flow_list() -> MainCapitalFlowListResponse:
|
|
|
|
|
return MainCapitalFlowListResponse(**main_capital_flow_service.list_records())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/main-capital-flows/{record_id}", response_model=MainCapitalFlowRecordDetail)
|
|
|
|
|
def main_capital_flow_detail(record_id: str) -> MainCapitalFlowRecordDetail:
|
|
|
|
|
return MainCapitalFlowRecordDetail(**main_capital_flow_service.get_record(record_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/main-capital-flows/recognize", response_model=MainCapitalFlowRecognizeResponse)
|
|
|
|
|
async def main_capital_flow_recognize(
|
|
|
|
|
image: UploadFile = File(...),
|
|
|
|
|
trade_date: str | None = Form(default=None),
|
|
|
|
|
subject: str | None = Form(default=None),
|
|
|
|
|
) -> MainCapitalFlowRecognizeResponse:
|
|
|
|
|
return MainCapitalFlowRecognizeResponse(
|
|
|
|
|
**await main_capital_flow_service.recognize_image(image, trade_date=trade_date, subject=subject)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/main-capital-flows", response_model=MainCapitalFlowCreateResponse)
|
|
|
|
|
def main_capital_flow_create(payload: MainCapitalFlowCreateRequest) -> MainCapitalFlowCreateResponse:
|
|
|
|
|
return MainCapitalFlowCreateResponse(**main_capital_flow_service.create_record(payload.model_dump()))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/main-capital-flows/{record_id}", response_model=MainCapitalFlowDeleteResponse)
|
|
|
|
|
def main_capital_flow_delete(record_id: str) -> MainCapitalFlowDeleteResponse:
|
|
|
|
|
return MainCapitalFlowDeleteResponse(**main_capital_flow_service.delete_record(record_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/analysis/report", response_model=AnalysisReportResponse)
|
|
|
|
|
def analysis_report(q: str = Query(..., min_length=2)) -> AnalysisReportResponse:
|
|
|
|
|
return analysis_service.build_report(q)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/analysis/chart", response_model=AnalysisChartResponse)
|
|
|
|
|
def analysis_chart(
|
|
|
|
|
q: str = Query(..., min_length=2),
|
|
|
|
|
cycle: str = Query(default="day"),
|
|
|
|
|
) -> AnalysisChartResponse:
|
|
|
|
|
return analysis_service.build_chart(q, cycle)
|
|
|
|
|
|
|
|
|
|
|
2026-03-20 21:47:30 +08:00
|
|
|
@router.get("/meta", response_model=MetaResponse)
|
|
|
|
|
def meta() -> MetaResponse:
|
|
|
|
|
return monitoring_service.get_meta()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/overview", response_model=OverviewResponse)
|
|
|
|
|
def overview() -> OverviewResponse:
|
|
|
|
|
return monitoring_service.get_overview()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/history", response_model=HistoryResponse)
|
|
|
|
|
def history() -> HistoryResponse:
|
|
|
|
|
return monitoring_service.get_history()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/push-records", response_model=PushRecordsResponse)
|
|
|
|
|
def push_records() -> PushRecordsResponse:
|
|
|
|
|
return monitoring_service.get_push_records()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/push-records/test", response_model=PushRecord)
|
|
|
|
|
def send_test_push_record() -> PushRecord:
|
|
|
|
|
return PushRecord(**alert_service.send_test_alert())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/rules", response_model=RulesResponse)
|
|
|
|
|
def rules() -> RulesResponse:
|
|
|
|
|
return monitoring_service.get_rules()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/source-diagnostics", response_model=SourceDiagnosticsResponse)
|
|
|
|
|
def source_diagnostics() -> SourceDiagnosticsResponse:
|
|
|
|
|
return monitoring_service.get_source_diagnostics()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/ashare/index-flows/realtime", response_model=AShareIndexFlowResponse)
|
|
|
|
|
def ashare_index_realtime() -> AShareIndexFlowResponse:
|
|
|
|
|
return AShareIndexFlowResponse(**ashare_flow_service.get_index_realtime())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/ashare/index-flows/daily", response_model=AShareIndexFlowResponse)
|
|
|
|
|
def ashare_index_daily(trade_date: str | None = Query(default=None)) -> AShareIndexFlowResponse:
|
|
|
|
|
return AShareIndexFlowResponse(**ashare_flow_service.get_index_daily(trade_date))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/ashare/sector-flows/realtime", response_model=AShareSectorFlowResponse)
|
|
|
|
|
def ashare_sector_realtime() -> AShareSectorFlowResponse:
|
|
|
|
|
return AShareSectorFlowResponse(**ashare_flow_service.get_sector_realtime())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/ashare/sector-flows/daily", response_model=AShareSectorFlowResponse)
|
|
|
|
|
def ashare_sector_daily(trade_date: str | None = Query(default=None)) -> AShareSectorFlowResponse:
|
|
|
|
|
return AShareSectorFlowResponse(**ashare_flow_service.get_sector_daily(trade_date))
|
2026-04-08 20:04:40 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/etf/broad/realtime", response_model=EtfRealtimeResponse)
|
|
|
|
|
def etf_broad_realtime() -> EtfRealtimeResponse:
|
|
|
|
|
return EtfRealtimeResponse(**etf_monitor_service.get_group_realtime("broad"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/etf/sector/realtime", response_model=EtfRealtimeResponse)
|
|
|
|
|
def etf_sector_realtime() -> EtfRealtimeResponse:
|
|
|
|
|
return EtfRealtimeResponse(**etf_monitor_service.get_group_realtime("sector"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/etf/broad/daily", response_model=EtfRealtimeResponse)
|
|
|
|
|
def etf_broad_daily(trade_date: str | None = Query(default=None)) -> EtfRealtimeResponse:
|
|
|
|
|
return EtfRealtimeResponse(**etf_monitor_service.get_group_daily("broad", trade_date))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/etf/sector/daily", response_model=EtfRealtimeResponse)
|
|
|
|
|
def etf_sector_daily(trade_date: str | None = Query(default=None)) -> EtfRealtimeResponse:
|
|
|
|
|
return EtfRealtimeResponse(**etf_monitor_service.get_group_daily("sector", trade_date))
|