o2o-negosium-original/schedules/anchoring/schema.sql
민헌 e1940c69aa feat(anchoring): 가격구간을 자릿수 계단식 사다리(46칸)로 재설계 (TODO 1)
균일 3,000원 × 33,334칸 → 자릿수 사다리 46칸. 폭 = 구간 상한의 10%
(선행 자릿수 밴드): [0, 1,000) 통일 1칸 + 자릿수(1천~1억, 5개)당 9칸
— "1천 원대·2천 원대 … 9천만 원대". 1억 초과는 마지막 인덱스(45) 클램프.

- 고가 구간의 무의미한 3,000원 해상도 제거 → 칸당 표본 밀도 대폭 개선
  (희소 칸 이월 문제 구조적 완화), 정적 테이블 2MB → 3KB
- 사다리 단일 소스 = constants.UPPER_BOUNDS(생성식), json 은 기동 시 대조 검증
- calc_bracket_index: // 3000 → bisect_right (좌폐우개 경계 규약 동일 유지)
- 조정 이력 0건 시점 적용 — 인덱스 재매핑/마이그레이션 없음. DB 스키마 무변경
- 골든 테스트 재작성(경계·자릿수 진입·클램프) + 사다리 형태 검증 추가 — 16 passed
- 문서 5종·인수인계·README·스키마 주석 동기화, TODO 과제 1 완료 처리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 20:08:23 +09:00

40 lines
2.6 KiB
SQL

-- ============================================================
-- anchoring 모듈 DDL (모듈 소유 — postgres-init 에 두지 않는다)
-- 적용: psql -h <host> -U <user> -d negosium_db -f schema.sql
-- 신규 DB 구축 순서: postgres-init/01~04 → 이 파일
-- 규범: docs/개발용.md §6. IF NOT EXISTS 라 재적용 안전.
-- 컨벤션: FK/CHECK/PG ENUM 없음, SMALLINT 코드, uuid 키, TIMESTAMPTZ(UTC).
-- ============================================================
\connect negosium_db
CREATE SCHEMA IF NOT EXISTS anchoring;
-- 앵커링 값 조정 이력. append-only — UPDATE/DELETE 금지(§5), updated_at/deleted 의도적 생략.
CREATE TABLE IF NOT EXISTS anchoring.rate_adjustments (
id BIGSERIAL PRIMARY KEY,
company_id uuid NOT NULL, -- 테넌트(partner.items.company_id 유래)
supplier_type SMALLINT NOT NULL, -- 1=유통(δ20) 2=제조(δ10) 3=총판(δ15)
price_bracket_index INTEGER NOT NULL, -- 가격구간 0..45 자릿수 사다리 (앱 보장)
nego_count INTEGER NOT NULL, -- 유효 표본 수 n (>=10, 앱 보장)
success_count INTEGER NOT NULL, -- n 중 성공(BID_SUCCESS) 건수
anchor_rate_before SMALLINT NOT NULL, -- 직전 값(‰) (이력 없었으면 정적 테이블 시작값)
anchor_rate_after SMALLINT NOT NULL, -- 조정 후 값(‰), clamp [10,200] 앱 보장
consumed_session_ids JSONB NOT NULL, -- 소비한 세션 uuid 배열(창 박제 — 재현성·감사)
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 현재 값 조회 최적화: 칸별 최신 조정
CREATE INDEX IF NOT EXISTS idx_rate_adjustments_cell
ON anchoring.rate_adjustments (company_id, supplier_type, price_bracket_index, id DESC);
-- 선행요건 §13 + 소비 마킹. target_anchoring_price 는 기존 컬럼(negodata 가 생성 시 박제).
ALTER TABLE negotiation.sessions
ADD COLUMN IF NOT EXISTS anchor_rate_permille SMALLINT NULL, -- 제안 당시 rate(‰) 박제
ADD COLUMN IF NOT EXISTS last_offered_price BIGINT NULL, -- 협력사 마지막 제시가(원) — 가격 입력마다 backend 가 갱신, 종료 후 불변. NULL=가격 흔적 없음(표본 제외)
ADD COLUMN IF NOT EXISTS anchoring_adjustment_id BIGINT NULL; -- NULL=미처리 0=제외확정 >0=소비한 조정 id
-- 배치 스캔 최적화: 미처리 세션만 (부분 인덱스)
CREATE INDEX IF NOT EXISTS idx_sessions_anchoring_pending
ON negotiation.sessions (qt_type, status)
WHERE anchoring_adjustment_id IS NULL AND deleted = false;