Files
myquant/djapi/api/report/views.py
T
simon 6ec198687c feat(djapi): 新增日报查询 API(news/reports + news/events)及文档
- api/report/ 包:query(连库+SQL)/ views(2 视图)/ serializers(OpenAPI)/ tests(17 单测)
- urls.py 注册 news/reports/、news/events/;settings.py SPECTACULAR 加「日报」tag
- .env.example 补 NEWS_DB_* 占位配置;README/continuation.md 更新
- docs/news_report_api.md 使用手册;CLAUDE*.md 修正 CLI 路径为 finance/ 前缀
2026-08-05 20:51:42 +08:00

148 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
日报查询 API 视图。
- GET /api/news/reports/ 日报查询(默认最近 24 小时;传 id 返回单份详情含事件)
- GET /api/news/events/ 跨日报重要事件聚合(最近 N 天 importance >= 阈值)
"""
from datetime import datetime, timedelta
from django.utils import timezone
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework.decorators import api_view
from rest_framework.response import Response
from . import query as report_query
from .serializers import (
ImportantEventSerializer,
ReportDetailSerializer,
ReportListSerializer,
)
_REPORT_TYPES = ("finance", "intl")
_SECTIONS = ("xwlb", "news", "cninfo", "intl")
def _parse_date_param(request, name):
"""解析 YYYY-MM-DD 参数,非法格式抛 ValueError。"""
raw = request.GET.get(name)
if not raw:
return None
try:
return datetime.strptime(raw, "%Y-%m-%d").date()
except ValueError:
raise ValueError(f"{name} 格式错误,应为 YYYY-MM-DD")
@extend_schema(
parameters=[
OpenApiParameter(name='report_type', type=str, required=False,
description='日报类型:finance | intl(默认两者)'),
OpenApiParameter(name='start_date', type=str, required=False,
description='起始日期 YYYY-MM-DD(默认:当前时间往前 24 小时)'),
OpenApiParameter(name='end_date', type=str, required=False,
description='结束日期 YYYY-MM-DD(默认今天)'),
OpenApiParameter(name='id', type=int, required=False,
description='日报 id,指定时返回单份详情(含事件,按板块/序号排序)'),
],
responses={200: ReportDetailSerializer},
description='AI 财经日报查询:默认返回最近 24 小时的日报列表(每天每类型取最新一份);'
'传 id 返回单份详情含事件',
tags=['日报'],
)
@api_view(['GET'])
def news_reports(request):
try:
report_id = request.GET.get('id')
if report_id is not None:
report_id = int(report_id)
if report_id <= 0:
raise ValueError("id 必须为正整数")
report_type = request.GET.get('report_type')
if report_type and report_type not in _REPORT_TYPES:
raise ValueError("report_type 仅支持 finance / intl")
start_date = _parse_date_param(request, 'start_date')
end_date = _parse_date_param(request, 'end_date')
except ValueError as e:
return Response({'error': str(e)}, status=400)
now = timezone.now()
start_date = start_date or (now - timedelta(hours=24)).date()
end_date = end_date or now.date()
try:
data = report_query.fetch_reports(
report_type=report_type,
start_date=start_date,
end_date=end_date,
report_id=report_id,
)
except Exception as e:
return Response({'error': f'查询失败: {e}'}, status=500)
if report_id is not None:
if data is None:
return Response({'error': f'日报 id={report_id} 不存在'}, status=404)
return Response(data)
return Response(data)
def _get_int_param(request, name, default, lo, hi):
"""解析整数参数并校验范围,非法抛 ValueError。"""
raw = request.GET.get(name)
if raw is None:
return default
try:
value = int(raw)
except ValueError:
raise ValueError(f"{name} 必须为整数")
if not (lo <= value <= hi):
raise ValueError(f"{name} 需在 {lo}~{hi} 之间")
return value
@extend_schema(
parameters=[
OpenApiParameter(name='days', type=int, required=False, default=7,
description='最近 N 天(1~365'),
OpenApiParameter(name='importance', type=int, required=False, default=4,
description='最低重要度(1~5'),
OpenApiParameter(name='report_type', type=str, required=False,
description='日报类型:finance | intl(默认两者)'),
OpenApiParameter(name='section', type=str, required=False,
description='板块:xwlb | news | cninfo | intl(默认全部)'),
OpenApiParameter(name='limit', type=int, required=False, default=100,
description='返回条数上限(1~500'),
],
responses={200: ImportantEventSerializer(many=True)},
description='跨日报重要事件聚合:最近 N 天 importance >= 阈值的事件,'
'按重要度、日期降序',
tags=['日报'],
)
@api_view(['GET'])
def news_events(request):
try:
days = _get_int_param(request, 'days', 7, 1, 365)
importance = _get_int_param(request, 'importance', 4, 1, 5)
limit = _get_int_param(request, 'limit', 100, 1, 500)
report_type = request.GET.get('report_type')
if report_type and report_type not in _REPORT_TYPES:
raise ValueError("report_type 仅支持 finance / intl")
section = request.GET.get('section')
if section and section not in _SECTIONS:
raise ValueError("section 仅支持 xwlb / news / cninfo / intl")
except ValueError as e:
return Response({'error': str(e)}, status=400)
try:
data = report_query.fetch_important_events(
days=days,
importance=importance,
report_type=report_type,
section=section,
limit=limit,
)
except Exception as e:
return Response({'error': f'查询失败: {e}'}, status=500)
return Response(data)