"""Barcode mapping CRUD endpoints.""" import json from pathlib import Path from typing import Dict, Optional, List from fastapi import APIRouter, HTTPException, Depends from pydantic import BaseModel from ..auth.dependencies import get_current_user router = APIRouter(prefix="/api/barcodes", tags=["barcodes"]) _project_root = Path(__file__).resolve().parent.parent.parent.parent _mappings_file = _project_root / "config" / "barcode_mappings.json" class BarcodeMapping(BaseModel): barcode: str target: Optional[str] = None description: Optional[str] = None # Special rule fields multiplier: Optional[int] = None target_unit: Optional[str] = None fixed_price: Optional[float] = None specification: Optional[str] = None class BarcodeMappingUpdate(BaseModel): target: Optional[str] = None description: Optional[str] = None multiplier: Optional[int] = None target_unit: Optional[str] = None fixed_price: Optional[float] = None specification: Optional[str] = None def _load_mappings() -> Dict: if not _mappings_file.is_file(): return {} try: return json.loads(_mappings_file.read_text(encoding="utf-8")) except Exception: return {} def _save_mappings(data: Dict): _mappings_file.parent.mkdir(parents=True, exist_ok=True) _mappings_file.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") @router.get("") async def list_barcodes( search: str = "", current_user: dict = Depends(get_current_user), ): mappings = _load_mappings() items = [] for barcode, info in mappings.items(): if isinstance(info, dict): target = info.get("map_to", info.get("target", "")) desc = info.get("description", "") item = { "barcode": barcode, "target": target, "description": desc, "multiplier": info.get("multiplier"), "target_unit": info.get("target_unit"), "fixed_price": info.get("fixed_price"), "specification": info.get("specification"), } else: item = { "barcode": barcode, "target": str(info), "description": "", "multiplier": None, "target_unit": None, "fixed_price": None, "specification": None, } s = search.lower() if search else "" if s and s not in barcode.lower() and s not in item["target"].lower() and s not in (desc or "").lower(): continue items.append(item) return {"items": items, "total": len(items)} @router.get("/{barcode}") async def get_barcode( barcode: str, current_user: dict = Depends(get_current_user), ): mappings = _load_mappings() if barcode not in mappings: raise HTTPException(404, f"未找到条码映射 {barcode}") info = mappings[barcode] if isinstance(info, dict): return {"barcode": barcode, "target": info.get("map_to", info.get("target", "")), "description": info.get("description", "")} return {"barcode": barcode, "target": str(info), "description": ""} @router.post("") async def create_barcode( body: BarcodeMapping, current_user: dict = Depends(get_current_user), ): mappings = _load_mappings() if body.barcode in mappings: raise HTTPException(409, f"条码 {body.barcode} 已存在") entry: dict = {"description": body.description or ""} if body.multiplier: entry["multiplier"] = body.multiplier if body.target_unit: entry["target_unit"] = body.target_unit if body.fixed_price is not None: entry["fixed_price"] = body.fixed_price if body.specification: entry["specification"] = body.specification else: entry["map_to"] = body.target or "" mappings[body.barcode] = entry _save_mappings(mappings) return {"message": f"已创建规则 {body.barcode}"} @router.put("/{barcode}") async def update_barcode( barcode: str, body: BarcodeMappingUpdate, current_user: dict = Depends(get_current_user), ): mappings = _load_mappings() if barcode not in mappings: raise HTTPException(404, f"未找到条码规则 {barcode}") existing = mappings[barcode] if not isinstance(existing, dict): existing = {"map_to": str(existing), "description": ""} # Check if this is a special rule (has multiplier) or being converted to one if body.multiplier is not None: # Convert to special rule: remove map_to, add multiplier fields existing.pop("map_to", None) existing["multiplier"] = body.multiplier if body.target_unit is not None: existing["target_unit"] = body.target_unit if body.fixed_price is not None: existing["fixed_price"] = body.fixed_price if body.specification is not None: existing["specification"] = body.specification elif body.target is not None: # Convert to simple mapping: remove special fields, add map_to for k in ("multiplier", "target_unit", "fixed_price", "specification"): existing.pop(k, None) existing["map_to"] = body.target if body.description is not None: existing["description"] = body.description mappings[barcode] = existing _save_mappings(mappings) return {"message": f"已更新规则 {barcode}"} @router.delete("/{barcode}") async def delete_barcode( barcode: str, current_user: dict = Depends(get_current_user), ): mappings = _load_mappings() if barcode not in mappings: raise HTTPException(404, f"未找到条码映射 {barcode}") del mappings[barcode] _save_mappings(mappings) return {"message": f"已删除映射 {barcode}"}