Files
houhuan e441ac82a8 Refactor processing logic and enhance error handling
- Cleaned up code in processing.py by removing inline semicolons and improving readability.
- Updated upsert_file_relation calls to ensure consistent handling of file relations.
- Enhanced query_file_relations in db_schema.py to support filtering by file existence.
- Improved API error handling in index.ts with user-friendly messages for 401 and 403 errors.
- Added online/offline status tracking in Layout.vue.
- Implemented debounced search functionality across multiple views to optimize performance.
- Introduced loading skeletons in Dashboard.vue for better user experience during data fetching.
- Enhanced file preview cleanup logic in Images.vue, Orders.vue, and Tables.vue to prevent memory leaks.
- Updated global styles to include new loading and notification animations.
2026-05-12 18:37:23 +08:00

209 lines
5.9 KiB
Python

"""Product memory CRUD endpoints."""
from typing import Optional, List, Dict
from pathlib import Path
from fastapi import APIRouter, HTTPException, Depends, Query
from pydantic import BaseModel
from ..auth.dependencies import get_current_user
router = APIRouter(prefix="/api/memory", tags=["memory"])
_project_root = Path(__file__).resolve().parent.parent.parent.parent
_db_path = str(_project_root / "data" / "product_cache.db")
_excel_source = str(_project_root / "templates" / "商品资料.xlsx")
class MemoryItem(BaseModel):
barcode: str
name: str
specification: Optional[str] = None
unit: Optional[str] = None
price: Optional[float] = None
avg_price: Optional[float] = None
min_price: Optional[float] = None
max_price: Optional[float] = None
price_count: int = 0
confidence: int = 0
source: str = "ocr"
last_used: Optional[str] = None
use_count: int = 0
class MemoryCreate(BaseModel):
barcode: str
name: Optional[str] = ""
specification: Optional[str] = None
unit: Optional[str] = None
price: Optional[float] = None
confidence: int = 50
class MemoryUpdate(BaseModel):
name: Optional[str] = None
specification: Optional[str] = None
unit: Optional[str] = None
price: Optional[float] = None
confidence: Optional[int] = None
class MemoryListResponse(BaseModel):
items: List[MemoryItem]
total: int
page: int
page_size: int
stats: Optional[Dict] = None
def _get_db():
from app.core.db.product_db import ProductDatabase
return ProductDatabase(_db_path, _excel_source)
def _row_to_item(row: Dict) -> MemoryItem:
return MemoryItem(
barcode=row.get("barcode", ""),
name=row.get("name", ""),
specification=row.get("specification"),
unit=row.get("unit"),
price=row.get("price"),
avg_price=row.get("avg_price"),
min_price=row.get("min_price"),
max_price=row.get("max_price"),
price_count=row.get("price_count", 0),
confidence=row.get("confidence", 0),
source=row.get("source", "ocr"),
last_used=row.get("last_used"),
use_count=row.get("use_count", 0),
)
@router.get("", response_model=MemoryListResponse)
async def list_memory(
search: str = "",
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=200),
current_user: dict = Depends(get_current_user),
):
db = _get_db()
results = db.get_all_memories()
if search:
s = search.lower()
results = [r for r in results if s in r.get("barcode", "").lower() or s in r.get("name", "").lower()]
total = len(results)
start = (page - 1) * page_size
page_items = results[start:start + page_size]
# Compute confidence stats from all results (not just current page)
high = sum(1 for r in results if r.get("confidence", 0) > 50)
medium = sum(1 for r in results if 10 <= r.get("confidence", 0) <= 50)
low = sum(1 for r in results if r.get("confidence", 0) < 10)
return MemoryListResponse(
items=[_row_to_item(r) for r in page_items],
total=total,
page=page,
page_size=page_size,
stats={"high": high, "medium": medium, "low": low, "total": total},
)
@router.get("/{barcode}")
async def get_memory(
barcode: str,
current_user: dict = Depends(get_current_user),
):
db = _get_db()
product = db.get_memory(barcode)
if not product:
raise HTTPException(404, f"未找到条码 {barcode} 的记忆记录")
return product
@router.post("")
async def create_memory(
body: MemoryCreate,
current_user: dict = Depends(get_current_user),
):
db = _get_db()
existing = db.get_memory(body.barcode)
if existing:
raise HTTPException(409, f"条码 {body.barcode} 已存在,请使用编辑功能")
db.learn_from_product({
"barcode": body.barcode,
"name": body.name or "",
"specification": body.specification or "",
"unit": body.unit or "",
"price": body.price or 0,
}, source="user_confirmed")
return {"message": f"已创建记忆记录 {body.barcode}"}
@router.put("/{barcode}")
async def update_memory(
barcode: str,
body: MemoryUpdate,
current_user: dict = Depends(get_current_user),
):
db = _get_db()
existing = db.get_memory(barcode)
if not existing:
raise HTTPException(404, f"未找到条码 {barcode}")
update_data = body.model_dump(exclude_none=True)
if not update_data:
raise HTTPException(400, "没有提供更新数据")
db.update_memory(barcode, update_data)
return {"message": f"已更新 {barcode}", "updated_fields": list(update_data.keys())}
@router.delete("/{barcode}")
async def delete_memory(
barcode: str,
current_user: dict = Depends(get_current_user),
):
db = _get_db()
existing = db.get_memory(barcode)
if not existing:
raise HTTPException(404, f"未找到条码 {barcode}")
db.delete_memory(barcode)
return {"message": f"已删除 {barcode}"}
@router.post("/reimport")
async def reimport_memory(
current_user: dict = Depends(get_current_user),
):
db = _get_db()
try:
count = db.reimport()
return {"message": f"重新导入完成,共导入 {count} 条记录", "count": count}
except Exception as e:
raise HTTPException(500, f"导入失败: {e}")
@router.get("/export/sync")
async def export_memory(
current_user: dict = Depends(get_current_user),
):
db = _get_db()
data = db.export_for_sync()
return {"data": data, "count": len(data)}
@router.post("/import/sync")
async def import_memory(
data: dict,
current_user: dict = Depends(get_current_user),
):
db = _get_db()
try:
count = db.import_from_sync(data.get("data", []))
return {"message": f"导入完成,共 {count}", "count": count}
except Exception as e:
raise HTTPException(500, f"导入失败: {e}")