feat/export-service
main
上一版导出服务 PR(#17)被审阅关闭,结论「请求修改」:1 个 P1 + 5 个 P2。本 PR 逐项修复、补回归测试,并继续交付第二阶段「函数图像绘制与渲染支持」(契约 §12 / §10.4)的静态渲染基础。
javascript:
data:
src
attrs.url
alt
block_html
html_block
asyncio.to_thread
function-plot
function_plot
functionplot
app/plot
ast
eval
exec
FunctionPlot
<text>
HtmlExporter
<pre>
domain/range/xlabel/ylabel/grid
y = <expr>
+ - * / ^
pi/e
tests/test_export.py
tests/test_plot.py
490 passed
仅 backend 下导出服务与函数图像相关文件;函数图像的交互式预览仍由前端 JS Renderer 完成,不在本 PR。
backend
实现 Export Service 完整生命周期:mistune AST → Document AST → HtmlExporter 渲染完整 HTML5,异步任务注册表 + 取消 + 24h 产物过期。新增 5 个 /api/exports 端点与 15 项测试;pdf/docx 与函数图像静态渲染留待后续 PR。
- P1 链接/图片 URL 协议白名单校验,危险协议降级为纯文本 + warning - P2 图片 AST 字段映射(src=attrs.url,alt 取 children 文本) - P2 原始 HTML 块转义保留,正文不丢失 + warning - P2 过期/淘汰/重启清理导出产物文件 - P2 解析与渲染移入 asyncio.to_thread,运行中取消生效 - P2 function-plot 围栏别名补全 - 回归测试覆盖全部修复 Co-Authored-By: Claude Code <noreply@anthropic.com>
- 新增 app/plot 包:白名单表达式解析(ast 无 eval)+ FunctionPlot 模型 + 静态 SVG 渲染 - HtmlExporter 的 function_plot 节点解析并内嵌 SVG,解析失败回退占位并转诊断 - 新增 test_plot.py(13 个测试)覆盖表达式安全、指令解析、SVG 输出与导出链路集成 Co-Authored-By: Claude Code <noreply@anthropic.com>
审阅提交:f49d124
f49d124
本次发现 1 个 P1、2 个 P2,均已复现。
位置:backend/app/plot/render.py:46–52
backend/app/plot/render.py:46–52
复现配置:
domain: 10000000000000000, 10000000000000002 range: -1, 1 y=0
计算出的步长为 0.5,但受浮点精度限制,v + step == v,循环会持续追加相同刻度,消耗 CPU 和内存。渲染运行在线程中,取消导出无法中止该循环。
0.5
v + step == v
建议改为有数量上限的整数索引迭代,并校验步长是否能推动数值变化。
位置:backend/app/plot/render.py:70–75,以及 _polyline 中的同类逻辑。
backend/app/plot/render.py:70–75
_polyline
y=x^0.5
math.isfinite
TypeError
y=sin()
异常未在绘图层处理,最终导致整个导出任务返回 EXPORT_RENDER_FAILED。
EXPORT_RENDER_FAILED
建议校验函数参数数量,将非实数采样结果作为断点处理;无法渲染的图块应保留源码并附带诊断,不应阻断整篇导出。
位置:backend/app/plot/render.py:195–198
backend/app/plot/render.py:195–198
range: 1, 1 y=x
检测到无效范围后,再次调用 _compute_range,但原来的 plot.range 未清除,因此仍返回相同范围,随后触发 ZeroDivisionError,导致导出失败。
_compute_range
plot.range
ZeroDivisionError
建议清除无效范围后重新采样计算,并校验范围端点为有限数值。
现有后端测试 490 项全部通过,但未覆盖上述边界场景,请补充回归测试。
此外,当前分支与 main 在以下文件存在合并冲突:
backend/app/contracts.py
backend/app/main.py
请修复上述问题、解决冲突,并在同步后的分支上重新运行测试,再提交复审。
No dependencies set.
The note is not visible to the blocked user.
背景
上一版导出服务 PR(#17)被审阅关闭,结论「请求修改」:1 个 P1 + 5 个 P2。本 PR 逐项修复、补回归测试,并继续交付第二阶段「函数图像绘制与渲染支持」(契约 §12 / §10.4)的静态渲染基础。
审阅问题修复(1 P1 + 5 P2)
javascript:/data:等危险协议降级为纯文本并记 warning,杜绝注入。src取attrs.url、alt取 children 文本(原实现误读字段,导致 src 恒空、alt 丢失)。block_html映射为html_block,渲染时转义保留正文 + warning。asyncio.to_thread,避免阻塞事件循环;写文件前增加取消检查,运行中取消生效。function-plot围栏别名补全(function-plot/function_plot/functionplot)。函数图像静态渲染(function-plot → SVG)
app/plot包:白名单表达式解析(ast解析 + 逐节点白名单校验 + 递归解释器,全程无eval/exec)、FunctionPlot内部模型、静态 SVG 渲染(纯几何 +<text>,无 script/foreignObject/内联事件,可安全内嵌 HTML)。HtmlExporter的function_plot节点改为解析并内嵌 SVG;解析失败回退占位<pre>并转诊断 warning。domain/range/xlabel/ylabel/grid指令 +y = <expr>或裸表达式,支持+ - * / ^、隐式乘法、常量pi/e、白名单函数(sin/cos/tan/…/sqrt/abs)。测试
tests/test_export.py,15 → 23)。tests/test_plot.py):表达式安全拒绝项、指令解析、SVG 输出、HTML 集成。490 passed。范围
仅
backend下导出服务与函数图像相关文件;函数图像的交互式预览仍由前端 JS Renderer 完成,不在本 PR。Feat/export serviceto feat(export): 导出服务审阅修复 + 函数图像静态渲染审阅结论:请求修改,暂不建议合并
审阅提交:
f49d124本次发现 1 个 P1、2 个 P2,均已复现。
[P1] 浮点刻度生成可能陷入死循环
位置:
backend/app/plot/render.py:46–52复现配置:
计算出的步长为
0.5,但受浮点精度限制,v + step == v,循环会持续追加相同刻度,消耗 CPU 和内存。渲染运行在线程中,取消导出无法中止该循环。建议改为有数量上限的整数索引迭代,并校验步长是否能推动数值变化。
[P2] 表达式求值异常导致整篇笔记导出失败
位置:
backend/app/plot/render.py:70–75,以及_polyline中的同类逻辑。y=x^0.5:默认定义域包含负数,求值返回复数,math.isfinite抛出TypeError。y=sin():解析阶段未检查参数数量,求值时抛出TypeError。异常未在绘图层处理,最终导致整个导出任务返回
EXPORT_RENDER_FAILED。建议校验函数参数数量,将非实数采样结果作为断点处理;无法渲染的图块应保留源码并附带诊断,不应阻断整篇导出。
[P2] 无效纵轴范围的自动回退未生效
位置:
backend/app/plot/render.py:195–198复现配置:
检测到无效范围后,再次调用
_compute_range,但原来的plot.range未清除,因此仍返回相同范围,随后触发ZeroDivisionError,导致导出失败。建议清除无效范围后重新采样计算,并校验范围端点为有限数值。
验证与合并要求
现有后端测试 490 项全部通过,但未覆盖上述边界场景,请补充回归测试。
此外,当前分支与
main在以下文件存在合并冲突:backend/app/contracts.pybackend/app/main.py请修复上述问题、解决冲突,并在同步后的分支上重新运行测试,再提交复审。
Pull request closed