Merge branch 'main' into feature/negodata-lps
This commit is contained in:
commit
f97e407f7c
@ -12,7 +12,7 @@
|
||||
font-weight: 45 920;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url('/fonts/PretendardVariable.woff2') format('woff2-variations');
|
||||
src: url('/fonts/PretendardVariable.woff2') format('woff2');
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
30
postgres-init/alters/2026-07-10-card-columns.sql
Normal file
30
postgres-init/alters/2026-07-10-card-columns.sql
Normal file
@ -0,0 +1,30 @@
|
||||
-- 기존 DB 보정 — card.nego_cards / card.wild_cards 의 카드 개편 컬럼 보강(King 배포 안전용).
|
||||
-- 신규 DB 는 init.sql 의 테이블 정의에 이미 포함되어 있어 이 파일이 필요 없다(멱등이라 실행해도 무해).
|
||||
--
|
||||
-- 배경: 이 컬럼들은 init.sql 의 CREATE TABLE 에만 정의돼 있고 ALTER 커버리지가 없었다.
|
||||
-- CREATE TABLE IF NOT EXISTS 는 기존 테이블에 컬럼을 추가하지 못하므로, 카드 테이블이
|
||||
-- 이 컬럼들보다 먼저 생성된 (오래된) DB 에는 컬럼이 누락돼 있을 수 있다.
|
||||
-- 현재 코드가 이 컬럼들에 의존한다:
|
||||
-- - agent 표현층(ScriptNaturalizer)이 nego_cards.tone/strategy_type 를 읽음
|
||||
-- - negodata 카드 편집기가 edit_script(Slate JSON, 조건 전략 내용 포함)·tone·strategy_type 을 저장
|
||||
-- - 카드 전술(WC) 이 wild_cards.script 를 읽음(script 는 기준선부터 존재 → 여기선 개편 컬럼만 보강)
|
||||
-- 적용: psql -h <host> -p <port> -U <user> -f postgres-init/alters/2026-07-10-card-columns.sql
|
||||
|
||||
\connect negosium_db
|
||||
|
||||
-- 협상 카드(card.nego_cards) — 편집본 JSON · 용도 · 톤 · 전략
|
||||
ALTER TABLE card.nego_cards
|
||||
ADD COLUMN IF NOT EXISTS edit_script JSONB NULL, -- 편집된 스크립트(Slate JSON, 재편집·조건 전략 내용 보관)
|
||||
ADD COLUMN IF NOT EXISTS usage_type SMALLINT NOT NULL DEFAULT 1, -- CardUsageType: 1=공통 2=신규견적전용 3=재견적전용
|
||||
ADD COLUMN IF NOT EXISTS tone SMALLINT NULL, -- CardTone: 1=강경 2=정중 3=우호 4=중립 5=단호
|
||||
ADD COLUMN IF NOT EXISTS strategy_type SMALLINT NULL; -- CardStrategyType: 1=경쟁 2=수용 3=고수 4=협력 5=선점 6=종결
|
||||
|
||||
-- 와일드 카드(card.wild_cards) — 위 + 세부조항(condition/available/memo)
|
||||
ALTER TABLE card.wild_cards
|
||||
ADD COLUMN IF NOT EXISTS edit_script JSONB NULL,
|
||||
ADD COLUMN IF NOT EXISTS usage_type SMALLINT NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS tone SMALLINT NULL,
|
||||
ADD COLUMN IF NOT EXISTS strategy_type SMALLINT NULL,
|
||||
ADD COLUMN IF NOT EXISTS condition VARCHAR(255) NULL, -- 커스텀 와일드카드 상세 조건
|
||||
ADD COLUMN IF NOT EXISTS available BOOLEAN NOT NULL DEFAULT FALSE, -- 수동 활성화 여부
|
||||
ADD COLUMN IF NOT EXISTS memo VARCHAR(255) NULL; -- 자유 메모
|
||||
@ -670,3 +670,90 @@ UPDATE company.users SET last_accessed_at = now() WHERE last_accessed_a
|
||||
ALTER TABLE company.users ALTER COLUMN last_accessed_at SET DEFAULT now();
|
||||
UPDATE supplier.supplier_users SET last_accessed_at = now() WHERE last_accessed_at IS NULL;
|
||||
ALTER TABLE supplier.supplier_users ALTER COLUMN last_accessed_at SET DEFAULT now();
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- LPS (인터넷 최저가 검색) — 별도 database lps_db
|
||||
-- ============================================================
|
||||
-- lps-api / lps-worker 는 negosium_db 가 아니라 별도 database `lps_db`(같은 서버)에 접속한다.
|
||||
-- lps_db 가 없으면 컨테이너가 접속 실패로 재시작을 반복하므로 여기서 생성한다.
|
||||
-- 아래 테이블은 lps/common/database/model/models.py 와 정합해야 한다(프로덕션은 create_all 을
|
||||
-- 기동 시 호출하지 않으므로 이 DDL 이 신규 lps_db 의 스키마 소스다). 모델 변경 시 함께 갱신할 것.
|
||||
-- 이 섹션은 파일 맨 끝에 둔다 — 아래 \connect 로 접속이 lps_db 로 바뀌기 때문.
|
||||
|
||||
SELECT 'CREATE DATABASE lps_db'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'lps_db')\gexec
|
||||
|
||||
\connect lps_db
|
||||
|
||||
ALTER DATABASE lps_db SET timezone TO 'UTC';
|
||||
SET timezone TO 'UTC';
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- gen_random_uuid()
|
||||
|
||||
-- 작업 큐(PostgreSQL 을 큐로: 원자적 CAS claim + lease 소유권 + dead-letter)
|
||||
CREATE TABLE IF NOT EXISTS job (
|
||||
job_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
job_type SMALLINT NOT NULL, -- JobType
|
||||
status SMALLINT NOT NULL DEFAULT 1, -- JobStatus (1=PENDING)
|
||||
priority SMALLINT NOT NULL DEFAULT 100, -- 낮을수록 우선
|
||||
payload JSONB NOT NULL DEFAULT '{}'::jsonb, -- 잡 입력
|
||||
result JSONB NULL, -- 잡 출력(완료 시)
|
||||
dedupe_key VARCHAR(200) NULL, -- 활성 중복 방지 키(부분 유니크)
|
||||
attempts SMALLINT NOT NULL DEFAULT 0, -- 시도 횟수(claim 시 +1)
|
||||
max_attempts SMALLINT NOT NULL DEFAULT 3,
|
||||
run_after TIMESTAMPTZ NOT NULL DEFAULT now(), -- 이 시각 이후에만 claim(백오프)
|
||||
lease_until TIMESTAMPTZ NULL, -- 소유권 임대 만료(reaper 회수 기준)
|
||||
worker_id VARCHAR(80) NULL, -- 현재 점유 워커
|
||||
run_started_at TIMESTAMPTZ NULL, -- RUNNING 진입 시각
|
||||
last_error TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- onupdate 는 앱(ORM)에서 갱신
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_job_claim ON job (status, run_after, priority, created_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_job_lease ON job (status, lease_until);
|
||||
-- 활성 중복 방지: 같은 dedupe_key 는 PENDING/RUNNING(1,2) 중 하나만 존재 가능
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_job_dedupe_active
|
||||
ON job (dedupe_key) WHERE status IN (1, 2) AND dedupe_key IS NOT NULL;
|
||||
|
||||
-- 네거티브 캐시 — '검색해도 없더라'를 TTL 동안 기억(재검색 낭비 방지)
|
||||
CREATE TABLE IF NOT EXISTS search_negative (
|
||||
key VARCHAR(300) PRIMARY KEY, -- 보통 product_code(없으면 query)
|
||||
until TIMESTAMPTZ NOT NULL, -- 이 시각까지 not_found 로 간주
|
||||
reason VARCHAR(200) NULL, -- 종료 사유 메모(관측)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 상품별 최저가 스냅샷(트리거 기반) — 시계열 그래프용
|
||||
CREATE TABLE IF NOT EXISTS price_history (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
product_code VARCHAR(100) NOT NULL, -- 상품 식별(조회 키)
|
||||
job_id uuid NULL, -- 검색 잡 연결(추적)
|
||||
triggered_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- X축(검색 실행 시각)
|
||||
outcome VARCHAR(20) NOT NULL, -- found / not_found
|
||||
matched_count INTEGER NULL, -- AI 매칭 건수
|
||||
naver_lowest INTEGER NULL,
|
||||
naver_name VARCHAR(300) NULL,
|
||||
naver_url TEXT NULL,
|
||||
coupang_lowest INTEGER NULL,
|
||||
coupang_name VARCHAR(300) NULL,
|
||||
coupang_url TEXT NULL,
|
||||
final_lowest INTEGER NULL, -- 전체 최저가(Y축 핵심)
|
||||
final_source VARCHAR(20) NULL, -- 최종 최저가 소스
|
||||
by_mall JSONB NULL, -- 몰별 최저가 스냅샷(열린 스키마)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_price_history_product ON price_history (product_code, triggered_at);
|
||||
|
||||
-- 봇 감지 이력 — IP·요청순번·차단방식 축적(패턴 분석)
|
||||
CREATE TABLE IF NOT EXISTS bot_detection (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source VARCHAR(20) NOT NULL, -- coupang 등
|
||||
query VARCHAR(300) NULL, -- 감지 당시 검색어
|
||||
ip_request_no INTEGER NULL, -- 현재 IP로 몇 번째 요청이었나
|
||||
proxy_port INTEGER NULL, -- 사용 중이던 프록시 포트(=IP 세션)
|
||||
elapsed_sec INTEGER NULL, -- 브라우저 실행 후 경과(초)
|
||||
marker VARCHAR(120) NULL, -- 감지 근거(차단 페이지 마커)
|
||||
headless BOOLEAN NULL,
|
||||
html_len INTEGER NULL, -- 응답 길이(차단 페이지는 작음)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user