전 저장소 리팩터링과 SSOT 정비

This commit is contained in:
Yun Chan 2026-07-15 21:31:30 +09:00
parent 14ecbd4e7d
commit 3dfddcac6f
173 changed files with 19679 additions and 6952 deletions

View file

@ -174,6 +174,7 @@ CREATE TABLE IF NOT EXISTS app.sessions (
session_no INT, -- 케이스 내 N번째 회기
theory_mode TEXT, -- 'humanistic'|'cbt'|'integrative'
stage_path JSONB NOT NULL DEFAULT '[]', -- 단계 전이 이력
session_goals JSONB NOT NULL DEFAULT '[]', -- 이번 회기 목표 단계(학습자 선택, 최대 4)
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
ended_at TIMESTAMPTZ,
FOREIGN KEY (persona_id, persona_version)
@ -189,6 +190,9 @@ CREATE TABLE IF NOT EXISTS app.session_review_status (
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','viewed','closed')),
note TEXT NOT NULL DEFAULT '',
worksheet_status TEXT NOT NULL DEFAULT 'pending',
worksheet_note TEXT NOT NULL DEFAULT '',
worksheet_reviewed_at TIMESTAMPTZ,
reviewed_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

View file

@ -321,6 +321,29 @@ CREATE POLICY p_alternative_utterance_delete ON app.alternative_utterance FOR DE
OR app.current_role_name() IN ('admin','instructor')
);
-- ── safety_events: AI/교수자/관리자와 본인 회기의 학습자만 접근 ──
ALTER TABLE app.safety_events ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS p_safety_events_select ON app.safety_events;
DROP POLICY IF EXISTS p_safety_events_insert ON app.safety_events;
CREATE POLICY p_safety_events_select ON app.safety_events FOR SELECT USING (
app.is_ai_context()
OR app.current_role_name() IN ('admin','instructor')
OR EXISTS (
SELECT 1 FROM app.sessions s
WHERE s.id = app.safety_events.session_id
AND s.learner_id = app.current_uid()
)
);
CREATE POLICY p_safety_events_insert ON app.safety_events FOR INSERT WITH CHECK (
app.is_ai_context()
OR app.current_role_name() IN ('admin','instructor')
OR EXISTS (
SELECT 1 FROM app.sessions s
WHERE s.id = app.safety_events.session_id
AND s.learner_id = app.current_uid()
)
);
-- ── case_profile / pinned_fact: 학습자=본인 케이스만(cross-trainee 누수 차단 T4) ──
ALTER TABLE app.case_profile ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS p_case_select ON app.case_profile;

View file

@ -161,6 +161,13 @@ CREATE INDEX IF NOT EXISTS idx_support_ticket_parent
ON app.support_ticket(parent_ticket_id)
WHERE parent_ticket_id IS NOT NULL;
ALTER TABLE app.session_review_status
ADD COLUMN IF NOT EXISTS worksheet_status TEXT NOT NULL DEFAULT 'pending';
ALTER TABLE app.session_review_status
ADD COLUMN IF NOT EXISTS worksheet_note TEXT NOT NULL DEFAULT '';
ALTER TABLE app.session_review_status
ADD COLUMN IF NOT EXISTS worksheet_reviewed_at TIMESTAMPTZ;
CREATE TABLE IF NOT EXISTS app.notification_event (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
kind TEXT NOT NULL CHECK (

View file

@ -135,13 +135,46 @@ CREATE TABLE IF NOT EXISTS app.live_coach_events (
session_id UUID NOT NULL REFERENCES app.sessions(id) ON DELETE CASCADE,
turn_seq INT NOT NULL CHECK (turn_seq >= 1),
stage TEXT NOT NULL,
event_type TEXT NOT NULL DEFAULT 'use'
CHECK (event_type IN ('use', 'recharge')),
credit_delta INT NOT NULL DEFAULT -1,
credit_balance INT,
reason TEXT,
learner_text_excerpt TEXT,
client_reply_excerpt TEXT,
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE app.live_coach_events
ADD COLUMN IF NOT EXISTS event_type TEXT NOT NULL DEFAULT 'use';
ALTER TABLE app.live_coach_events
ADD COLUMN IF NOT EXISTS credit_delta INT NOT NULL DEFAULT -1;
ALTER TABLE app.live_coach_events
ADD COLUMN IF NOT EXISTS credit_balance INT;
ALTER TABLE app.live_coach_events
ADD COLUMN IF NOT EXISTS reason TEXT;
UPDATE app.live_coach_events
SET event_type = 'use'
WHERE event_type IS NULL OR event_type NOT IN ('use', 'recharge');
UPDATE app.live_coach_events
SET credit_delta = CASE WHEN event_type = 'recharge' THEN 1 ELSE -1 END
WHERE credit_delta IS NULL;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'live_coach_events_event_type_check'
) THEN
ALTER TABLE app.live_coach_events
ADD CONSTRAINT live_coach_events_event_type_check
CHECK (event_type IN ('use', 'recharge'));
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_live_coach_events_session_turn
ON app.live_coach_events(session_id, turn_seq, created_at);
CREATE INDEX IF NOT EXISTS idx_live_coach_events_session_type
ON app.live_coach_events(session_id, event_type, created_at);
ALTER TABLE app.live_coach_events ENABLE ROW LEVEL SECURITY;