feat(cafe24): 편집기에서 PC/모바일 구분 제거 — 항상 같은 내용으로 반영
요청대로 단순화했다. 모바일 상세설명 보기 영역과 「모바일도 함께」 체크박스를 없애고, 적용 시 description 과 mobile_description 에 **항상 같은 HTML** 을 쓴다. separated_mobile_description 값과 무관하므로 한쪽만 바뀌어 어긋나는 사고가 없어진다. 다만 분리 사용 상품의 모바일 내용이 PC 와 달랐다면 그 내용을 덮어쓰게 된다. 그래서 쓰기 전에 **모바일 내용도 BACKUP revision 으로 따로 남긴다** — 백업이 없으면 되찾을 방법이 없다. 기존 BACKUP 은 PC 값만 담고 있었다. 부수 정리: 편집기 컨텍스트에서 html_mobile 제거, mobile_html 의 None 분기 제거, 감사로그 표기를 "PC·모바일 동시 반영"으로 고정, 라벨을 "상세설명 HTML · PC/모바일 공통"으로 변경. 검증: 유닛테스트 51개 통과. 분리+내용상이 상품으로 편집기를 렌더해 모바일 보기 영역· 체크박스가 사라지고 공통 문구·버전 이력·다시 읽기가 남은 것을 확인. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,9 @@
|
||||
MANUAL 버전 + 감사로그
|
||||
로컬 DB 의 마지막 버전을 "지금 카페24에 올라간 값"으로 가정하지 않는다.
|
||||
|
||||
PC/모바일은 구분하지 않는다 — 적용 시 `description` 과 `mobile_description` 에
|
||||
같은 HTML 을 쓴다(운영 방침). 분리 사용 상품이어도 한쪽만 바뀌는 일이 없다.
|
||||
|
||||
이미지 경로의 한글은 카페24에 퍼센트 인코딩으로 저장돼 있다. 편집기에는
|
||||
`store.decode_html_urls` 로 풀어서 보여주고, 저장할 때 `encode_html_urls` 로
|
||||
되돌린다(왕복 보존 — store.py 주석 참고).
|
||||
@@ -139,9 +142,6 @@ def _editor_ctx(st: Any, product_no: int) -> dict[str, Any]:
|
||||
# (2) 태그마다 줄을 나눠 정리해서 보여준다.
|
||||
# 저장할 때 같은 정리를 거친 값을 카페24에 쓴다(화면과 저장값이 같다).
|
||||
"html_pc": store.format_html(store.decode_html_urls(desc.description)) if desc else "",
|
||||
"html_mobile": (
|
||||
store.format_html(store.decode_html_urls(desc.mobile_description)) if desc else ""
|
||||
),
|
||||
# 지문은 **인코딩된 원본**으로 만든다(적용 직전 카페24 값과 비교하므로).
|
||||
"fingerprint": store.fingerprint(desc.description) if desc else "",
|
||||
"revisions": st.list_revisions(product_no, limit=20),
|
||||
@@ -241,7 +241,6 @@ def product_apply(
|
||||
base_fingerprint: str = Form(""),
|
||||
memo: str = Form(""),
|
||||
list_query: str = Form(""),
|
||||
apply_mobile: str = Form(""),
|
||||
):
|
||||
"""편집한 HTML 을 카페24에 즉시 적용한다.
|
||||
|
||||
@@ -251,8 +250,8 @@ def product_apply(
|
||||
3) 편집 시작 시점의 지문과 비교해 충돌이면 거부한다
|
||||
4) 쓰고, MANUAL 버전과 감사로그를 남긴다
|
||||
|
||||
PC/모바일 미분리 상품은 모바일 필드도 같은 HTML 로 맞춘다. 분리 상품은
|
||||
모바일을 건드리지 않는다(화면에 별도 반영 안내를 띄운다).
|
||||
PC/모바일을 구분하지 않는다 — 두 필드에 같은 HTML 을 쓴다. 분리 사용 상품의
|
||||
모바일 내용이 PC 와 달랐다면 덮어쓰기 전에 그 내용도 BACKUP 으로 남긴다.
|
||||
"""
|
||||
checked = guard(request)
|
||||
if not isinstance(checked, tuple):
|
||||
@@ -290,6 +289,16 @@ def product_apply(
|
||||
memo="적용 직전 자동 백업",
|
||||
created_by=actor,
|
||||
)
|
||||
# 모바일 내용이 PC 와 달랐다면 그것도 따로 남긴다. 아래에서 모바일을 PC 와 같게
|
||||
# 덮어쓰므로, 백업하지 않으면 그 내용을 되찾을 방법이 없다.
|
||||
if current.mobile_description and current.mobile_description != current.description:
|
||||
st.add_revision(
|
||||
product_no=product_no,
|
||||
html_content=current.mobile_description,
|
||||
revision_type=store.REVISION_BACKUP,
|
||||
memo="적용 직전 자동 백업 (모바일 — PC와 달랐던 내용)",
|
||||
created_by=actor,
|
||||
)
|
||||
|
||||
if base_fingerprint and base_fingerprint != store.fingerprint(current.description):
|
||||
st.log_audit(
|
||||
@@ -301,17 +310,11 @@ def product_apply(
|
||||
status_code=303,
|
||||
)
|
||||
|
||||
# 미분리 상품은 모바일도 함께 맞춘다 — PC 만 바꾸면 모바일 상세가 어긋난다.
|
||||
# 분리 상품은 화면의 「모바일도 함께」 체크에 따른다(두 내용이 같으면 기본 체크,
|
||||
# 다르면 기본 해제 — 일부러 다르게 만든 모바일 페이지를 덮어쓰지 않기 위해).
|
||||
if current.separated_mobile:
|
||||
mobile_html = submitted if apply_mobile else None
|
||||
else:
|
||||
mobile_html = submitted
|
||||
# PC/모바일을 구분하지 않는다 — 항상 같은 내용으로 함께 쓴다(운영 방침).
|
||||
# 분리 사용 상품이어도 모바일에 같은 HTML 을 넣으므로 한쪽만 바뀌는 일이 없다.
|
||||
mobile_html = submitted
|
||||
|
||||
if submitted == current.description and (
|
||||
mobile_html is None or mobile_html == current.mobile_description
|
||||
):
|
||||
if submitted == current.description and mobile_html == current.mobile_description:
|
||||
return RedirectResponse(url=f"{back}&msg=변경된 내용이 없어 적용하지 않았습니다.", status_code=303)
|
||||
try:
|
||||
products.update_descriptions(
|
||||
@@ -338,8 +341,7 @@ def product_apply(
|
||||
st.log_audit(
|
||||
actor=actor, action="apply_description", product_no=product_no,
|
||||
revision_id=revision_id, result="SUCCESS",
|
||||
detail=f"{len(submitted)}자 적용 (백업 {backup_id}"
|
||||
+ (", 모바일 동시 반영)" if mobile_html is not None else ")"),
|
||||
detail=f"{len(submitted)}자 적용 (백업 {backup_id}, PC·모바일 동시 반영)",
|
||||
)
|
||||
logger.info("카페24 상품 %s 상세설명 적용 (%s)", product_no, actor)
|
||||
return RedirectResponse(
|
||||
|
||||
@@ -31,20 +31,7 @@
|
||||
|
||||
{% if desc %}
|
||||
<p class="cf24-note">
|
||||
{% if desc.separated_mobile %}
|
||||
<strong>PC/모바일 분리 사용 상품입니다.</strong>
|
||||
모바일({{ desc.mobile_description | length }}자)은 아래 <strong>「모바일도 함께」</strong>
|
||||
체크박스로 같은 내용을 반영할 수 있습니다.
|
||||
{% if desc.mobile_differs %}
|
||||
<span class="cf24-warn">지금 모바일 내용이 PC와 달라 기본 해제되어 있습니다</span> —
|
||||
체크하면 모바일이 PC와 같은 내용으로 덮어써집니다.
|
||||
{% else %}
|
||||
현재 두 내용이 같아 기본 체크되어 있습니다.
|
||||
{% endif %}
|
||||
{% else %}
|
||||
모바일은 PC와 동일 설정이라 적용 시 <strong>함께 반영</strong>됩니다.
|
||||
{% endif %}
|
||||
{% if desc.mobile_differs %}<span class="cf24-warn">현재 PC/모바일 내용이 다릅니다.</span>{% endif %}
|
||||
적용하면 <strong>PC·모바일 상세설명이 같은 내용으로</strong> 반영됩니다.
|
||||
<br />
|
||||
소스는 <strong>태그마다 줄을 나눠 정리</strong>해서 보여주며, <strong>적용하면 정리된 소스가
|
||||
그대로 카페24에 저장</strong>됩니다. 줄바꿈·들여쓰기만 바뀌고 태그 구조는 그대로이며,
|
||||
@@ -62,17 +49,7 @@
|
||||
<input type="hidden" name="list_query" value="{{ list_query }}" />
|
||||
|
||||
<div class="cf24-editor-bar">
|
||||
<span class="cf24-muted">
|
||||
PC 상세설명 HTML · {{ desc.description | length }}자
|
||||
{% if desc.separated_mobile %}
|
||||
<label class="cf24-check-inline"
|
||||
title="분리 사용 상품이라 기본적으로 PC 만 바뀝니다. 체크하면 모바일 상세설명도 같은 내용으로 함께 반영합니다.">
|
||||
<input type="checkbox" name="apply_mobile" value="1"
|
||||
{% if not desc.mobile_differs %}checked{% endif %} />
|
||||
모바일도 함께
|
||||
</label>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="cf24-muted">상세설명 HTML · {{ desc.description | length }}자 · PC/모바일 공통</span>
|
||||
<span class="cf24-editor-bar-right">
|
||||
<input class="cf24-memo" type="text" name="memo" maxlength="200"
|
||||
placeholder="변경 메모 (버전 이력에 남습니다)" />
|
||||
@@ -98,17 +75,6 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if desc.separated_mobile or desc.mobile_differs %}
|
||||
<details class="cf24-details">
|
||||
<summary>모바일 상세설명 HTML 보기 (읽기 전용 · {{ desc.mobile_description | length }}자)</summary>
|
||||
<textarea id="cf24-html-mo" class="cf24-html" rows="12" readonly
|
||||
spellcheck="false">{{ html_mobile }}</textarea>
|
||||
<div class="cf24-actions">
|
||||
<button type="button" class="erp-btn erp-btn-outline" data-copy="cf24-html-mo">복사</button>
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
<details class="cf24-details">
|
||||
<summary>버전 이력 {% if revisions %}({{ revisions | length }}건){% endif %}</summary>
|
||||
{% if revisions %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814k" />
|
||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814m" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
+12
-13
@@ -71,11 +71,11 @@ app/modules/cafe24/ ← 상품관리 모듈
|
||||
```
|
||||
┌─ 550px ───────────────────┬───────── 남은 폭 전부 ─────────┐
|
||||
│ 상품명 검색(한 줄) │ 상품 이름 · 번호 · 진열/판매 │
|
||||
│ ☐진열중 ☐판매중 │ PC 상세설명 HTML 편집기 │
|
||||
│ ── 목록(전체, 스크롤) ── │ (문법 강조 · 남은 높이 전부) │
|
||||
│ 번호 상품명 진열 판매 수정 │ [메모] [복사] [카페24에 적용] │
|
||||
│ (제목행 클릭 = 정렬) │ ▸ 모바일 HTML(분리 상품만) │
|
||||
│ │ ▸ 버전 이력 │
|
||||
│ ☑진열중 ☑판매중 (기본 체크) │ 상세설명 HTML 편집기 │
|
||||
│ ── 목록(전체, 스크롤) ── │ (PC/모바일 공통 · 문법 강조) │
|
||||
│ 번호 상품명 진열 판매 수정 │ [메모][복사][다시 읽기][적용] │
|
||||
│ (제목행 클릭 = 정렬) │ ▸ 버전 이력 │
|
||||
│ │ │
|
||||
└───────────────────────────┴─────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -163,7 +163,7 @@ app/modules/cafe24/ ← 상품관리 모듈
|
||||
진행률을 보여주고, 한 건 실패가 나머지를 막지 않는다.
|
||||
- 적용 순서는 단건 편집과 같다: **현재값 재조회 → BACKUP → 교체 → PUT → MANUAL +
|
||||
감사로그**(`action=bulk_style`). 지문 대조는 없다 — 방금 읽은 값을 바로 쓰기 때문.
|
||||
- PC/모바일 분리 상품은 모바일도 함께 바꾼다.
|
||||
- PC/모바일 분리 상품은 각 필드의 `<style>` 블록을 각각 바꾼다.
|
||||
|
||||
교체할 내용은 `routes_bulk.TARGET_STYLE_BLOCK` 에 있다. `<style>` 안쪽은
|
||||
`format_html` 이 건드리지 않으므로 탭 들여쓰기까지 그대로 저장된다.
|
||||
@@ -255,13 +255,12 @@ cafe24_oauth_tokens 저장
|
||||
추가 규칙:
|
||||
|
||||
- 빈 내용은 거부한다(상세페이지 전체를 날리는 실수 방지).
|
||||
- **PC/모바일 미분리(`separated_mobile_description='F'`) 상품은 모바일 필드도
|
||||
같은 HTML 로 함께 쓴다.** PC 만 바꾸면 모바일이 어긋난다.
|
||||
- **분리(`'T'`) 상품은 편집기의 「모바일도 함께」 체크박스에 따른다.**
|
||||
현재 두 내용이 같으면 **기본 체크**(그대로 두면 함께 반영), 다르면 **기본 해제**하고
|
||||
경고를 띄운다 — 일부러 다르게 만든 모바일 페이지를 조용히 덮어쓰지 않기 위함이다.
|
||||
일괄수정(`routes_bulk`)은 분리 상품도 항상 모바일까지 바꾼다(대상이 `<style>` 블록
|
||||
하나로 한정되어 있어 내용을 잃을 위험이 없다).
|
||||
- **PC/모바일을 구분하지 않는다.** `description` 과 `mobile_description` 에 항상 같은
|
||||
HTML 을 쓴다(운영 방침). `separated_mobile_description` 값과 무관하며, 편집 화면에도
|
||||
모바일 소스를 따로 보여주지 않는다 — 한쪽만 바뀌어 어긋나는 사고가 없어진다.
|
||||
분리 사용 상품의 모바일 내용이 PC 와 달랐다면 덮어쓰기 전에 **그 내용도 BACKUP
|
||||
revision 으로 남긴다**(백업이 없으면 되찾을 방법이 없다).
|
||||
일괄수정(`routes_bulk`)은 각 필드의 `<style>` 블록만 바꾼다.
|
||||
- 실패해도 BACKUP 은 이미 남아 있으므로 오류 메시지에 버전 번호를 알려준다.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user