MD-17 · BEST 상품 자동 교체

BEST 상품 자동 교체 (그룹코드 차순위 · 최저가 폴백)

메인 BEST 편성의 히어로코드가 쇼트/중단되면 같은 그룹코드 안에서 최저가(net) 차순위를 자동 노출하고, MD 수동 편성은 우선 보존한다.

🔴 결정 대기 메인 컨텐츠 관리 자동화 배치 그룹코드 폴백
➕ 기능 추가
무엇 — 메인 BEST 편성의 히어로코드가 쇼트/중단되면 같은 그룹코드 안에서 최저가(net) 차순위를 자동 노출하고, MD 수동 편성은 우선 보존한다.📍 위치: 오더히어로 관리 › 메인 컨텐츠 관리

핵심 결정

폴백 키
group_id (동일 상품군 내에서만 대체)
교체 기준
최저가 net_price ASC (동가 시 net_unit_price → created_on)
충돌 우선순위
MD 고정 > 자동교체 (수동 편성 슬롯은 자동 스킵)
안전장치
대상 없음 = 슬롯 숨김 + MD 알림(원본 유지) / 실패 = 1클릭 되돌리기
스코프
쇼트·중단 차순위 폴백까지. 카테고리 BEST 업종 자동화는 2차 확대
01

화면 — 먼저 보기

이 티켓이 실제로 어떤 화면이 되는지 먼저 봅니다

현재 메인 BEST는 100% 수기 편성. 쇼트/중단 상품이 그대로 노출되면 사장님 이탈 → 그룹코드 최저가 차순위를 자동 대체하되 오판·충돌·롤백 규칙을 먼저 확정한다.
현행 화면 · 실제 캡처
현행 「메인 컨텐츠」 — BEST 자동 교체 대상
현행 「메인 컨텐츠」 — BEST 자동 교체 대상
변경안 · 제안 화면
오더히어로 관리 › 메인 컨텐츠 관리기능 추가
상태 ▾교체모드 ▾🔍 히어로코드·상품명자동교체 실행되돌리기
편성 위치원본 히어로코드상품명그룹코드상태 🆕실제 노출 코드 🆕교체 모드 🆕넷 공급가최종 교체 🆕
BEST 1위H0004821국산 대파 1kgG-1023🔴 쇼트H0009912 (차순위)자동2,180원07-30 04:00 배치
BEST 2위H0007733냉장 삼겹살 1kgG-2044정상H0007733MD 고정9,900원
BEST 3위H0005510청양고추 500gG-1180⚫ 중단대상 없음 (슬롯 숨김)자동·보류07-30 실패·알림

▲ 메인 컨텐츠 관리 — BEST 편성 슬롯 목록 — 바뀌는 컬럼은 🆕 로 표시(예시 데이터)

1
배치·이벤트 스캔 — 주/월 정기 배치와 실시간 품절 이벤트가 BEST 슬롯의 원본 히어로코드 상태(쇼트/중단)를 점검한다.
2
폴백 후보 산출 — 원본이 쇼트/중단이면 같은 group_id 안에서 판매가능 상품을 모아 net_price 최저가 순으로 차순위 후보를 뽑는다.
3
MD 고정 확인 — 슬롯의 swap_mode가 MD_FIXED면 자동교체를 건너뛰고, AUTO 슬롯만 대체한다(수동 편성 보존).
4
교체 실행 & 로그 — displayed_hero_code를 차순위로 갱신하고 swap_log에 원본·사유를 기록한다. 후보가 0이면 원본 유지 채 슬롯 숨김 + MD 알림.
5
검수 · 되돌리기 — MD가 목록에서 자동교체 결과와 품절 오판을 확인하고, 실행 로그에서 원본 코드로 1클릭 롤백한다.
02

정책 / 정의

용어를 못 박고, 규칙을 명확히

용어 정의
용어정의
히어로코드상품 식별 표준키. seller_product.hero_product_code, 메인 편성의 노출 단위.
그룹코드(group_id)동일 상품군 묶음 키(seller_product.group_id, 인덱스 존재). 폴백 후보 풀을 정의한다.
넷 공급가(net)매입 순공급가. seller_product.net_price(bigint). 자동 교체의 최저가 정렬 기준값.
쇼트 / 중단쇼트=일시 품절(sold_out). 중단=판매중지(use_yn≠'U' 또는 long_term_sold_out).
차순위 폴백원본 히어로코드 노출 불가 시 같은 그룹코드 안에서 최저가 대체 코드를 자동 노출하는 동작.
MD 고정(swap_mode=MD_FIXED)MD가 수동 편성해 자동교체 대상에서 제외한 슬롯. 충돌 시 자동보다 우선.
실제 노출 코드원본 히어로코드가 살아있으면 원본, 폴백되면 대체 코드. 앱에 실제 노출되는 코드.
정책 (규칙)
#규칙비고
1폴백 후보는 동일 group_id 안에서만 산출한다.seller_product.group_id 기준. group_id가 NULL인 상품은 폴백 불가 → 대상 없음 처리(스키마상 nullable, 정책 가정).
2교체 기준은 최저가(net_price) ASC. 동가 시 net_unit_price → created_on 순.미팅 확정 = 최저가. 정확한 tie-break 순서는 착수 전 확정 필요(가정).
3쇼트/중단 판정은 sold_out OR long_term_sold_out OR use_yn≠'U' OR total_available_quantity<=0 신호를 사용한다.'품절 오판' 방지가 핵심 리스크. 어떤 신호를 AND/OR로 묶을지 결정 대기(🔴). seller_product_sold_out_log를 판정 근거로 병행.
4MD 고정 슬롯은 자동교체에서 제외한다. 자동·수동 충돌 시 MD 고정이 항상 이긴다.swap_mode=MD_FIXED. 자동 슬롯만 배치가 건드림. MD 고정 여부는 목록/모달에서 토글.
5그룹 내 판매가능 후보가 0이면 원본을 유지한 채 슬롯을 숨기고 MD에게 알린다. 자동 삭제 금지.'바꿀 상품이 없을 때' 처리. 빈 슬롯 노출·자동 편성 이탈 방지.
6모든 자동교체는 원본 코드를 로그에 보존하고 1클릭 되돌리기를 제공한다.'자동 실행 실패 되돌리기'. main_content_swap_log에 원본 hero_code·사유 저장, 원복 시 displayed_hero_code 복원.
7정기 BEST 교체는 배치(주1/월1), 쇼트 폴백은 근실시간으로 실행한다.정기 교체 주기는 미팅 결정 대기(가정: 주간). 쇼트 폴백은 품절 이벤트 트리거.
8자동교체 실행 주체는 시스템 계정으로 감사 기록한다.created_by/updated_by = 'AUTO_SWAP'. 수기 편성과 이력 구분.
9카테고리 BEST의 업종별 순위 자동화는 본 스코프에서 제외한다.티켓의 '확대 희망' 항목. 폴백/최저가 교체 안정화 후 2차.
03

ERD

데이터 구조 · 관계 · DDL

main_content_product WRITE
content_idPK varchar(100)
hero_product_codePK varchar(100)
sort_orderinteger
usableboolean
swap_modevarchar(10)
displayed_hero_codevarchar(100)
swap_reasonvarchar(30)
swapped_ontimestamp
메인 편성 슬롯(BEST 포함)의 상품 매핑. 교체 모드·실제 노출 코드 컬럼 추가.
seller_product READ
hero_product_codePK varchar(100)
group_idinteger
net_pricebigint
sold_outboolean
long_term_sold_outboolean
use_ynvarchar(1)
total_available_quantityinteger
폴백 후보 산출·최저가 정렬·쇼트/중단 판정 소스.
seller_product_group_mscd READ
group_idPK integer
main_product_namevarchar(255)
category1varchar(10)
그룹코드 마스터(대표 상품명·카테고리). 그룹 표시·카테고리 BEST(2차) 근거.
main_content_swap_log 신규
swap_log_idPK bigserial
content_idFK varchar(100)
original_hero_codeFK varchar(100)
replaced_hero_codevarchar(100)
group_idinteger
reasonvarchar(30)
statusvarchar(10)
executed_byvarchar(20)
executed_ontimestamp
자동교체 실행 이력·되돌리기 원장. 원본 코드 보존.
main_content_product.hero_product_code = seller_product.hero_product_code (편성 → 상품 원본)
seller_product(원본).group_id = seller_product(후보).group_id (동일 그룹 차순위 폴백)
seller_product.group_id = seller_product_group_mscd.group_id (그룹 대표명/카테고리)
main_content_swap_log.content_id + original_hero_code → main_content_product (되돌리기 대상)
-- 1) 메인 편성 슬롯: 자동교체 모드·실제 노출 코드·이력 컬럼 추가
ALTER TABLE main_content_product
  ADD COLUMN swap_mode           varchar(10)  NOT NULL DEFAULT 'AUTO',  -- 교체 모드: AUTO(자동) / MD_FIXED(수동 고정, 자동 제외)
  ADD COLUMN displayed_hero_code varchar(100),                          -- 실제 노출 코드(폴백 시 대체 코드, 정상 시 원본과 동일)
  ADD COLUMN swap_reason         varchar(30),                           -- 최종 교체 사유: SHORT/STOP/NO_TARGET/MANUAL
  ADD COLUMN swapped_on          timestamp;                             -- 최종 자동교체 시각

COMMENT ON COLUMN main_content_product.swap_mode IS '교체 모드(AUTO=자동교체 대상, MD_FIXED=MD 수동 고정)';
COMMENT ON COLUMN main_content_product.displayed_hero_code IS '앱에 실제 노출되는 히어로코드(그룹코드 차순위 폴백 결과)';

-- 2) 자동교체 실행 이력/되돌리기 원장 (신규)
CREATE TABLE main_content_swap_log (
  swap_log_id        bigserial    PRIMARY KEY,          -- 이력 ID
  content_id         varchar(100) NOT NULL,             -- 대상 편성 섹션(BEST 등)
  original_hero_code varchar(100) NOT NULL,             -- 원본 히어로코드(되돌리기 복원값)
  replaced_hero_code varchar(100),                      -- 교체된 코드(NULL=바꿀 대상 없음)
  group_id           integer,                           -- 폴백에 사용한 그룹코드
  net_price          bigint,                            -- 교체 시점 후보 넷 공급가(최저가 근거)
  reason             varchar(30)  NOT NULL,             -- SHORT/STOP/NO_TARGET/ROLLBACK
  status             varchar(10)  NOT NULL DEFAULT 'SUCCESS', -- SUCCESS/FAIL/REVERTED
  executed_by        varchar(20)  NOT NULL DEFAULT 'AUTO_SWAP', -- 실행 주체(시스템/ MD ID)
  executed_on        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE main_content_swap_log IS 'BEST 자동교체 실행 이력 및 되돌리기 원장';

CREATE INDEX idx_mcsl_content ON main_content_swap_log (content_id, executed_on DESC);
CREATE INDEX idx_mcsl_status  ON main_content_swap_log (status);
04

화면 상세 — 세부 표·규칙

목록 외 품목표·모달·규칙 등 세부 화면

자동교체 규칙 매트릭스 (폴백·충돌·예외)
케이스판정 조건자동 처리MD 충돌 시
쇼트 폴백원본 sold_out=true같은 group_id 최저가(net_price) 차순위 노출MD 고정이면 폴백 스킵
중단 폴백use_yn≠'U' 또는 long_term_sold_out그룹 내 차순위 노출MD 고정이면 스킵
대상 없음그룹 내 판매가능 후보 0원본 유지 + 슬롯 숨김 + MD 알림자동 삭제 금지
품절 오판 방지를 위해 판정 신호 조합(AND/OR)은 착수 전 확정 필요(🔴 결정 대기).
자동교체 실행 로그 · 되돌리기 모달
실행 일시슬롯원본 → 교체사유상태실행자되돌리기
07-30 04:00BEST 1위H0004821 → H0009912SHORTSUCCESSAUTO_SWAP[원복]
07-30 04:00BEST 3위H0005510 → —NO_TARGETFAILAUTO_SWAP[알림 확인]
07-29 11:20BEST 5위H0002210 → H0002255SHORTREVERTEDmd_kim복원 완료
원본 코드는 로그에 항상 보존되어 1클릭 원복 가능. 실패(NO_TARGET)는 되돌리기 대신 MD 알림 확인 액션.