topfans/backend/scripts/migrations/migrate_laser_card_v3_tables.sql

221 lines
9.2 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- =============================================================================
-- 镭射卡重构 v3 — 业务表迁移Phase 1
-- 文档docs/specs/2026-05-25-laser-card-refactor-design.md §6.56.11
-- 创建日期2026-05-27
--
-- 说明:
-- 1. 本期新增 laser_card_templates / laser_card_instances / laser_card_operation_logs
-- 2. 不对 assets / asset_registry 做 ALTER品类+工艺走 assets.tags 约定,见 §6.3.1
-- 3. 依赖public.assets、public.mint_orders、public.materials 已存在
-- 执行psql -U <user> -d <db> -f migrate_laser_card_v3_tables.sql
-- 备注§4 含全部 COMMENT ON表/字段/索引/约束),可重复执行;已建表无备注时可单独跑 migrate_laser_card_v3_comments_only.sql
-- =============================================================================
BEGIN;
-- -----------------------------------------------------------------------------
-- 1. laser_card_templates
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_templates (
id BIGSERIAL PRIMARY KEY,
template_code VARCHAR(64) NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'published',
version INT NOT NULL DEFAULT 1,
thumbnail_oss_key VARCHAR(255),
backdrop_options JSONB NOT NULL DEFAULT '[]'::jsonb,
render_config JSONB NOT NULL DEFAULT '{}'::jsonb,
engine_min_version VARCHAR(32) NOT NULL DEFAULT 'compositor-1.0.0',
sort_order INT NOT NULL DEFAULT 0,
star_id BIGINT NOT NULL DEFAULT 0,
created_by BIGINT NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
deleted_at BIGINT
);
CREATE UNIQUE INDEX IF NOT EXISTS uk_lct_code_version
ON public.laser_card_templates (template_code, version)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_lct_status_star
ON public.laser_card_templates (status, star_id)
WHERE deleted_at IS NULL;
-- -----------------------------------------------------------------------------
-- 2. laser_card_instances
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_instances (
id BIGSERIAL PRIMARY KEY,
instance_no VARCHAR(32) NOT NULL,
instance_ulid VARCHAR(40) NOT NULL,
template_id BIGINT NOT NULL,
template_code VARCHAR(64) NOT NULL,
template_version INT NOT NULL,
owner_user_id BIGINT NOT NULL,
star_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'rendered',
client_request_id VARCHAR(64),
render_config JSONB,
materials_snapshot JSONB NOT NULL DEFAULT '[]'::jsonb,
composite_oss_key VARCHAR(255),
composite_material_id BIGINT,
asset_id BIGINT,
mint_order_id VARCHAR(100),
idempotency_key VARCHAR(64),
version INT NOT NULL DEFAULT 1,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
deleted_at BIGINT,
CONSTRAINT fk_lci_template
FOREIGN KEY (template_id) REFERENCES public.laser_card_templates (id),
CONSTRAINT fk_lci_asset
FOREIGN KEY (asset_id) REFERENCES public.assets (id) ON DELETE SET NULL,
CONSTRAINT fk_lci_mint_order
FOREIGN KEY (mint_order_id) REFERENCES public.mint_orders (order_id) ON DELETE SET NULL,
CONSTRAINT fk_lci_composite_material
FOREIGN KEY (composite_material_id) REFERENCES public.materials (id) ON DELETE SET NULL,
CONSTRAINT chk_lci_status
CHECK (status IN ('rendered', 'minting', 'minted'))
);
CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_instance_no
ON public.laser_card_instances (instance_no);
CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_instance_ulid
ON public.laser_card_instances (instance_ulid);
CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_user_client_req
ON public.laser_card_instances (owner_user_id, client_request_id)
WHERE deleted_at IS NULL AND client_request_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_lci_owner_status
ON public.laser_card_instances (owner_user_id, status)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_lci_asset
ON public.laser_card_instances (asset_id)
WHERE asset_id IS NOT NULL AND deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_lci_star_created
ON public.laser_card_instances (star_id, created_at DESC)
WHERE deleted_at IS NULL;
-- -----------------------------------------------------------------------------
-- 3. laser_card_operation_logs
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_operation_logs (
id BIGSERIAL PRIMARY KEY,
instance_id BIGINT NOT NULL,
instance_no VARCHAR(32) NOT NULL,
operator_user_id BIGINT NOT NULL,
action VARCHAR(50) NOT NULL,
status_before VARCHAR(20),
status_after VARCHAR(20),
request_id VARCHAR(64),
payload_json JSONB,
result_json JSONB,
ip_address VARCHAR(45),
user_agent VARCHAR(255),
latency_ms INT,
err_code VARCHAR(32),
created_at BIGINT NOT NULL,
CONSTRAINT fk_lclog_instance
FOREIGN KEY (instance_id) REFERENCES public.laser_card_instances (id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_lclog_instance_time
ON public.laser_card_operation_logs (instance_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_lclog_operator_time
ON public.laser_card_operation_logs (operator_user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_lclog_action_time
ON public.laser_card_operation_logs (action, created_at DESC);
-- -----------------------------------------------------------------------------
-- 4. 表 / 字段 / 索引 / 约束备注COMMENT可重复执行
-- -----------------------------------------------------------------------------
\ir migrate_laser_card_v3_comments.sql
-- -----------------------------------------------------------------------------
-- 5. 种子数据5 套预设(与 frontend/utils/laser-card/laserPresets.js 对齐)
-- -----------------------------------------------------------------------------
INSERT INTO public.laser_card_templates (
template_code,
name,
status,
version,
backdrop_options,
render_config,
engine_min_version,
sort_order,
star_id,
created_by,
created_at,
updated_at
)
SELECT
v.template_code,
v.name,
'published',
1,
v.backdrop_options::jsonb,
v.render_config::jsonb,
'compositor-1.0.0',
v.sort_order,
0,
0,
(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint,
(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint
FROM (
VALUES
(
'dream',
'梦幻',
0,
'[{"id":"liquidBlue","label":"液态蓝","oss_key":"static/laser-bg/laser-bg-1.png"}]',
'{"style":"dream","angle":36,"beam":"prism","backdrop":"liquidBlue","laserStrength":78,"diffractionDensity":68,"marbleFlow":52,"beamIntensity":92,"grainDensity":82}'
),
(
'classic',
'经典',
1,
'[{"id":"liquidLavender","label":"液态紫","oss_key":"static/laser-bg/laser-bg-2.png"}]',
'{"style":"classic","angle":24,"beam":"aurora","backdrop":"liquidLavender","laserStrength":85,"diffractionDensity":74,"marbleFlow":38,"beamIntensity":96,"grainDensity":76}'
),
(
'holoFull',
'全息',
2,
'[{"id":"liquidPearl","label":"液态珍珠","oss_key":"static/laser-bg/laser-bg-3.png"}]',
'{"style":"holoFull","angle":58,"beam":"neon","backdrop":"liquidPearl","laserStrength":90,"diffractionDensity":80,"marbleFlow":58,"beamIntensity":100,"grainDensity":88}'
),
(
'ice',
'冰蓝',
3,
'[{"id":"liquidBlue","label":"液态蓝","oss_key":"static/laser-bg/laser-bg-1.png"}]',
'{"style":"ice","angle":46,"beam":"white","backdrop":"liquidBlue","laserStrength":72,"diffractionDensity":60,"marbleFlow":44,"beamIntensity":88,"grainDensity":74}'
),
(
'sunset',
'晚霞',
4,
'[{"id":"liquidLavender","label":"液态紫","oss_key":"static/laser-bg/laser-bg-2.png"}]',
'{"style":"sunset","angle":30,"beam":"sunset","backdrop":"liquidLavender","laserStrength":82,"diffractionDensity":66,"marbleFlow":50,"beamIntensity":94,"grainDensity":84}'
)
) AS v (template_code, name, sort_order, backdrop_options, render_config)
WHERE NOT EXISTS (
SELECT 1
FROM public.laser_card_templates t
WHERE t.template_code = v.template_code
AND t.version = 1
AND t.deleted_at IS NULL
);
COMMIT;