feat(server): 系统缓存清理接口与 OCR 文本层兜底增强
- 新增 system_cache 模块与 POST /settings/cache/clear,管理员可一键清理 OCR 结果/运行时配置/模型失败冷却/知识库索引/地点语义等进程内缓存 - 各服务暴露 clear_*_cache 方法(ocr/runtime_settings/runtime_chat/knowledge/application_location_semantic),SettingsCacheClearRead 汇总清理项 - OCR 转图片失败时尝试用 PDF 文本层兜底构建识别文档(有效字符≥8),并写结果缓存;OcrService 暴露 clear_result_cache - receipt_folder 车票过滤补充身份证号关键词,附件文档/操作/展示模块同步适配 - 新增 system_cache_endpoints 测试,更新 openapi_schema/ocr/receipt_folder/attachment_association_jobs 测试
This commit is contained in:
@@ -308,6 +308,7 @@ def test_ocr_service_rejects_pdf_ocr_when_rendered_image_fonts_are_broken(
|
||||
monkeypatch.setattr(OcrService, "_convert_pdf_to_images", fake_convert_pdf_to_images)
|
||||
monkeypatch.setattr(OcrService, "_invoke_worker", fake_invoke_worker)
|
||||
get_settings.cache_clear()
|
||||
OcrService._result_cache.clear()
|
||||
try:
|
||||
result = OcrService().recognize_files(
|
||||
[
|
||||
@@ -315,6 +316,7 @@ def test_ocr_service_rejects_pdf_ocr_when_rendered_image_fonts_are_broken(
|
||||
]
|
||||
)
|
||||
finally:
|
||||
OcrService._result_cache.clear()
|
||||
get_settings.cache_clear()
|
||||
|
||||
failed = result.documents[0]
|
||||
@@ -324,6 +326,63 @@ def test_ocr_service_rejects_pdf_ocr_when_rendered_image_fonts_are_broken(
|
||||
assert failed.warnings == ["PDF 转图片失败:检测到中文字体映射缺失,未生成可 OCR 的图片。"]
|
||||
|
||||
|
||||
def test_ocr_service_uses_pdf_text_layer_when_rendering_fails(
|
||||
monkeypatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
def fake_convert_pdf_to_images(self, *, pdf_path: Path, output_dir: Path) -> tuple[list[Path], bool]:
|
||||
raise RuntimeError("PDF 转图片失败:Missing language pack for Adobe-GB1")
|
||||
|
||||
def fake_invoke_worker(
|
||||
self,
|
||||
*,
|
||||
python_bin: str,
|
||||
worker_path: str,
|
||||
input_paths: list[Path],
|
||||
) -> dict:
|
||||
raise AssertionError("PDF 转图失败但文本层可用时,不应调用 OCR worker。")
|
||||
|
||||
monkeypatch.setenv("STORAGE_ROOT_DIR", str(tmp_path / "storage"))
|
||||
monkeypatch.setattr(OcrService, "_resolve_python_bin", lambda self: "python")
|
||||
monkeypatch.setattr(OcrService, "_resolve_worker_path", lambda self: "worker.py")
|
||||
monkeypatch.setattr(OcrService, "_convert_pdf_to_images", fake_convert_pdf_to_images)
|
||||
monkeypatch.setattr(OcrService, "_invoke_worker", fake_invoke_worker)
|
||||
monkeypatch.setattr(
|
||||
OcrService,
|
||||
"_extract_pdf_text_layer",
|
||||
lambda self, pdf_path: (
|
||||
"G458\n"
|
||||
"Wuhan Shanghaihongqiao\n"
|
||||
"2026 02 20 07:55\n"
|
||||
"票价: 354.00\n"
|
||||
"12306 95306"
|
||||
),
|
||||
)
|
||||
get_settings.cache_clear()
|
||||
OcrService._result_cache.clear()
|
||||
try:
|
||||
result = OcrService().recognize_files(
|
||||
[
|
||||
("2月20_武汉-上海.pdf", b"%PDF-1.7 text-layer-fallback", "application/pdf"),
|
||||
]
|
||||
)
|
||||
finally:
|
||||
OcrService._result_cache.clear()
|
||||
get_settings.cache_clear()
|
||||
|
||||
recovered = result.documents[0]
|
||||
assert result.success_count == 1
|
||||
assert recovered.document_type == "train_ticket"
|
||||
assert recovered.document_type_label == "火车/高铁票"
|
||||
assert recovered.preview_kind == ""
|
||||
assert recovered.preview_data_url == ""
|
||||
assert any(field.label == "金额" and field.value == "354元" for field in recovered.document_fields)
|
||||
assert any(field.label == "车次/航班" and field.value == "G458" for field in recovered.document_fields)
|
||||
assert any(field.label == "行程" and field.value == "武汉-上海" for field in recovered.document_fields)
|
||||
assert "PDF 转图片失败" in recovered.warnings[0]
|
||||
assert "已使用 PDF 文本层" in recovered.warnings[1]
|
||||
|
||||
|
||||
def test_ocr_pdf_conversion_tries_next_renderer_when_poppler_font_mapping_fails(
|
||||
monkeypatch,
|
||||
tmp_path: Path,
|
||||
@@ -339,6 +398,7 @@ def test_ocr_pdf_conversion_tries_next_renderer_when_poppler_font_mapping_fails(
|
||||
text: bool,
|
||||
timeout: int,
|
||||
check: bool,
|
||||
env: dict[str, str] | None = None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
calls.append(Path(command[0]).name)
|
||||
if Path(command[0]).name == "pdftoppm":
|
||||
@@ -437,6 +497,7 @@ def test_ocr_service_invokes_worker_even_when_pdf_text_layer_is_usable(
|
||||
),
|
||||
)
|
||||
get_settings.cache_clear()
|
||||
OcrService._result_cache.clear()
|
||||
try:
|
||||
result = OcrService().recognize_files(
|
||||
[
|
||||
@@ -444,6 +505,7 @@ def test_ocr_service_invokes_worker_even_when_pdf_text_layer_is_usable(
|
||||
]
|
||||
)
|
||||
finally:
|
||||
OcrService._result_cache.clear()
|
||||
get_settings.cache_clear()
|
||||
|
||||
recognized = result.documents[0]
|
||||
|
||||
Reference in New Issue
Block a user