fix(report): 错误 hint 去除「任务中心」指向 + PDF 元数据 AI 生成标识
CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s

- errors.py:IDEMPOTENCY_CONFLICT / TASK_STATE_INVALID 的 hint 改为「首页报告」口径(App 无任务中心页,经 hint 透出会误导)
- report.html.j2:head 增加 author/description/generator meta(WeasyPrint 落入 PDF 属性,AI 生成内容标识 CR-05)
- test_report_export.py:+2 单测(模板 meta 渲染 + PDF 产物 Info 字典解压断言,不新增依赖)

多 agent 评审 R3/R4 产物;单测 576 passed
This commit is contained in:
xsl
2026-08-30 23:17:00 +08:00
parent cd0ddb131b
commit 00ef56b08b
3 changed files with 65 additions and 2 deletions
+58
View File
@@ -9,6 +9,8 @@
from __future__ import annotations
import re
import zlib
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from datetime import UTC, datetime
@@ -24,6 +26,7 @@ from apps.api.main import create_app
from apps.api.routers.reports import report_export_service_dep
from packages.report.dimensions import DIMENSIONS
from packages.report.export_service import ExportFile, ExportSubmission
from packages.report.pdf import renderer as pdf_renderer
from packages.report.pdf.renderer import (
ReportPdfMeta,
_reject_all_urls,
@@ -115,6 +118,61 @@ def test_url_fetcher_rejects_everything_including_file_scheme() -> None:
_reject_all_urls(url)
# --- PDF 元数据 AI 声明(CR-05 隐式标识,R4 N2'-①) -----------------------
def _render_template_html() -> str:
"""用渲染器同款 Jinja 环境+上下文形状渲染 HTML(与 render_report_pdf 内部一致)。"""
template = pdf_renderer._env.get_template("report.html.j2")
return template.render(
meta=META,
date_text=META.generated_at.strftime("%Y年%m月%d日"),
summary="",
sections=[],
dimensions=[],
core_dimensions=[],
ok_count=0,
)
def _decompressed_streams(pdf: bytes) -> list[bytes]:
"""解出 PDF 里全部 FlateDecode 流——WeasyPrint 把 Info 字典放进压缩对象流,
裸字节 grep 不到 /Author 等标记(zlib 为标准库,不新增依赖)。"""
out: list[bytes] = []
for m in re.finditer(rb"stream\r?\n", pdf):
start = m.end()
end = pdf.find(b"endstream", start)
if end < 0:
continue
try:
out.append(zlib.decompress(pdf[start:end]))
except zlib.error:
continue
return out
def test_report_html_declares_ai_generation_meta() -> None:
html = _render_template_html()
assert '<meta name="author" content="美搭(AI 生成内容)">' in html
assert 'name="description"' in html
# description 携带报告号/版本/日期(ReportPdfMeta 现成数据,不新增字段)
assert "报告 101 v1" in html
assert "生成于 2026年08月29日" in html
def test_rendered_pdf_carries_ai_metadata() -> None:
# WeasyPrint 把 meta author/description/generator 映射进 PDF 信息字典
# (Author/Subject/Creator);值编码为带 BOM 的 UTF-16BE 十六进制文本串。
pdf = render_report_pdf(_sample_result(), meta=META)
joined = b"\n".join(_decompressed_streams(pdf))
for marker in (b"/Author", b"/Subject", b"/Creator"):
assert marker in joined, f"PDF 信息字典缺 {marker!r}"
author_hex = "feff" + "美搭(AI 生成内容)".encode("utf-16-be").hex()
assert author_hex.encode("ascii") in joined
desc_hex = "feff" + "本报告由人工智能生成,仅供参考。报告 101 v1".encode("utf-16-be").hex()
assert desc_hex.encode("ascii") in joined
# --- 路由接线(假服务,不连库) --------------------------------------------