feat(expense): add persistent zero-entry receipt association
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
import json
|
||||
import mimetypes
|
||||
import re
|
||||
import shutil
|
||||
@@ -20,8 +20,8 @@ from app.schemas.receipt_folder import (
|
||||
ReceiptFolderItemRead,
|
||||
ReceiptFolderUpdate,
|
||||
)
|
||||
from app.services.document_preview import DocumentPreviewAssets
|
||||
from app.services.document_intelligence import build_document_insight
|
||||
from app.services.document_preview import DocumentPreviewAssets
|
||||
from app.services.ocr import SUPPORTED_SUFFIXES
|
||||
|
||||
RECEIPT_DATE_PATTERN = re.compile(
|
||||
@@ -116,9 +116,16 @@ class ReceiptFolderStorageMixin:
|
||||
|
||||
@staticmethod
|
||||
def _owner_key(current_user: CurrentUserContext) -> str:
|
||||
raw = str(current_user.username or current_user.name or "anonymous").strip().lower()
|
||||
normalized = re.sub(r"[^\w.\-\u4e00-\u9fff]+", "_", raw).strip("._")
|
||||
return normalized or "anonymous"
|
||||
raw_owner = str(current_user.username or current_user.name or "anonymous").strip().lower()
|
||||
owner = re.sub(r"[^\w.\-\u4e00-\u9fff]+", "_", raw_owner).strip("._") or "anonymous"
|
||||
raw_tenant = str(getattr(current_user, "tenant_id", "default") or "default").strip().lower()
|
||||
tenant = re.sub(r"[^\w.\-]+", "_", raw_tenant).strip("._") or "default"
|
||||
# 默认租户继续读取历史目录;其他租户必须使用独立命名空间。
|
||||
if raw_tenant == "default":
|
||||
return owner
|
||||
tenant_digest = hashlib.sha256(raw_tenant.encode("utf-8")).hexdigest()[:12]
|
||||
owner_digest = hashlib.sha256(raw_owner.encode("utf-8")).hexdigest()[:12]
|
||||
return f"{tenant[:48]}-{tenant_digest}__{owner[:64]}-{owner_digest}"
|
||||
|
||||
@staticmethod
|
||||
def _should_persist_source(filename: str, content: bytes) -> bool:
|
||||
@@ -1180,6 +1187,8 @@ class ReceiptFolderService(ReceiptFolderStorageMixin, ReceiptFolderItemMixin, Re
|
||||
meta = {
|
||||
"id": receipt_id,
|
||||
"owner_key": owner_key,
|
||||
"tenant_id": str(getattr(current_user, "tenant_id", "default") or "default").strip()
|
||||
or "default",
|
||||
"file_name": normalized_name,
|
||||
"source_file_name": normalized_name,
|
||||
"media_type": resolved_media_type,
|
||||
@@ -1258,6 +1267,23 @@ class ReceiptFolderService(ReceiptFolderStorageMixin, ReceiptFolderItemMixin, Re
|
||||
self._write_meta(receipt_dir, meta)
|
||||
return self._build_item(meta)
|
||||
|
||||
def restore_receipt_meta(
|
||||
self,
|
||||
*,
|
||||
receipt_id: str,
|
||||
current_user: CurrentUserContext,
|
||||
meta: dict[str, Any],
|
||||
) -> None:
|
||||
"""仅供跨数据库/文件事务补偿使用,恢复调用前的票据元数据。"""
|
||||
receipt_dir = self._receipt_dir(self._owner_key(current_user), receipt_id)
|
||||
restored = json.loads(json.dumps(meta, ensure_ascii=False))
|
||||
restored["id"] = str(receipt_id or "").strip()
|
||||
restored["owner_key"] = self._owner_key(current_user)
|
||||
restored["tenant_id"] = str(
|
||||
getattr(current_user, "tenant_id", "default") or "default"
|
||||
).strip() or "default"
|
||||
self._write_meta(receipt_dir, restored)
|
||||
|
||||
def list_receipts(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user