-- 2026-05-01 -- 业务运行上下文与沙箱隔离。 BEGIN; CREATE TABLE IF NOT EXISTS biz.site_runtime_context ( site_id bigint PRIMARY KEY, mode character varying(20) NOT NULL DEFAULT 'live', sandbox_date date, sandbox_instance_id character varying(64), ai_mode character varying(20) NOT NULL DEFAULT 'live', status character varying(20) NOT NULL DEFAULT 'active', reason text, updated_by bigint, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT site_runtime_context_site_id_fkey FOREIGN KEY (site_id) REFERENCES biz.sites(site_id), CONSTRAINT site_runtime_context_mode_check CHECK (mode IN ('live', 'sandbox')), CONSTRAINT site_runtime_context_ai_mode_check CHECK (ai_mode IN ('live')), CONSTRAINT site_runtime_context_sandbox_check CHECK ( (mode = 'live' AND sandbox_date IS NULL AND sandbox_instance_id IS NULL) OR (mode = 'sandbox' AND sandbox_date IS NOT NULL AND sandbox_instance_id IS NOT NULL) ) ); COMMENT ON TABLE biz.site_runtime_context IS '门店业务运行上下文:live 使用真实日期,sandbox 使用指定业务日期并按实例隔离写入。'; COMMENT ON COLUMN biz.site_runtime_context.mode IS '运行模式:live / sandbox。'; COMMENT ON COLUMN biz.site_runtime_context.sandbox_date IS 'sandbox 模式下系统假设的业务日期。'; COMMENT ON COLUMN biz.site_runtime_context.sandbox_instance_id IS 'sandbox 模式写入隔离实例 ID。'; COMMENT ON COLUMN biz.site_runtime_context.ai_mode IS 'AI 调用模式;当前固定 live,沙箱也真实调用 DashScope。'; ALTER TABLE biz.coach_tasks ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.coach_task_transfer_log ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.recall_events ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.coach_task_history ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.ai_cache ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.ai_run_logs ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; ALTER TABLE biz.ai_trigger_jobs ADD COLUMN IF NOT EXISTS runtime_mode character varying(20) NOT NULL DEFAULT 'live', ADD COLUMN IF NOT EXISTS sandbox_instance_id character varying(64) NOT NULL DEFAULT 'live'; UPDATE biz.coach_tasks SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.coach_task_transfer_log SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.recall_events SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.coach_task_history SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.ai_cache SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.ai_run_logs SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; UPDATE biz.ai_trigger_jobs SET runtime_mode = 'live', sandbox_instance_id = 'live' WHERE sandbox_instance_id IS NULL; DROP INDEX IF EXISTS biz.idx_coach_tasks_site_assistant_member_type; CREATE UNIQUE INDEX IF NOT EXISTS idx_coach_tasks_runtime_unique_active ON biz.coach_tasks (site_id, assistant_id, member_id, task_type, runtime_mode, sandbox_instance_id) WHERE status = 'active'; DROP INDEX IF EXISTS biz.idx_recall_events_site_assistant_member_day; CREATE UNIQUE INDEX IF NOT EXISTS idx_recall_events_runtime_site_assistant_member_day ON biz.recall_events ( site_id, assistant_id, member_id, runtime_mode, sandbox_instance_id, (date_trunc('day', pay_time AT TIME ZONE 'Asia/Shanghai')) ); CREATE INDEX IF NOT EXISTS idx_coach_tasks_runtime_assistant_status ON biz.coach_tasks (site_id, runtime_mode, sandbox_instance_id, assistant_id, status); CREATE INDEX IF NOT EXISTS idx_ai_cache_runtime_lookup ON biz.ai_cache (cache_type, site_id, runtime_mode, sandbox_instance_id, target_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_ai_trigger_jobs_runtime_site ON biz.ai_trigger_jobs (site_id, runtime_mode, sandbox_instance_id, event_type, status); COMMIT; -- 回滚参考: -- BEGIN; -- DROP INDEX IF EXISTS biz.idx_ai_trigger_jobs_runtime_site; -- DROP INDEX IF EXISTS biz.idx_ai_cache_runtime_lookup; -- DROP INDEX IF EXISTS biz.idx_coach_tasks_runtime_assistant_status; -- DROP INDEX IF EXISTS biz.idx_recall_events_runtime_site_assistant_member_day; -- CREATE UNIQUE INDEX idx_recall_events_site_assistant_member_day ON biz.recall_events USING btree (site_id, assistant_id, member_id, (date_trunc('day', pay_time AT TIME ZONE 'Asia/Shanghai'))); -- DROP INDEX IF EXISTS biz.idx_coach_tasks_runtime_unique_active; -- CREATE UNIQUE INDEX idx_coach_tasks_site_assistant_member_type ON biz.coach_tasks USING btree (site_id, assistant_id, member_id, task_type) WHERE status = 'active'; -- ALTER TABLE biz.ai_trigger_jobs DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.ai_run_logs DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.ai_cache DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.coach_task_history DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.recall_events DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.coach_task_transfer_log DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- ALTER TABLE biz.coach_tasks DROP COLUMN IF EXISTS sandbox_instance_id, DROP COLUMN IF EXISTS runtime_mode; -- DROP TABLE IF EXISTS biz.site_runtime_context; -- COMMIT;