-- ============================================================================= -- 镭射卡重构 v3 — 业务表迁移(Phase 1)+ COMMENT + 种子数据 -- 合并版,避免 \ir 路径和 PowerShell 编码问题 -- ============================================================================= 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 ON TABLE public.laser_card_templates IS '镭射卡预设模板表。内置 5 套预设(dream/classic/holoFull/ice/sunset),与前端 laserPresets.js 对齐;本期只读种子,无管理端 CRUD。'; COMMENT ON COLUMN public.laser_card_templates.id IS '主键,自增'; COMMENT ON COLUMN public.laser_card_templates.template_code IS '业务编码,与五图 presetId 一致:dream | classic | holoFull | ice | sunset'; COMMENT ON COLUMN public.laser_card_templates.name IS 'C 端展示名称,如「梦幻」「全息」'; COMMENT ON COLUMN public.laser_card_templates.description IS '模板描述,可选'; COMMENT ON COLUMN public.laser_card_templates.status IS '发布状态:published(可用)| archived(下线)'; COMMENT ON COLUMN public.laser_card_templates.version IS '模板版本号;与 template_code 联合唯一(uk_lct_code_version)'; COMMENT ON COLUMN public.laser_card_templates.thumbnail_oss_key IS '列表缩略图 OSS 对象键,可选'; COMMENT ON COLUMN public.laser_card_templates.backdrop_options IS '底纹选项 JSON 数组,元素含 id/label/oss_key'; COMMENT ON COLUMN public.laser_card_templates.render_config IS '默认渲染参数 JSON:style/angle/beam/backdrop/laserStrength 等'; COMMENT ON COLUMN public.laser_card_templates.engine_min_version IS '最低客户端合成器版本,默认 compositor-1.0.0'; COMMENT ON COLUMN public.laser_card_templates.sort_order IS '模板列表排序,越小越靠前'; COMMENT ON COLUMN public.laser_card_templates.star_id IS '所属明星/星球 ID;0 表示全站通用模板'; COMMENT ON COLUMN public.laser_card_templates.created_by IS '创建人用户 ID;种子数据填 0'; COMMENT ON COLUMN public.laser_card_templates.created_at IS '创建时间,毫秒时间戳'; COMMENT ON COLUMN public.laser_card_templates.updated_at IS '更新时间,毫秒时间戳'; COMMENT ON COLUMN public.laser_card_templates.deleted_at IS '软删除时间,毫秒时间戳;NULL 表示未删除'; COMMENT ON INDEX public.uk_lct_code_version IS '同 template_code + version 在未删除行内唯一'; COMMENT ON INDEX public.idx_lct_status_star IS '按发布状态与 star_id 筛选模板列表'; COMMENT ON TABLE public.laser_card_instances IS '镭射卡业务实例表。一次「上传→五图选卡→铸造」对应一行;铸造前素材存 materials_snapshot,成功后回填 asset_id、mint_order_id。品类/工艺不写本表,见 assets.tags(cast:star_card + craft:laser)。'; COMMENT ON COLUMN public.laser_card_instances.id IS '主键,自增;对内使用'; COMMENT ON COLUMN public.laser_card_instances.instance_no IS '业务单号,UK;格式 LC + yyyyMMddHHmmss + 6 位随机'; COMMENT ON COLUMN public.laser_card_instances.instance_ulid IS '对外实例 ID,UK;如 lc_inst_01HXYZ...,API 路径参数'; COMMENT ON COLUMN public.laser_card_instances.template_id IS 'FK -> laser_card_templates.id,用户选中的预设'; COMMENT ON COLUMN public.laser_card_instances.template_code IS '模板编码冗余,避免列表 JOIN templates'; COMMENT ON COLUMN public.laser_card_instances.template_version IS '创建实例时锁定的模板版本快照'; COMMENT ON COLUMN public.laser_card_instances.owner_user_id IS '持有者用户 ID;鉴权须等于 JWT user_id'; COMMENT ON COLUMN public.laser_card_instances.star_id IS '多星隔离:所属明星/星球 ID'; COMMENT ON COLUMN public.laser_card_instances.status IS '状态机:rendered(已选卡未铸造)| minting(铸造中)| minted(已完成)'; COMMENT ON COLUMN public.laser_card_instances.client_request_id IS '客户端幂等 UUID;与 owner_user_id 联合唯一'; COMMENT ON COLUMN public.laser_card_instances.render_config IS '用户最终渲染参数 JSON 快照,可选'; COMMENT ON COLUMN public.laser_card_instances.materials_snapshot IS '铸造前素材 OSS 清单 JSON 数组。每项字段:role(source|cutout|backdrop|composite)、oss_key、hash、width、height、mime_type、file_size、preset_id'; COMMENT ON COLUMN public.laser_card_instances.composite_oss_key IS '用户选中的合成图 OSS key'; COMMENT ON COLUMN public.laser_card_instances.composite_material_id IS '铸造成功后回填 materials.id'; COMMENT ON COLUMN public.laser_card_instances.asset_id IS 'FK -> assets.id;铸造成功后回填,铸造前为 NULL'; COMMENT ON COLUMN public.laser_card_instances.mint_order_id IS 'FK -> mint_orders.order_id;发起铸造时关联'; COMMENT ON COLUMN public.laser_card_instances.idempotency_key IS '最后一次写操作的幂等键,可选'; COMMENT ON COLUMN public.laser_card_instances.version IS '乐观锁版本号,每次更新实例 +1'; COMMENT ON COLUMN public.laser_card_instances.created_at IS '创建时间,毫秒时间戳'; COMMENT ON COLUMN public.laser_card_instances.updated_at IS '更新时间,毫秒时间戳'; COMMENT ON COLUMN public.laser_card_instances.deleted_at IS '软删除时间,毫秒时间戳;NULL 表示未删除'; COMMENT ON INDEX public.uk_lci_instance_no IS '业务单号唯一'; COMMENT ON INDEX public.uk_lci_instance_ulid IS '对外实例 ID 唯一'; COMMENT ON INDEX public.uk_lci_user_client_req IS '同一用户下 client_request_id 幂等'; COMMENT ON INDEX public.idx_lci_owner_status IS '按用户与状态查实例列表'; COMMENT ON INDEX public.idx_lci_asset IS '按已铸造资产反查实例'; COMMENT ON INDEX public.idx_lci_star_created IS '按星球与时间倒序列表'; COMMENT ON TABLE public.laser_card_operation_logs IS '镭射卡操作审计流水表。只追加不更新;记录 create_instance / mint_start / mint_success / mint_fail 等。'; COMMENT ON COLUMN public.laser_card_operation_logs.id IS '主键,自增'; COMMENT ON COLUMN public.laser_card_operation_logs.instance_id IS 'FK -> laser_card_instances.id;实例删除时级联删除日志'; COMMENT ON COLUMN public.laser_card_operation_logs.instance_no IS '实例业务单号冗余,便于按单号检索'; COMMENT ON COLUMN public.laser_card_operation_logs.operator_user_id IS '操作人用户 ID'; COMMENT ON COLUMN public.laser_card_operation_logs.action IS '动作:create_instance | generate_variants | mint_start | mint_success | mint_fail'; COMMENT ON COLUMN public.laser_card_operation_logs.status_before IS '变更前实例 status,可选'; COMMENT ON COLUMN public.laser_card_operation_logs.status_after IS '变更后实例 status,可选'; COMMENT ON COLUMN public.laser_card_operation_logs.request_id IS '请求追踪 ID,可选'; COMMENT ON COLUMN public.laser_card_operation_logs.payload_json IS '请求摘要 JSON,禁止存图片二进制'; COMMENT ON COLUMN public.laser_card_operation_logs.result_json IS '响应摘要 JSON,可选'; COMMENT ON COLUMN public.laser_card_operation_logs.ip_address IS '客户端 IP,IPv4/IPv6'; COMMENT ON COLUMN public.laser_card_operation_logs.user_agent IS '客户端 User-Agent'; COMMENT ON COLUMN public.laser_card_operation_logs.latency_ms IS '接口耗时(毫秒),可选'; COMMENT ON COLUMN public.laser_card_operation_logs.err_code IS '失败时的业务错误码,可选'; COMMENT ON COLUMN public.laser_card_operation_logs.created_at IS '日志创建时间,毫秒时间戳'; COMMENT ON INDEX public.idx_lclog_instance_time IS '按实例查操作历史(时间倒序)'; COMMENT ON INDEX public.idx_lclog_operator_time IS '按操作人查历史'; COMMENT ON INDEX public.idx_lclog_action_time IS '按动作类型统计与排查'; COMMENT ON CONSTRAINT fk_lci_template ON public.laser_card_instances IS '关联所选镭射预设模板'; COMMENT ON CONSTRAINT fk_lci_asset ON public.laser_card_instances IS '铸造成功后的藏品 assets.id;删除资产时置 NULL'; COMMENT ON CONSTRAINT fk_lci_mint_order ON public.laser_card_instances IS '关联铸造订单 mint_orders.order_id'; COMMENT ON CONSTRAINT fk_lci_composite_material ON public.laser_card_instances IS '选中合成图在 materials 表中的主键'; COMMENT ON CONSTRAINT chk_lci_status ON public.laser_card_instances IS '实例状态枚举:rendered | minting | minted'; COMMENT ON CONSTRAINT fk_lclog_instance ON public.laser_card_operation_logs IS '关联镭射实例;实例删除时级联删除审计日志'; -- ============================================================================= -- 5. 种子数据:5 套预设 -- ============================================================================= 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;