bibimbap/docs/badge-ddl.sql

91 lines
3.8 KiB
SQL

-- W4 유저 배지/평판. 멱등. db/apply-local-ddl.sh 로 실행 DB 비파괴 적용.
-- 신규 도메인(스토리지 0). 추가만, 파괴 없음. search_path=dev.
-- 1) badges (배지 카탈로그 — 정의 가능·확장 가능)
CREATE SEQUENCE IF NOT EXISTS "badges_id_seq";
CREATE TABLE IF NOT EXISTS "badges" (
"id" bigint DEFAULT nextval('badges_id_seq'::regclass) NOT NULL,
"badge_key" character varying(50) NOT NULL,
"display_name" character varying(100) NOT NULL,
"description" character varying(500),
"badge_type" character varying(30) NOT NULL,
"criteria_json" text,
"is_active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
PRIMARY KEY ("id")
);
ALTER SEQUENCE "badges_id_seq" OWNED BY "badges"."id";
CREATE UNIQUE INDEX IF NOT EXISTS "ux_badges_key" ON "badges" ("badge_key");
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'badges_type_check') THEN
ALTER TABLE "badges"
ADD CONSTRAINT "badges_type_check"
CHECK ("badge_type" IN ('REVIEWER', 'TECHNICIAN'));
END IF;
END
$$;
-- 2) user_badges (유저↔배지 보유. 자동/수동 부여 공통 기록)
CREATE SEQUENCE IF NOT EXISTS "user_badges_id_seq";
CREATE TABLE IF NOT EXISTS "user_badges" (
"id" bigint DEFAULT nextval('user_badges_id_seq'::regclass) NOT NULL,
"user_id" bigint NOT NULL,
"badge_key" character varying(50) NOT NULL,
"awarded_by" bigint,
"awarded_at" timestamp with time zone DEFAULT now() NOT NULL,
"revoked_at" timestamp with time zone,
"revoke_reason" character varying(500),
PRIMARY KEY ("id")
);
ALTER SEQUENCE "user_badges_id_seq" OWNED BY "user_badges"."id";
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'user_badges_user_id_fkey') THEN
ALTER TABLE "user_badges"
ADD CONSTRAINT "user_badges_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users" ("id");
END IF;
END
$$;
CREATE UNIQUE INDEX IF NOT EXISTS "ux_user_badges_active"
ON "user_badges" ("user_id", "badge_key") WHERE "revoked_at" IS NULL;
CREATE INDEX IF NOT EXISTS "idx_user_badges_user_active"
ON "user_badges" ("user_id") WHERE "revoked_at" IS NULL;
-- 3) reputation_events (평판 신호 감사로그 — append-only, 임계 판정 단일 소스)
CREATE SEQUENCE IF NOT EXISTS "reputation_events_id_seq";
CREATE TABLE IF NOT EXISTS "reputation_events" (
"id" bigint DEFAULT nextval('reputation_events_id_seq'::regclass) NOT NULL,
"user_id" bigint NOT NULL,
"event_type" character varying(40) NOT NULL,
"source_ref" character varying(100),
"weight" integer DEFAULT 1 NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
PRIMARY KEY ("id")
);
ALTER SEQUENCE "reputation_events_id_seq" OWNED BY "reputation_events"."id";
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'reputation_events_type_check') THEN
ALTER TABLE "reputation_events"
ADD CONSTRAINT "reputation_events_type_check"
CHECK ("event_type" IN ('REVIEW_WRITTEN', 'GAME_UPLOADED'));
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'reputation_events_user_id_fkey') THEN
ALTER TABLE "reputation_events"
ADD CONSTRAINT "reputation_events_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users" ("id");
END IF;
END
$$;
CREATE UNIQUE INDEX IF NOT EXISTS "ux_reputation_events_source"
ON "reputation_events" ("user_id", "event_type", "source_ref")
WHERE "source_ref" IS NOT NULL;
CREATE INDEX IF NOT EXISTS "idx_reputation_events_user_type"
ON "reputation_events" ("user_id", "event_type");