feat(report): 报告 PDF 导出管线(Phase A,WeasyPrint 服务端渲染)
- POST /reports/{id}/export:request_key 幂等 + hd_export 日配额三段式(reserve/commit/refund)+ tier2 熔断场景闸(BudgetGuardDep),仅 succeeded 报告可导
- GET /reports/{id}/export/download:归属校验 + 路径收敛(rendered_asset_key 必须落在 report_export_dir 内),FileResponse 携带 UTF-8 文件名
- celery 任务 meida.report.export(cpu 队列):渲染→落盘 var/exports→回写 rendered_asset_key;业务性拒绝(报告状态变化)结案退款不重试,基础设施异常重试一次后兜底结案退款
- packages/report/pdf:Jinja2 模板 + WeasyPrint 渲染器(url_fetcher 全拒外部资源防 SSRF;维度中文名按 DIMENSION_BY_KEY 回填;色板维度渲染色卡)
- 单测 10 例(资格/幂等/并发同键/下载校验/渲染);scripts/seed_pdf_e2e.sql 供本地 E2E 造数
This commit is contained in:
@@ -191,6 +191,21 @@ def storage_service_dep(settings: SettingsDep) -> StorageService:
|
||||
return StorageService(settings)
|
||||
|
||||
|
||||
def budget_guard_dep(settings: SettingsDep) -> DailyBudgetGuard:
|
||||
"""全局日预算的场景闸(架构 §13.3):hd_export 等可降级场景在请求入口先过它。
|
||||
|
||||
每次请求新建实例是刻意的:Guard 本身无状态(水位读 Redis),
|
||||
与 build_llm_gateway 里给网关用的那只互不影响。
|
||||
"""
|
||||
return DailyBudgetGuard(
|
||||
RedisCostCounter(get_redis()),
|
||||
daily_budget_cents=Decimal(settings.llm_daily_budget_cents),
|
||||
enabled=settings.llm_daily_budget_enabled,
|
||||
tz_offset_hours=settings.llm_budget_timezone_offset_hours,
|
||||
tiers=list(settings.llm_budget_tiers),
|
||||
)
|
||||
|
||||
|
||||
def agent_service_dep(
|
||||
session: SessionDep,
|
||||
gateway: GatewayDep,
|
||||
@@ -204,4 +219,5 @@ BillingServiceDep = Annotated[BillingService, Depends(billing_service_dep)]
|
||||
TaskServiceDep = Annotated[TaskService, Depends(task_service_dep)]
|
||||
AnalyticsServiceDep = Annotated[AnalyticsService, Depends(analytics_service_dep)]
|
||||
StorageServiceDep = Annotated[StorageService, Depends(storage_service_dep)]
|
||||
BudgetGuardDep = Annotated[DailyBudgetGuard, Depends(budget_guard_dep)]
|
||||
AgentServiceDep = Annotated[AgentService, Depends(agent_service_dep)]
|
||||
|
||||
Reference in New Issue
Block a user