"""카페24 상품 엔드포인트 래퍼. 전송/재시도/인증은 Cafe24Client 가 담당하고, 여기서는 경로와 payload 모양만 안다. 향후 주문관리는 같은 클라이언트로 `orders.py` 를 추가하면 된다. ⚠️ 상품 수정 payload 구조는 카페24 Admin API 버전에 따라 다를 수 있다. 실제 쇼핑몰에 반영하기 전 반드시 테스트 상품 1건으로 검증할 것. """ from __future__ import annotations from typing import Any from .client import Cafe24Client # 카페24 상품 목록 API 의 1회 최대 조회 수 PAGE_LIMIT = 100 def count_products(client: Cafe24Client, *, product_name: str = "") -> int: params: dict[str, Any] = {} if product_name: params["product_name"] = product_name payload = client.get("/admin/products/count", params=params) try: return int(payload.get("count") or 0) except (TypeError, ValueError): return 0 def list_products( client: Cafe24Client, *, limit: int = PAGE_LIMIT, offset: int = 0, product_name: str = "", product_no: int | None = None, ) -> list[dict[str, Any]]: """상품 목록 1페이지. 검색어가 있으면 상품명 부분일치로 조회한다.""" params: dict[str, Any] = { "limit": max(1, min(int(limit), PAGE_LIMIT)), "offset": max(0, int(offset)), } if product_name: params["product_name"] = product_name if product_no: params["product_no"] = int(product_no) payload = client.get("/admin/products", params=params) products = payload.get("products") return products if isinstance(products, list) else [] def get_product(client: Cafe24Client, product_no: int) -> dict[str, Any]: """상품 1건 기본 정보 (상세설명은 별도 조회 — get_description).""" payload = client.get(f"/admin/products/{int(product_no)}", product_no=int(product_no)) product = payload.get("product") return product if isinstance(product, dict) else {} def get_description(client: Cafe24Client, product_no: int) -> str: """상품의 현재 상세설명 HTML. 카페24는 상세설명을 별도 리소스로 제공한다. 이 값이 언제나 source of truth 이며, 로컬 DB 의 마지막 버전을 현재값이라고 가정하지 않는다. """ no = int(product_no) payload = client.get(f"/admin/products/{no}/description", product_no=no) description = payload.get("description") if isinstance(description, dict): return str(description.get("description") or "") return "" def update_description(client: Cafe24Client, product_no: int, html: str) -> dict[str, Any]: """상세설명 HTML 전체 교체. 성공하면 카페24 응답 dict 를 돌려준다. 실패는 Cafe24ApiError/Cafe24AuthError 로 올라오므로, 호출부는 예외가 없을 때만 성공으로 처리하면 된다. """ no = int(product_no) payload = client.put( f"/admin/products/{no}/description", json={"request": {"description": html}}, product_no=no, ) description = payload.get("description") return description if isinstance(description, dict) else payload def normalize_product(raw: dict[str, Any]) -> dict[str, Any]: """카페24 상품 dict → 캐시 테이블 컬럼 모양으로 정규화. 카페24는 boolean 을 'T'/'F' 문자열로 준다. """ def flag(value: Any, *, default: bool = True) -> bool: if isinstance(value, bool): return value text = str(value or "").strip().upper() if text in ("T", "TRUE", "Y", "1"): return True if text in ("F", "FALSE", "N", "0"): return False return default try: product_no = int(raw.get("product_no") or 0) except (TypeError, ValueError): product_no = 0 return { "product_no": product_no, "product_code": str(raw.get("product_code") or ""), "product_name": str(raw.get("product_name") or ""), "display": flag(raw.get("display")), "selling": flag(raw.get("selling")), }