feat(cafe24): 편집기에서 상품명 수정

제목 옆 연필 버튼 → 입력칸 → 저장. 이름만 바꾸려고 카페24 관리자에 들어갈
필요가 없어진다.

- POST /cafe24/products/{no}/name (JSON) 추가. 쓰기 전에 카페24 현재값을 읽어
  이전 이름을 감사로그(rename_product)에 남긴다 - 되돌릴 revision 이 없으므로
  로그가 유일한 복구 단서다. 값이 같으면 호출하지 않는다.
- build_update_payload/update_product 에 product_name 추가(부분 수정이라
  상세설명·진열·판매는 그대로).
- 빈 값과 250자 초과는 서버에서 400. 화면도 maxlength 로 막는다.
- 평소에는 읽기 전용 제목이고 연필을 눌러야 입력칸이 된다 - 클릭 한 번으로
  실수로 고쳐지지 않게. Enter 저장, Esc 취소.
- 화면은 요청값이 아니라 카페24가 확인해 준 이름으로 다시 그리고, 왼쪽 목록의
  이름·정렬키(data-name)도 함께 갱신한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 18:35:37 +09:00
parent 5127600a3d
commit 3537e058b0
6 changed files with 219 additions and 4 deletions
+61
View File
@@ -229,6 +229,67 @@ def product_pane(request: Request, product_no: int) -> HTMLResponse:
return _no_store(render_template(request, "cafe24/_editor.html", ctx))
# 카페24 상품명 최대 길이(API 문서 기준). 넘기면 카페24가 거절하므로 미리 막는다.
NAME_MAX = 250
@products_router.post("/products/{product_no}/name")
def product_rename(
request: Request,
product_no: int,
payload: dict[str, Any] = Body(default_factory=dict),
) -> dict[str, Any]:
"""상품명만 바꾼다 — 편집기 제목 옆 연필 버튼용(JSON API).
상세설명과 마찬가지로 **쓰기 전에 카페24의 현재값을 읽는다.** 여기서는 되돌릴
HTML 이 없으므로 revision 은 만들지 않고, 대신 이전 이름을 감사로그에 남긴다
(되돌리려면 로그를 보고 다시 바꾼다).
"""
st, user = require_store(request)
actor = str(user.get("email") or "")
name = str(payload.get("name") or "").strip()
if not name:
raise HTTPException(status_code=400, detail="상품명을 입력하세요.")
if len(name) > NAME_MAX:
raise HTTPException(status_code=400, detail=f"상품명은 {NAME_MAX}자를 넘을 수 없습니다.")
api = build_cafe24_api(st)
try:
current = products.get_product(api.client, product_no)
except Cafe24Error as exc:
st.log_audit(
actor=actor, action="rename_product", product_no=product_no,
result="FAIL", detail=f"현재값 조회 실패: {exc}",
)
raise HTTPException(status_code=502, detail=f"카페24 현재값을 읽지 못했습니다: {exc}") from exc
before = str(current.get("product_name") or "")
if before == name:
return {"ok": True, "product_name": before, "changed": False}
try:
updated = products.update_product(api.client, product_no, product_name=name)
except Cafe24Error as exc:
st.log_audit(
actor=actor, action="rename_product", product_no=product_no,
result="FAIL", detail=f"'{before}''{name}' 실패: {exc}",
)
logger.warning("카페24 상품 %s 이름 변경 실패: %s", product_no, exc)
raise HTTPException(status_code=502, detail=str(exc)) from exc
info = products.normalize_product(updated) if updated else {}
if info.get("product_no"):
st.upsert_products([info])
after = str(info.get("product_name") or name)
st.log_audit(
actor=actor, action="rename_product", product_no=product_no,
result="SUCCESS", detail=f"'{before}''{after}'",
)
logger.info("카페24 상품 %s 이름 변경 (%s)", product_no, actor)
return {"ok": True, "product_name": after, "changed": True}
@products_router.post("/products/{product_no}/status")
def product_status(
request: Request,