Initial AI manga platform

This commit is contained in:
www
2026-06-15 17:45:28 +08:00
commit 7a8191650f
267 changed files with 105987 additions and 0 deletions
@@ -0,0 +1,629 @@
-- CreateTable
CREATE TABLE `users` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`email` VARCHAR(191) NULL,
`phone` VARCHAR(50) NULL,
`password_hash` VARCHAR(255) NOT NULL,
`nickname` VARCHAR(100) NULL,
`avatar_url` VARCHAR(500) NULL,
`role` VARCHAR(50) NOT NULL DEFAULT 'user',
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`wechat_openid` VARCHAR(191) NULL,
`last_login_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `users_email_key`(`email`),
UNIQUE INDEX `users_phone_key`(`phone`),
UNIQUE INDEX `users_wechat_openid_key`(`wechat_openid`),
INDEX `users_role_status_idx`(`role`, `status`),
INDEX `users_created_at_idx`(`created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `projects` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`title` VARCHAR(255) NULL,
`input_mode` VARCHAR(50) NOT NULL,
`genre` VARCHAR(100) NULL,
`style_code` VARCHAR(100) NULL,
`output_type` VARCHAR(50) NULL,
`target_episode_count` INTEGER NULL,
`episode_duration` INTEGER NULL,
`status` VARCHAR(80) NOT NULL DEFAULT 'draft',
`copyright_status` VARCHAR(80) NOT NULL DEFAULT 'pending',
`payment_status` VARCHAR(80) NOT NULL DEFAULT 'unpaid',
`quality_level` VARCHAR(50) NULL,
`is_long_series` BOOLEAN NOT NULL DEFAULT false,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
`completed_at` DATETIME(3) NULL,
INDEX `projects_user_id_status_idx`(`user_id`, `status`),
INDEX `projects_genre_status_idx`(`genre`, `status`),
INDEX `projects_created_at_idx`(`created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `novel_sources` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`source_type` VARCHAR(50) NOT NULL,
`title` VARCHAR(255) NULL,
`author_name` VARCHAR(100) NULL,
`raw_asset_id` BIGINT NULL,
`raw_text` LONGTEXT NULL,
`clean_text` LONGTEXT NULL,
`word_count` INTEGER NULL,
`chapter_count` INTEGER NULL,
`parse_status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`parse_report` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `novel_sources_project_id_idx`(`project_id`),
INDEX `novel_sources_parse_status_idx`(`parse_status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `novel_chapters` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`chapter_no` INTEGER NOT NULL,
`title` VARCHAR(255) NULL,
`content` LONGTEXT NOT NULL,
`summary` TEXT NULL,
`visual_summary` TEXT NULL,
`word_count` INTEGER NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `novel_chapters_project_id_chapter_no_idx`(`project_id`, `chapter_no`),
INDEX `novel_chapters_project_id_status_idx`(`project_id`, `status`),
UNIQUE INDEX `novel_chapters_novel_source_id_chapter_no_key`(`novel_source_id`, `chapter_no`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `copyright_records` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`user_id` BIGINT NOT NULL,
`authorization_type` VARCHAR(50) NOT NULL,
`statement_text` TEXT NOT NULL,
`ip` VARCHAR(80) NULL,
`user_agent` TEXT NULL,
`confirmed_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `copyright_records_project_id_idx`(`project_id`),
INDEX `copyright_records_user_id_idx`(`user_id`),
INDEX `copyright_records_authorization_type_idx`(`authorization_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `story_bibles` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`title` VARCHAR(255) NULL,
`logline` TEXT NULL,
`main_plot` TEXT NULL,
`core_conflict` TEXT NULL,
`selling_points` TEXT NULL,
`tone` VARCHAR(100) NULL,
`world_summary` TEXT NULL,
`ending_direction` TEXT NULL,
`taboo_rules` TEXT NULL,
`version` INTEGER NOT NULL DEFAULT 1,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `story_bibles_project_id_status_idx`(`project_id`, `status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `world_bibles` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`world_type` VARCHAR(100) NULL,
`setting_text` TEXT NULL,
`rules_text` TEXT NULL,
`power_system` TEXT NULL,
`social_structure` TEXT NULL,
`time_period` TEXT NULL,
`visual_rules` TEXT NULL,
`forbidden_rules` TEXT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `world_bibles_project_id_status_idx`(`project_id`, `status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `characters` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`alias_names` JSON NULL,
`role_type` VARCHAR(50) NOT NULL,
`gender_label` VARCHAR(50) NULL,
`age_group` VARCHAR(50) NULL,
`identity_desc` TEXT NULL,
`appearance_desc` TEXT NULL,
`face_desc` TEXT NULL,
`hair_desc` TEXT NULL,
`eye_desc` TEXT NULL,
`body_desc` TEXT NULL,
`costume_rules` TEXT NULL,
`special_props` TEXT NULL,
`personality_desc` TEXT NULL,
`speech_style` TEXT NULL,
`relationship_desc` TEXT NULL,
`character_arc` TEXT NULL,
`negative_rules` TEXT NULL,
`anchor_asset_id` BIGINT NULL,
`importance_level` INTEGER NOT NULL DEFAULT 0,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `characters_project_id_role_type_idx`(`project_id`, `role_type`),
INDEX `characters_project_id_status_idx`(`project_id`, `status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `character_images` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`asset_id` BIGINT NULL,
`image_type` VARCHAR(50) NOT NULL,
`prompt_text` TEXT NULL,
`negative_prompt` TEXT NULL,
`is_anchor` BOOLEAN NOT NULL DEFAULT false,
`quality_score` DECIMAL(5, 2) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `character_images_project_id_idx`(`project_id`),
INDEX `character_images_character_id_image_type_idx`(`character_id`, `image_type`),
INDEX `character_images_character_id_is_anchor_idx`(`character_id`, `is_anchor`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `character_memories` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`memory_type` VARCHAR(50) NOT NULL,
`content` TEXT NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `character_memories_project_id_character_id_idx`(`project_id`, `character_id`),
INDEX `character_memories_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `character_memories_memory_type_idx`(`memory_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `episodes` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_no` INTEGER NOT NULL,
`source_chapter_ids` JSON NULL,
`title` VARCHAR(255) NULL,
`summary` TEXT NULL,
`opening_hook` TEXT NULL,
`middle_conflict` TEXT NULL,
`ending_hook` TEXT NULL,
`target_duration` INTEGER NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `episodes_project_id_status_idx`(`project_id`, `status`),
UNIQUE INDEX `episodes_project_id_episode_no_key`(`project_id`, `episode_no`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `episode_scripts` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NOT NULL,
`script_text` LONGTEXT NULL,
`narration_text` LONGTEXT NULL,
`dialogue_json` JSON NULL,
`version` INTEGER NOT NULL DEFAULT 1,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `episode_scripts_project_id_idx`(`project_id`),
INDEX `episode_scripts_episode_id_version_idx`(`episode_id`, `version`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `storyboard_shots` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NOT NULL,
`shot_no` INTEGER NOT NULL,
`scene_name` VARCHAR(255) NULL,
`location_desc` TEXT NULL,
`characters_json` JSON NULL,
`visual_desc` TEXT NULL,
`action_desc` TEXT NULL,
`dialogue_text` TEXT NULL,
`narration_text` TEXT NULL,
`camera_motion` VARCHAR(100) NULL,
`effect_type` VARCHAR(100) NULL,
`duration` DECIMAL(6, 2) NULL,
`prompt_text` TEXT NULL,
`negative_prompt` TEXT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `storyboard_shots_project_id_episode_id_shot_no_idx`(`project_id`, `episode_id`, `shot_no`),
INDEX `storyboard_shots_project_id_status_idx`(`project_id`, `status`),
UNIQUE INDEX `storyboard_shots_episode_id_shot_no_key`(`episode_id`, `shot_no`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `shot_images` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`shot_id` BIGINT NOT NULL,
`asset_id` BIGINT NULL,
`image_type` VARCHAR(50) NOT NULL DEFAULT 'preview',
`prompt_text` TEXT NULL,
`negative_prompt` TEXT NULL,
`quality_score` DECIMAL(5, 2) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `shot_images_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `shot_images_shot_id_image_type_idx`(`shot_id`, `image_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `plot_memories` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`chapter_id` BIGINT NULL,
`memory_type` VARCHAR(50) NOT NULL,
`content` TEXT NOT NULL,
`importance_level` INTEGER NOT NULL DEFAULT 0,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `plot_memories_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `plot_memories_project_id_memory_type_idx`(`project_id`, `memory_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `plot_threads` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`thread_name` VARCHAR(255) NOT NULL,
`thread_type` VARCHAR(80) NOT NULL,
`description` TEXT NULL,
`start_episode_no` INTEGER NULL,
`expected_resolve_episode_no` INTEGER NULL,
`resolved_episode_no` INTEGER NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'open',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `plot_threads_project_id_status_idx`(`project_id`, `status`),
INDEX `plot_threads_project_id_thread_type_idx`(`project_id`, `thread_type`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `continuity_checks` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`check_type` VARCHAR(80) NOT NULL,
`result_status` VARCHAR(50) NOT NULL,
`issue_text` TEXT NULL,
`suggestion_text` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `continuity_checks_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `continuity_checks_result_status_idx`(`result_status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `assets` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NULL,
`project_id` BIGINT NULL,
`asset_type` VARCHAR(50) NOT NULL,
`file_path` VARCHAR(500) NOT NULL,
`file_url` VARCHAR(500) NULL,
`mime_type` VARCHAR(100) NULL,
`width` INTEGER NULL,
`height` INTEGER NULL,
`duration` DECIMAL(10, 2) NULL,
`size` BIGINT NULL,
`hash` VARCHAR(128) NULL,
`visibility` VARCHAR(30) NOT NULL DEFAULT 'private',
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `assets_project_id_asset_type_idx`(`project_id`, `asset_type`),
INDEX `assets_user_id_asset_type_idx`(`user_id`, `asset_type`),
INDEX `assets_hash_idx`(`hash`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `render_tasks` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`shot_id` BIGINT NULL,
`task_type` VARCHAR(80) NOT NULL,
`provider_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`input_json` JSON NULL,
`input_hash` VARCHAR(128) NULL,
`idempotency_key` VARCHAR(191) NULL,
`output_asset_id` BIGINT NULL,
`provider_request_id` VARCHAR(255) NULL,
`retry_count` INTEGER NOT NULL DEFAULT 0,
`max_retry` INTEGER NOT NULL DEFAULT 0,
`cost_estimate` DECIMAL(12, 4) NULL,
`cost_actual` DECIMAL(12, 4) NULL,
`error_code` VARCHAR(100) NULL,
`error_message` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`started_at` DATETIME(3) NULL,
`finished_at` DATETIME(3) NULL,
UNIQUE INDEX `render_tasks_idempotency_key_key`(`idempotency_key`),
INDEX `render_tasks_project_id_status_idx`(`project_id`, `status`),
INDEX `render_tasks_task_type_status_idx`(`task_type`, `status`),
INDEX `render_tasks_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `render_tasks_input_hash_idx`(`input_hash`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `provider_configs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`provider_type` VARCHAR(80) NOT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`display_name` VARCHAR(100) NULL,
`mode` VARCHAR(50) NOT NULL DEFAULT 'mock',
`model_name` VARCHAR(100) NULL,
`config_json` JSON NULL,
`fallback_provider_id` BIGINT NULL,
`is_enabled` BOOLEAN NOT NULL DEFAULT true,
`priority` INTEGER NOT NULL DEFAULT 0,
`rate_limit_json` JSON NULL,
`cost_rule_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `provider_configs_provider_type_is_enabled_idx`(`provider_type`, `is_enabled`),
UNIQUE INDEX `provider_configs_provider_type_provider_code_key`(`provider_type`, `provider_code`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `provider_logs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`provider_id` BIGINT NULL,
`task_id` BIGINT NULL,
`project_id` BIGINT NULL,
`provider_type` VARCHAR(80) NOT NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(100) NULL,
`request_json` JSON NULL,
`response_json` JSON NULL,
`input_size` INTEGER NULL,
`output_size` INTEGER NULL,
`cost_estimate` DECIMAL(12, 4) NULL,
`cost_actual` DECIMAL(12, 4) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'success',
`error_code` VARCHAR(100) NULL,
`error_message` TEXT NULL,
`started_at` DATETIME(3) NULL,
`finished_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `provider_logs_provider_type_status_idx`(`provider_type`, `status`),
INDEX `provider_logs_project_id_idx`(`project_id`),
INDEX `provider_logs_task_id_idx`(`task_id`),
INDEX `provider_logs_created_at_idx`(`created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `orders` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`project_id` BIGINT NULL,
`order_no` VARCHAR(100) NOT NULL,
`package_code` VARCHAR(100) NULL,
`amount` DECIMAL(12, 2) NOT NULL DEFAULT 0,
`currency` VARCHAR(20) NOT NULL DEFAULT 'CNY',
`payment_method` VARCHAR(50) NULL,
`payment_status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`paid_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `orders_order_no_key`(`order_no`),
INDEX `orders_user_id_payment_status_idx`(`user_id`, `payment_status`),
INDEX `orders_project_id_idx`(`project_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `quota_accounts` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`total_quota` DECIMAL(12, 2) NOT NULL DEFAULT 0,
`available_quota` DECIMAL(12, 2) NOT NULL DEFAULT 0,
`frozen_quota` DECIMAL(12, 2) NOT NULL DEFAULT 0,
`used_quota` DECIMAL(12, 2) NOT NULL DEFAULT 0,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `quota_accounts_user_id_key`(`user_id`),
INDEX `quota_accounts_status_idx`(`status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `quota_logs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`project_id` BIGINT NULL,
`task_id` BIGINT NULL,
`change_type` VARCHAR(50) NOT NULL,
`amount` DECIMAL(12, 2) NOT NULL,
`balance_after` DECIMAL(12, 2) NULL,
`reason` VARCHAR(255) NULL,
`metadata_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `quota_logs_user_id_created_at_idx`(`user_id`, `created_at`),
INDEX `quota_logs_project_id_idx`(`project_id`),
INDEX `quota_logs_task_id_idx`(`task_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `revision_requests` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`user_id` BIGINT NOT NULL,
`revision_type` VARCHAR(50) NOT NULL,
`target_type` VARCHAR(80) NULL,
`target_id` BIGINT NULL,
`description` TEXT NOT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `revision_requests_project_id_status_idx`(`project_id`, `status`),
INDEX `revision_requests_user_id_status_idx`(`user_id`, `status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `content_reviews` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NULL,
`user_id` BIGINT NULL,
`target_type` VARCHAR(80) NOT NULL,
`target_id` BIGINT NULL,
`review_type` VARCHAR(80) NOT NULL,
`result_status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`risk_level` VARCHAR(50) NULL,
`issue_text` TEXT NULL,
`suggestion_text` TEXT NULL,
`reviewer_id` BIGINT NULL,
`reviewed_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `content_reviews_project_id_result_status_idx`(`project_id`, `result_status`),
INDEX `content_reviews_target_type_target_id_idx`(`target_type`, `target_id`),
INDEX `content_reviews_review_type_result_status_idx`(`review_type`, `result_status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `case_showcases` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`user_id` BIGINT NULL,
`title` VARCHAR(255) NOT NULL,
`cover_asset_id` BIGINT NULL,
`video_asset_id` BIGINT NULL,
`authorization_status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`visibility` VARCHAR(30) NOT NULL DEFAULT 'private',
`sort_order` INTEGER NOT NULL DEFAULT 0,
`published_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `case_showcases_visibility_sort_order_idx`(`visibility`, `sort_order`),
INDEX `case_showcases_project_id_idx`(`project_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `analytics_events` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NULL,
`event_type` VARCHAR(80) NOT NULL,
`platform` VARCHAR(80) NULL,
`metric_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `analytics_events_project_id_idx`(`project_id`),
INDEX `analytics_events_episode_id_idx`(`episode_id`),
INDEX `analytics_events_event_type_created_at_idx`(`event_type`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `system_configs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`config_key` VARCHAR(191) NOT NULL,
`config_value` JSON NULL,
`description` TEXT NULL,
`is_public` BOOLEAN NOT NULL DEFAULT false,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `system_configs_config_key_key`(`config_key`),
INDEX `system_configs_is_public_idx`(`is_public`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `operation_logs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NULL,
`operator_role` VARCHAR(50) NULL,
`action` VARCHAR(100) NOT NULL,
`target_type` VARCHAR(80) NULL,
`target_id` BIGINT NULL,
`ip` VARCHAR(80) NULL,
`user_agent` TEXT NULL,
`metadata_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `operation_logs_user_id_created_at_idx`(`user_id`, `created_at`),
INDEX `operation_logs_target_type_target_id_idx`(`target_type`, `target_id`),
INDEX `operation_logs_action_created_at_idx`(`action`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,55 @@
ALTER TABLE `projects`
ADD COLUMN `output_mode` VARCHAR(50) NOT NULL DEFAULT 'image_manga' AFTER `output_type`,
ADD COLUMN `visual_mode` VARCHAR(100) NULL AFTER `output_mode`,
ADD COLUMN `video_generation_level` VARCHAR(50) NULL AFTER `visual_mode`;
ALTER TABLE `storyboard_shots`
ADD COLUMN `live_action_desc` TEXT NULL AFTER `negative_prompt`,
ADD COLUMN `actor_action` TEXT NULL AFTER `live_action_desc`,
ADD COLUMN `camera_instruction` TEXT NULL AFTER `actor_action`,
ADD COLUMN `performance_instruction` TEXT NULL AFTER `camera_instruction`,
ADD COLUMN `video_prompt` TEXT NULL AFTER `performance_instruction`,
ADD COLUMN `keyframe_asset_id` BIGINT NULL AFTER `video_prompt`,
ADD COLUMN `video_clip_asset_id` BIGINT NULL AFTER `keyframe_asset_id`,
ADD COLUMN `video_status` VARCHAR(50) NULL AFTER `video_clip_asset_id`;
CREATE TABLE `actor_profiles` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`actor_desc` TEXT NULL,
`appearance_rules` TEXT NULL,
`wardrobe_rules` TEXT NULL,
`performance_style` TEXT NULL,
`voice_style` TEXT NULL,
`reference_asset_ids` JSON NULL,
`anchor_asset_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `actor_profiles_project_id_character_id_key`(`project_id`, `character_id`),
INDEX `actor_profiles_project_id_status_idx`(`project_id`, `status`),
INDEX `actor_profiles_character_id_idx`(`character_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `video_clips` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NOT NULL,
`shot_id` BIGINT NOT NULL,
`provider_id` BIGINT NULL,
`input_asset_id` BIGINT NULL,
`output_asset_id` BIGINT NULL,
`duration` DECIMAL(6, 2) NULL,
`prompt_text` TEXT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`cost_actual` DECIMAL(12, 4) NULL,
`retry_count` INTEGER NOT NULL DEFAULT 0,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `video_clips_project_id_episode_id_idx`(`project_id`, `episode_id`),
INDEX `video_clips_shot_id_status_idx`(`shot_id`, `status`),
INDEX `video_clips_provider_id_idx`(`provider_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,88 @@
ALTER TABLE `video_clips`
ADD COLUMN `quality_status` VARCHAR(50) NULL AFTER `retry_count`,
ADD COLUMN `quality_score` DECIMAL(5, 2) NULL AFTER `quality_status`,
ADD COLUMN `quality_issues` JSON NULL AFTER `quality_score`;
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'VideoProvider',
'runway-image-to-video',
'Runway Image-to-Video',
'real',
'gen4.5',
JSON_OBJECT(
'driver', 'runway_image_to_video',
'api_key_env', 'RUNWAYML_API_SECRET',
'base_url', 'https://api.dev.runwayml.com',
'api_version', '2024-11-06',
'timeout_ms', 180000,
'create_endpoint', '/v1/image_to_video',
'task_endpoint_template', '/v1/tasks/{task_id}',
'poll_interval_ms', 10000,
'max_poll_attempts', 90,
'ratio', '720:1280',
'duration', 5,
'note', '默认禁用。启用后必须在业务侧显式确认真实视频生成,避免误扣费。'
),
false,
40,
JSON_OBJECT('rpm', 5, 'concurrency', 1),
JSON_OBJECT(
'flat_cost', 0,
'unit', 'video_seconds',
'price_per_second', 0,
'currency', 'USD',
'max_cost_per_call', 0,
'daily_cost_limit', 0,
'note', '请按 Runway 实际账单填写 price_per_second 或单次/每日成本上限。'
),
NOW(3),
NOW(3)
),
(
'VideoProvider',
'kling-image-to-video',
'Kling Image-to-Video',
'real',
'kling-v3',
JSON_OBJECT(
'driver', 'kling_image_to_video',
'api_key_env', 'KLING_API_KEY',
'base_url', 'https://api-singapore.klingai.com',
'timeout_ms', 180000,
'create_endpoint', '/v1/videos/image2video',
'task_endpoint_template', '/v1/videos/image2video/{task_id}',
'poll_interval_ms', 10000,
'max_poll_attempts', 90,
'image_field', 'image',
'prompt_field', 'prompt',
'duration_field', 'duration',
'aspect_ratio_field', 'aspect_ratio',
'aspect_ratio', '9:16',
'duration', 5,
'note', '默认禁用。不同 Kling 官方/网关接口字段可能不同,可在高级配置里调整字段名和 Base URL。'
),
false,
40,
JSON_OBJECT('rpm', 5, 'concurrency', 1),
JSON_OBJECT(
'flat_cost', 0,
'unit', 'video_seconds',
'price_per_second', 0,
'currency', 'USD',
'max_cost_per_call', 0,
'daily_cost_limit', 0,
'note', '请按 Kling 实际账单填写 price_per_second 或单次/每日成本上限。'
),
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`updated_at` = NOW(3);
@@ -0,0 +1,108 @@
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'VideoProvider',
'minimax_hailuo_23_fast',
'MiniMax Hailuo 2.3 Fast 图生视频',
'real',
'MiniMax-Hailuo-2.3-Fast',
'{"driver":"configurable_image_to_video","error_prefix":"MINIMAX_VIDEO","api_key_env":"MINIMAX_API_KEY","base_url":"https://api.minimax.io","timeout_ms":300000,"create_endpoint":"/v1/video_generation","task_endpoint_template":"/v1/query/video_generation?task_id={task_id}","output_url_endpoint_template":"/v1/files/retrieve?file_id={file_id}","poll_interval_ms":10000,"max_poll_attempts":120,"image_field":"first_frame_image","prompt_field":"prompt","model_field":"model","duration_field":"duration","resolution_field":"resolution","duration":6,"resolution":"768P","extra_body_json":{"prompt_optimizer":true},"supports_reference_image":true,"supports_start_end_frame":false,"supports_audio":false,"supports_lipsync":false,"supports_character_reference":false,"note":"默认禁用。优先用于低成本快速验证真人短剧动效;成功后 MiniMax 返回 file_id,系统会再取 download_url 落库。"}',
false,
80,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.0317,"currency":"USD","max_cost_per_call":1,"daily_cost_limit":10,"estimated_seconds":6,"note":"预估价仅用于后台试算,请按 MiniMax 控制台实时价格和账单调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'minimax_hailuo_23',
'MiniMax Hailuo 2.3 图生视频',
'real',
'MiniMax-Hailuo-2.3',
'{"driver":"configurable_image_to_video","error_prefix":"MINIMAX_VIDEO","api_key_env":"MINIMAX_API_KEY","base_url":"https://api.minimax.io","timeout_ms":300000,"create_endpoint":"/v1/video_generation","task_endpoint_template":"/v1/query/video_generation?task_id={task_id}","output_url_endpoint_template":"/v1/files/retrieve?file_id={file_id}","poll_interval_ms":10000,"max_poll_attempts":120,"image_field":"first_frame_image","prompt_field":"prompt","model_field":"model","duration_field":"duration","resolution_field":"resolution","duration":6,"resolution":"1080P","extra_body_json":{"prompt_optimizer":true},"supports_reference_image":true,"supports_start_end_frame":false,"supports_audio":false,"supports_lipsync":false,"supports_character_reference":false,"note":"默认禁用。质量优先于 Fast,适合正式样片对比;真实调用前必须在业务侧确认成本。"}',
false,
78,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.0467,"currency":"USD","max_cost_per_call":1.5,"daily_cost_limit":15,"estimated_seconds":6,"note":"预估价仅用于后台试算,请按 MiniMax 控制台实时价格和账单调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'alibaba_wan26_i2v_flash',
'阿里 Wan2.6 I2V Flash',
'real',
'wan2.6-i2v-flash',
'{"driver":"configurable_image_to_video","error_prefix":"DASHSCOPE_VIDEO","api_key_env":"ALIBABA_DASHSCOPE_API_KEY","base_url":"https://dashscope.aliyuncs.com","timeout_ms":300000,"create_endpoint":"/api/v1/services/aigc/video-generation/video-synthesis","task_endpoint_template":"/api/v1/tasks/{task_id}","poll_interval_ms":10000,"max_poll_attempts":120,"body_style":"dashscope_legacy_i2v","headers":{"X-DashScope-Async":"enable"},"duration":5,"resolution":"720P","prompt_extend":true,"watermark":false,"supports_reference_image":true,"supports_start_end_frame":false,"supports_audio":true,"supports_lipsync":false,"supports_character_reference":false,"note":"默认禁用。阿里/百炼不同模型版本字段可能变化,必要时在高级配置中调整 endpoint、body_style 或 extra_body_json。"}',
false,
74,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.0215,"currency":"USD","max_cost_per_call":1,"daily_cost_limit":10,"estimated_seconds":5,"note":"Flash 预估价仅用于试算,请按阿里云/百炼实际账单调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'alibaba_wan26_i2v',
'阿里 Wan2.6 I2V 标准',
'real',
'wan2.6-i2v',
'{"driver":"configurable_image_to_video","error_prefix":"DASHSCOPE_VIDEO","api_key_env":"ALIBABA_DASHSCOPE_API_KEY","base_url":"https://dashscope.aliyuncs.com","timeout_ms":300000,"create_endpoint":"/api/v1/services/aigc/video-generation/video-synthesis","task_endpoint_template":"/api/v1/tasks/{task_id}","poll_interval_ms":10000,"max_poll_attempts":120,"body_style":"dashscope_legacy_i2v","headers":{"X-DashScope-Async":"enable"},"duration":5,"resolution":"1080P","prompt_extend":true,"watermark":false,"supports_reference_image":true,"supports_start_end_frame":false,"supports_audio":true,"supports_lipsync":false,"supports_character_reference":false,"note":"默认禁用。标准模式适合正式出片对比;真实启用前请先小样本验证字段、速度和账单。"}',
false,
72,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.086,"currency":"USD","max_cost_per_call":2,"daily_cost_limit":20,"estimated_seconds":5,"note":"标准模式预估价仅用于试算,请按阿里云/百炼实际账单调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'vidu_q3_turbo_reference',
'Vidu Q3 Turbo 参考图生视频',
'real',
'viduq3-turbo',
'{"driver":"configurable_image_to_video","error_prefix":"VIDU_VIDEO","api_key_env":"VIDU_API_KEY","base_url":"https://api.vidu.com","auth_scheme":"Token","timeout_ms":300000,"create_endpoint":"/ent/v2/reference2video","task_endpoint_template":"/ent/v2/tasks/{task_id}/creations","poll_interval_ms":10000,"max_poll_attempts":120,"body_style":"vidu_reference","duration":5,"resolution":"720p","aspect_ratio":"9:16","extra_body_json":{"audio":true,"movement_amplitude":"auto"},"supports_reference_image":true,"supports_start_end_frame":true,"supports_audio":true,"supports_lipsync":true,"supports_character_reference":true,"note":"默认禁用。适合做人物一致性和中文短剧感对比,参考图需要外部可访问 URL。"}',
false,
70,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.05,"currency":"USD","max_cost_per_call":1.5,"daily_cost_limit":15,"estimated_seconds":5,"note":"Vidu Q3 Turbo 预估价仅用于试算,请按 Vidu 控制台实时价格和 credits 消耗调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'vidu_q3_pro',
'Vidu Q3 Pro 参考图生视频',
'real',
'viduq3',
'{"driver":"configurable_image_to_video","error_prefix":"VIDU_VIDEO","api_key_env":"VIDU_API_KEY","base_url":"https://api.vidu.com","auth_scheme":"Token","timeout_ms":300000,"create_endpoint":"/ent/v2/reference2video","task_endpoint_template":"/ent/v2/tasks/{task_id}/creations","poll_interval_ms":10000,"max_poll_attempts":120,"body_style":"vidu_reference","duration":5,"resolution":"1080p","aspect_ratio":"9:16","extra_body_json":{"audio":true,"movement_amplitude":"auto"},"supports_reference_image":true,"supports_start_end_frame":true,"supports_audio":true,"supports_lipsync":true,"supports_character_reference":true,"note":"默认禁用。质量优先,适合正式样片;真实启用前请先限制单次成本。"}',
false,
68,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0.12,"currency":"USD","max_cost_per_call":3,"daily_cost_limit":30,"estimated_seconds":5,"note":"Vidu Q3 Pro 预估价仅用于试算,请按 Vidu 控制台实时价格和 credits 消耗调整。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'jimeng_seedance',
'即梦/Seedance 图生视频',
'real',
'doubao-seedance-1-5-pro-251215',
'{"driver":"configurable_image_to_video","error_prefix":"SEEDANCE_VIDEO","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":300000,"create_endpoint":"/videos/generations","task_endpoint_template":"/videos/generations/{task_id}","poll_interval_ms":10000,"max_poll_attempts":120,"image_field":"image_url","prompt_field":"prompt","model_field":"model","duration_field":"duration","resolution_field":"resolution","aspect_ratio_field":"ratio","duration":5,"resolution":"720p","aspect_ratio":"9:16","extra_body_json":{"fps":24,"watermark":false,"camerafixed":false},"supports_reference_image":true,"supports_start_end_frame":true,"supports_audio":true,"supports_lipsync":true,"supports_character_reference":true,"note":"默认禁用。不同火山/即梦/网关 API 字段差异较大,此配置作为可改模板,正式接入前必须用小样本验证。"}',
false,
66,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0,"currency":"USD","max_cost_per_call":0,"daily_cost_limit":0,"estimated_seconds":5,"note":"Seedance/即梦价格按具体开通渠道差异较大,启用前请手动填写 price_per_second 和成本上限。"}',
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`updated_at` = NOW(3);
@@ -0,0 +1,62 @@
CREATE TABLE `global_characters` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`display_name` VARCHAR(100) NULL,
`role_archetype` VARCHAR(50) NOT NULL DEFAULT 'lead',
`gender_label` VARCHAR(50) NULL,
`age_group` VARCHAR(50) NULL,
`identity_desc` TEXT NULL,
`appearance_desc` TEXT NULL,
`face_desc` TEXT NULL,
`hair_desc` TEXT NULL,
`eye_desc` TEXT NULL,
`body_desc` TEXT NULL,
`default_costume_rules` TEXT NULL,
`wardrobe_json` JSON NULL,
`special_props` TEXT NULL,
`personality_desc` TEXT NULL,
`speech_style` TEXT NULL,
`voice_provider_code` VARCHAR(100) NULL,
`voice_model` VARCHAR(100) NULL,
`voice_id` VARCHAR(100) NULL,
`voice_style` TEXT NULL,
`performance_style` TEXT NULL,
`negative_rules` TEXT NULL,
`anchor_asset_id` BIGINT NULL,
`voice_sample_asset_id` BIGINT NULL,
`commercial_status` VARCHAR(50) NOT NULL DEFAULT 'internal_test',
`usage_scope` VARCHAR(50) NOT NULL DEFAULT 'internal',
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
INDEX `global_characters_status_role_archetype_idx`(`status`, `role_archetype`),
INDEX `global_characters_commercial_status_idx`(`commercial_status`),
INDEX `global_characters_created_by_user_id_idx`(`created_by_user_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `global_character_assets` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`global_character_id` BIGINT NOT NULL,
`asset_id` BIGINT NULL,
`asset_type` VARCHAR(50) NOT NULL,
`label` VARCHAR(100) NULL,
`prompt_text` TEXT NULL,
`is_primary` BOOLEAN NOT NULL DEFAULT false,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `global_character_assets_global_character_id_asset_type_idx`(`global_character_id`, `asset_type`),
INDEX `global_character_assets_asset_id_idx`(`asset_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `characters`
ADD COLUMN `global_character_id` BIGINT NULL,
ADD COLUMN `wardrobe_variant` VARCHAR(100) NULL,
ADD COLUMN `voice_provider_code` VARCHAR(100) NULL,
ADD COLUMN `voice_model` VARCHAR(100) NULL,
ADD COLUMN `voice_id` VARCHAR(100) NULL,
ADD COLUMN `voice_style` TEXT NULL,
ADD COLUMN `performance_style` TEXT NULL,
ADD INDEX `characters_global_character_id_idx`(`global_character_id`);
@@ -0,0 +1,8 @@
ALTER TABLE `storyboard_shots`
ADD COLUMN `scene_type` VARCHAR(50) NULL AFTER `duration`,
ADD COLUMN `importance_score` INT NULL AFTER `scene_type`,
ADD COLUMN `emotion_score` INT NULL AFTER `importance_score`,
ADD COLUMN `action_score` INT NULL AFTER `emotion_score`,
ADD COLUMN `route_tier` VARCHAR(50) NULL AFTER `action_score`,
ADD INDEX `storyboard_shots_scene_type_idx`(`scene_type`),
ADD INDEX `storyboard_shots_route_tier_idx`(`route_tier`);
@@ -0,0 +1,79 @@
CREATE TABLE `hit_analysis_cases` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`source_platform` VARCHAR(100) NULL,
`source_url` VARCHAR(500) NULL,
`content_type` VARCHAR(80) NOT NULL DEFAULT 'short_drama',
`genre` VARCHAR(100) NULL,
`language` VARCHAR(30) NOT NULL DEFAULT 'zh-CN',
`target_audience` VARCHAR(255) NULL,
`duration_seconds` INT NULL,
`episode_count` INT NULL,
`tags_json` JSON NULL,
`metrics_json` JSON NULL,
`transcript_text` LONGTEXT NULL,
`summary_text` TEXT NULL,
`analysis_json` JSON NULL,
`diagnosis_score` DECIMAL(5, 2) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`),
INDEX `hit_analysis_cases_source_platform_idx`(`source_platform`),
INDEX `hit_analysis_cases_genre_status_idx`(`genre`, `status`),
INDEX `hit_analysis_cases_status_diagnosis_score_idx`(`status`, `diagnosis_score`),
INDEX `hit_analysis_cases_created_at_idx`(`created_at`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `hit_analysis_segments` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`case_id` BIGINT NOT NULL,
`segment_no` INT NOT NULL,
`start_second` INT NULL,
`end_second` INT NULL,
`scene_type` VARCHAR(80) NULL,
`hook_type` VARCHAR(100) NULL,
`emotion` VARCHAR(80) NULL,
`conflict_type` VARCHAR(100) NULL,
`plot_function` VARCHAR(120) NULL,
`visual_strategy` VARCHAR(120) NULL,
`dialogue_pattern` VARCHAR(120) NULL,
`camera_notes` TEXT NULL,
`importance_score` INT NULL,
`emotion_score` INT NULL,
`action_score` INT NULL,
`tags_json` JSON NULL,
`summary_text` TEXT NULL,
`prompt_seed` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `hit_analysis_segments_case_id_segment_no_key`(`case_id`, `segment_no`),
INDEX `hit_analysis_segments_case_id_idx`(`case_id`),
INDEX `hit_analysis_segments_scene_type_idx`(`scene_type`),
INDEX `hit_analysis_segments_hook_type_idx`(`hook_type`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `creative_patterns` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`source_case_id` BIGINT NULL,
`pattern_type` VARCHAR(80) NOT NULL,
`title` VARCHAR(255) NOT NULL,
`genre` VARCHAR(100) NULL,
`language` VARCHAR(30) NOT NULL DEFAULT 'zh-CN',
`description` TEXT NULL,
`structure_json` JSON NULL,
`prompt_template` TEXT NULL,
`negative_prompt` TEXT NULL,
`tags_json` JSON NULL,
`usage_count` INT NOT NULL DEFAULT 0,
`effectiveness_score` DECIMAL(5, 2) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`),
INDEX `creative_patterns_pattern_type_status_idx`(`pattern_type`, `status`),
INDEX `creative_patterns_genre_status_idx`(`genre`, `status`),
INDEX `creative_patterns_source_case_id_idx`(`source_case_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,13 @@
CREATE TABLE `project_creative_patterns` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`creative_pattern_id` BIGINT NOT NULL,
`source` VARCHAR(50) NOT NULL DEFAULT 'user_selected',
`snapshot_json` JSON NULL,
`sort_order` INT NOT NULL DEFAULT 0,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `project_creative_patterns_project_id_creative_pattern_id_key`(`project_id`, `creative_pattern_id`),
INDEX `project_creative_patterns_project_id_sort_order_idx`(`project_id`, `sort_order`),
INDEX `project_creative_patterns_creative_pattern_id_idx`(`creative_pattern_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+826
View File
@@ -0,0 +1,826 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id BigInt @id @default(autoincrement())
email String? @unique @db.VarChar(191)
phone String? @unique @db.VarChar(50)
password_hash String @db.VarChar(255)
nickname String? @db.VarChar(100)
avatar_url String? @db.VarChar(500)
role String @default("user") @db.VarChar(50)
status String @default("active") @db.VarChar(50)
wechat_openid String? @unique @db.VarChar(191)
last_login_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([role, status])
@@index([created_at])
@@map("users")
}
model Project {
id BigInt @id @default(autoincrement())
user_id BigInt
title String? @db.VarChar(255)
input_mode String @db.VarChar(50)
genre String? @db.VarChar(100)
style_code String? @db.VarChar(100)
output_type String? @db.VarChar(50)
output_mode String @default("image_manga") @db.VarChar(50)
visual_mode String? @db.VarChar(100)
video_generation_level String? @db.VarChar(50)
target_episode_count Int?
episode_duration Int?
status String @default("draft") @db.VarChar(80)
copyright_status String @default("pending") @db.VarChar(80)
payment_status String @default("unpaid") @db.VarChar(80)
quality_level String? @db.VarChar(50)
is_long_series Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
completed_at DateTime?
@@index([user_id, status])
@@index([genre, status])
@@index([created_at])
@@map("projects")
}
model NovelSource {
id BigInt @id @default(autoincrement())
project_id BigInt
source_type String @db.VarChar(50)
title String? @db.VarChar(255)
author_name String? @db.VarChar(100)
raw_asset_id BigInt?
raw_text String? @db.LongText
clean_text String? @db.LongText
word_count Int?
chapter_count Int?
parse_status String @default("pending") @db.VarChar(50)
parse_report Json?
created_at DateTime @default(now())
@@index([project_id])
@@index([parse_status])
@@map("novel_sources")
}
model NovelChapter {
id BigInt @id @default(autoincrement())
project_id BigInt
novel_source_id BigInt?
chapter_no Int
title String? @db.VarChar(255)
content String @db.LongText
summary String? @db.Text
visual_summary String? @db.Text
word_count Int?
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
@@unique([novel_source_id, chapter_no])
@@index([project_id, chapter_no])
@@index([project_id, status])
@@map("novel_chapters")
}
model CopyrightRecord {
id BigInt @id @default(autoincrement())
project_id BigInt
user_id BigInt
authorization_type String @db.VarChar(50)
statement_text String @db.Text
ip String? @db.VarChar(80)
user_agent String? @db.Text
confirmed_at DateTime @default(now())
@@index([project_id])
@@index([user_id])
@@index([authorization_type])
@@map("copyright_records")
}
model StoryBible {
id BigInt @id @default(autoincrement())
project_id BigInt
title String? @db.VarChar(255)
logline String? @db.Text
main_plot String? @db.Text
core_conflict String? @db.Text
selling_points String? @db.Text
tone String? @db.VarChar(100)
world_summary String? @db.Text
ending_direction String? @db.Text
taboo_rules String? @db.Text
version Int @default(1)
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, status])
@@map("story_bibles")
}
model WorldBible {
id BigInt @id @default(autoincrement())
project_id BigInt
world_type String? @db.VarChar(100)
setting_text String? @db.Text
rules_text String? @db.Text
power_system String? @db.Text
social_structure String? @db.Text
time_period String? @db.Text
visual_rules String? @db.Text
forbidden_rules String? @db.Text
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
@@index([project_id, status])
@@map("world_bibles")
}
model Character {
id BigInt @id @default(autoincrement())
project_id BigInt
global_character_id BigInt?
name String @db.VarChar(100)
alias_names Json?
role_type String @db.VarChar(50)
gender_label String? @db.VarChar(50)
age_group String? @db.VarChar(50)
identity_desc String? @db.Text
appearance_desc String? @db.Text
face_desc String? @db.Text
hair_desc String? @db.Text
eye_desc String? @db.Text
body_desc String? @db.Text
costume_rules String? @db.Text
special_props String? @db.Text
personality_desc String? @db.Text
speech_style String? @db.Text
relationship_desc String? @db.Text
character_arc String? @db.Text
negative_rules String? @db.Text
anchor_asset_id BigInt?
wardrobe_variant String? @db.VarChar(100)
voice_provider_code String? @db.VarChar(100)
voice_model String? @db.VarChar(100)
voice_id String? @db.VarChar(100)
voice_style String? @db.Text
performance_style String? @db.Text
importance_level Int @default(0)
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, role_type])
@@index([project_id, status])
@@index([global_character_id])
@@map("characters")
}
model GlobalCharacter {
id BigInt @id @default(autoincrement())
name String @db.VarChar(100)
display_name String? @db.VarChar(100)
role_archetype String @default("lead") @db.VarChar(50)
gender_label String? @db.VarChar(50)
age_group String? @db.VarChar(50)
identity_desc String? @db.Text
appearance_desc String? @db.Text
face_desc String? @db.Text
hair_desc String? @db.Text
eye_desc String? @db.Text
body_desc String? @db.Text
default_costume_rules String? @db.Text
wardrobe_json Json?
special_props String? @db.Text
personality_desc String? @db.Text
speech_style String? @db.Text
voice_provider_code String? @db.VarChar(100)
voice_model String? @db.VarChar(100)
voice_id String? @db.VarChar(100)
voice_style String? @db.Text
performance_style String? @db.Text
negative_rules String? @db.Text
anchor_asset_id BigInt?
voice_sample_asset_id BigInt?
commercial_status String @default("internal_test") @db.VarChar(50)
usage_scope String @default("internal") @db.VarChar(50)
status String @default("active") @db.VarChar(50)
created_by_user_id BigInt?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([status, role_archetype])
@@index([commercial_status])
@@index([created_by_user_id])
@@map("global_characters")
}
model GlobalCharacterAsset {
id BigInt @id @default(autoincrement())
global_character_id BigInt
asset_id BigInt?
asset_type String @db.VarChar(50)
label String? @db.VarChar(100)
prompt_text String? @db.Text
is_primary Boolean @default(false)
status String @default("active") @db.VarChar(50)
created_at DateTime @default(now())
@@index([global_character_id, asset_type])
@@index([asset_id])
@@map("global_character_assets")
}
model CharacterImage {
id BigInt @id @default(autoincrement())
project_id BigInt
character_id BigInt
asset_id BigInt?
image_type String @db.VarChar(50)
prompt_text String? @db.Text
negative_prompt String? @db.Text
is_anchor Boolean @default(false)
quality_score Decimal? @db.Decimal(5, 2)
status String @default("pending") @db.VarChar(50)
created_at DateTime @default(now())
@@index([project_id])
@@index([character_id, image_type])
@@index([character_id, is_anchor])
@@map("character_images")
}
model CharacterMemory {
id BigInt @id @default(autoincrement())
project_id BigInt
character_id BigInt
episode_id BigInt?
memory_type String @db.VarChar(50)
content String @db.Text
created_at DateTime @default(now())
@@index([project_id, character_id])
@@index([project_id, episode_id])
@@index([memory_type])
@@map("character_memories")
}
model Episode {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_no Int
source_chapter_ids Json?
title String? @db.VarChar(255)
summary String? @db.Text
opening_hook String? @db.Text
middle_conflict String? @db.Text
ending_hook String? @db.Text
target_duration Int?
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@unique([project_id, episode_no])
@@index([project_id, status])
@@map("episodes")
}
model EpisodeScript {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt
script_text String? @db.LongText
narration_text String? @db.LongText
dialogue_json Json?
version Int @default(1)
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id])
@@index([episode_id, version])
@@map("episode_scripts")
}
model StoryboardShot {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt
shot_no Int
scene_name String? @db.VarChar(255)
location_desc String? @db.Text
characters_json Json?
visual_desc String? @db.Text
action_desc String? @db.Text
dialogue_text String? @db.Text
narration_text String? @db.Text
camera_motion String? @db.VarChar(100)
effect_type String? @db.VarChar(100)
duration Decimal? @db.Decimal(6, 2)
scene_type String? @db.VarChar(50)
importance_score Int?
emotion_score Int?
action_score Int?
route_tier String? @db.VarChar(50)
prompt_text String? @db.Text
negative_prompt String? @db.Text
live_action_desc String? @db.Text
actor_action String? @db.Text
camera_instruction String? @db.Text
performance_instruction String? @db.Text
video_prompt String? @db.Text
keyframe_asset_id BigInt?
video_clip_asset_id BigInt?
video_status String? @db.VarChar(50)
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@unique([episode_id, shot_no])
@@index([project_id, episode_id, shot_no])
@@index([project_id, status])
@@index([scene_type])
@@index([route_tier])
@@map("storyboard_shots")
}
model ShotImage {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt?
shot_id BigInt
asset_id BigInt?
image_type String @default("preview") @db.VarChar(50)
prompt_text String? @db.Text
negative_prompt String? @db.Text
quality_score Decimal? @db.Decimal(5, 2)
status String @default("pending") @db.VarChar(50)
created_at DateTime @default(now())
@@index([project_id, episode_id])
@@index([shot_id, image_type])
@@map("shot_images")
}
model ActorProfile {
id BigInt @id @default(autoincrement())
project_id BigInt
character_id BigInt
actor_desc String? @db.Text
appearance_rules String? @db.Text
wardrobe_rules String? @db.Text
performance_style String? @db.Text
voice_style String? @db.Text
reference_asset_ids Json?
anchor_asset_id BigInt?
status String @default("draft") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@unique([project_id, character_id])
@@index([project_id, status])
@@index([character_id])
@@map("actor_profiles")
}
model VideoClip {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt
shot_id BigInt
provider_id BigInt?
input_asset_id BigInt?
output_asset_id BigInt?
duration Decimal? @db.Decimal(6, 2)
prompt_text String? @db.Text
status String @default("pending") @db.VarChar(50)
cost_actual Decimal? @db.Decimal(12, 4)
retry_count Int @default(0)
quality_status String? @db.VarChar(50)
quality_score Decimal? @db.Decimal(5, 2)
quality_issues Json?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, episode_id])
@@index([shot_id, status])
@@index([provider_id])
@@map("video_clips")
}
model PlotMemory {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt?
chapter_id BigInt?
memory_type String @db.VarChar(50)
content String @db.Text
importance_level Int @default(0)
status String @default("active") @db.VarChar(50)
created_at DateTime @default(now())
@@index([project_id, episode_id])
@@index([project_id, memory_type])
@@map("plot_memories")
}
model PlotThread {
id BigInt @id @default(autoincrement())
project_id BigInt
thread_name String @db.VarChar(255)
thread_type String @db.VarChar(80)
description String? @db.Text
start_episode_no Int?
expected_resolve_episode_no Int?
resolved_episode_no Int?
status String @default("open") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, status])
@@index([project_id, thread_type])
@@map("plot_threads")
}
model ContinuityCheck {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt?
check_type String @db.VarChar(80)
result_status String @db.VarChar(50)
issue_text String? @db.Text
suggestion_text String? @db.Text
created_at DateTime @default(now())
@@index([project_id, episode_id])
@@index([result_status])
@@map("continuity_checks")
}
model Asset {
id BigInt @id @default(autoincrement())
user_id BigInt?
project_id BigInt?
asset_type String @db.VarChar(50)
file_path String @db.VarChar(500)
file_url String? @db.VarChar(500)
mime_type String? @db.VarChar(100)
width Int?
height Int?
duration Decimal? @db.Decimal(10, 2)
size BigInt?
hash String? @db.VarChar(128)
visibility String @default("private") @db.VarChar(30)
status String @default("active") @db.VarChar(50)
created_at DateTime @default(now())
@@index([project_id, asset_type])
@@index([user_id, asset_type])
@@index([hash])
@@map("assets")
}
model RenderTask {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt?
shot_id BigInt?
task_type String @db.VarChar(80)
provider_id BigInt?
status String @default("pending") @db.VarChar(50)
input_json Json?
input_hash String? @db.VarChar(128)
idempotency_key String? @unique @db.VarChar(191)
output_asset_id BigInt?
provider_request_id String? @db.VarChar(255)
retry_count Int @default(0)
max_retry Int @default(0)
cost_estimate Decimal? @db.Decimal(12, 4)
cost_actual Decimal? @db.Decimal(12, 4)
error_code String? @db.VarChar(100)
error_message String? @db.Text
created_at DateTime @default(now())
started_at DateTime?
finished_at DateTime?
@@index([project_id, status])
@@index([task_type, status])
@@index([project_id, episode_id])
@@index([input_hash])
@@map("render_tasks")
}
model ProviderConfig {
id BigInt @id @default(autoincrement())
provider_type String @db.VarChar(80)
provider_code String @db.VarChar(100)
display_name String? @db.VarChar(100)
mode String @default("mock") @db.VarChar(50)
model_name String? @db.VarChar(100)
config_json Json?
fallback_provider_id BigInt?
is_enabled Boolean @default(true)
priority Int @default(0)
rate_limit_json Json?
cost_rule_json Json?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@unique([provider_type, provider_code])
@@index([provider_type, is_enabled])
@@map("provider_configs")
}
model ProviderLog {
id BigInt @id @default(autoincrement())
provider_id BigInt?
task_id BigInt?
project_id BigInt?
provider_type String @db.VarChar(80)
provider_code String? @db.VarChar(100)
model_name String? @db.VarChar(100)
request_json Json?
response_json Json?
input_size Int?
output_size Int?
cost_estimate Decimal? @db.Decimal(12, 4)
cost_actual Decimal? @db.Decimal(12, 4)
status String @default("success") @db.VarChar(50)
error_code String? @db.VarChar(100)
error_message String? @db.Text
started_at DateTime?
finished_at DateTime?
created_at DateTime @default(now())
@@index([provider_type, status])
@@index([project_id])
@@index([task_id])
@@index([created_at])
@@map("provider_logs")
}
model Order {
id BigInt @id @default(autoincrement())
user_id BigInt
project_id BigInt?
order_no String @unique @db.VarChar(100)
package_code String? @db.VarChar(100)
amount Decimal @default(0) @db.Decimal(12, 2)
currency String @default("CNY") @db.VarChar(20)
payment_method String? @db.VarChar(50)
payment_status String @default("pending") @db.VarChar(50)
paid_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([user_id, payment_status])
@@index([project_id])
@@map("orders")
}
model QuotaAccount {
id BigInt @id @default(autoincrement())
user_id BigInt @unique
total_quota Decimal @default(0) @db.Decimal(12, 2)
available_quota Decimal @default(0) @db.Decimal(12, 2)
frozen_quota Decimal @default(0) @db.Decimal(12, 2)
used_quota Decimal @default(0) @db.Decimal(12, 2)
status String @default("active") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([status])
@@map("quota_accounts")
}
model QuotaLog {
id BigInt @id @default(autoincrement())
user_id BigInt
project_id BigInt?
task_id BigInt?
change_type String @db.VarChar(50)
amount Decimal @db.Decimal(12, 2)
balance_after Decimal? @db.Decimal(12, 2)
reason String? @db.VarChar(255)
metadata_json Json?
created_at DateTime @default(now())
@@index([user_id, created_at])
@@index([project_id])
@@index([task_id])
@@map("quota_logs")
}
model RevisionRequest {
id BigInt @id @default(autoincrement())
project_id BigInt
user_id BigInt
revision_type String @db.VarChar(50)
target_type String? @db.VarChar(80)
target_id BigInt?
description String @db.Text
status String @default("pending") @db.VarChar(50)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, status])
@@index([user_id, status])
@@map("revision_requests")
}
model ContentReview {
id BigInt @id @default(autoincrement())
project_id BigInt?
user_id BigInt?
target_type String @db.VarChar(80)
target_id BigInt?
review_type String @db.VarChar(80)
result_status String @default("pending") @db.VarChar(50)
risk_level String? @db.VarChar(50)
issue_text String? @db.Text
suggestion_text String? @db.Text
reviewer_id BigInt?
reviewed_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([project_id, result_status])
@@index([target_type, target_id])
@@index([review_type, result_status])
@@map("content_reviews")
}
model CaseShowcase {
id BigInt @id @default(autoincrement())
project_id BigInt
user_id BigInt?
title String @db.VarChar(255)
cover_asset_id BigInt?
video_asset_id BigInt?
authorization_status String @default("pending") @db.VarChar(50)
visibility String @default("private") @db.VarChar(30)
sort_order Int @default(0)
published_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([visibility, sort_order])
@@index([project_id])
@@map("case_showcases")
}
model HitAnalysisCase {
id BigInt @id @default(autoincrement())
title String @db.VarChar(255)
source_platform String? @db.VarChar(100)
source_url String? @db.VarChar(500)
content_type String @default("short_drama") @db.VarChar(80)
genre String? @db.VarChar(100)
language String @default("zh-CN") @db.VarChar(30)
target_audience String? @db.VarChar(255)
duration_seconds Int?
episode_count Int?
tags_json Json?
metrics_json Json?
transcript_text String? @db.LongText
summary_text String? @db.Text
analysis_json Json?
diagnosis_score Decimal? @db.Decimal(5, 2)
status String @default("draft") @db.VarChar(50)
created_by_user_id BigInt?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([source_platform])
@@index([genre, status])
@@index([status, diagnosis_score])
@@index([created_at])
@@map("hit_analysis_cases")
}
model HitAnalysisSegment {
id BigInt @id @default(autoincrement())
case_id BigInt
segment_no Int
start_second Int?
end_second Int?
scene_type String? @db.VarChar(80)
hook_type String? @db.VarChar(100)
emotion String? @db.VarChar(80)
conflict_type String? @db.VarChar(100)
plot_function String? @db.VarChar(120)
visual_strategy String? @db.VarChar(120)
dialogue_pattern String? @db.VarChar(120)
camera_notes String? @db.Text
importance_score Int?
emotion_score Int?
action_score Int?
tags_json Json?
summary_text String? @db.Text
prompt_seed String? @db.Text
created_at DateTime @default(now())
@@unique([case_id, segment_no])
@@index([case_id])
@@index([scene_type])
@@index([hook_type])
@@map("hit_analysis_segments")
}
model CreativePattern {
id BigInt @id @default(autoincrement())
source_case_id BigInt?
pattern_type String @db.VarChar(80)
title String @db.VarChar(255)
genre String? @db.VarChar(100)
language String @default("zh-CN") @db.VarChar(30)
description String? @db.Text
structure_json Json?
prompt_template String? @db.Text
negative_prompt String? @db.Text
tags_json Json?
usage_count Int @default(0)
effectiveness_score Decimal? @db.Decimal(5, 2)
status String @default("active") @db.VarChar(50)
created_by_user_id BigInt?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([pattern_type, status])
@@index([genre, status])
@@index([source_case_id])
@@map("creative_patterns")
}
model ProjectCreativePattern {
id BigInt @id @default(autoincrement())
project_id BigInt
creative_pattern_id BigInt
source String @default("user_selected") @db.VarChar(50)
snapshot_json Json?
sort_order Int @default(0)
created_at DateTime @default(now())
@@unique([project_id, creative_pattern_id])
@@index([project_id, sort_order])
@@index([creative_pattern_id])
@@map("project_creative_patterns")
}
model AnalyticsEvent {
id BigInt @id @default(autoincrement())
project_id BigInt
episode_id BigInt?
event_type String @db.VarChar(80)
platform String? @db.VarChar(80)
metric_json Json?
created_at DateTime @default(now())
@@index([project_id])
@@index([episode_id])
@@index([event_type, created_at])
@@map("analytics_events")
}
model SystemConfig {
id BigInt @id @default(autoincrement())
config_key String @unique @db.VarChar(191)
config_value Json?
description String? @db.Text
is_public Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([is_public])
@@map("system_configs")
}
model OperationLog {
id BigInt @id @default(autoincrement())
user_id BigInt?
operator_role String? @db.VarChar(50)
action String @db.VarChar(100)
target_type String? @db.VarChar(80)
target_id BigInt?
ip String? @db.VarChar(80)
user_agent String? @db.Text
metadata_json Json?
created_at DateTime @default(now())
@@index([user_id, created_at])
@@index([target_type, target_id])
@@index([action, created_at])
@@map("operation_logs")
}
+147
View File
@@ -0,0 +1,147 @@
import { PrismaClient } from '@prisma/client';
import { hash } from 'bcryptjs';
import { DEFAULT_AI_ROUTER_CONFIG } from '../src/ai-router/ai-router.types';
const prisma = new PrismaClient();
const mockProviders = [
['TextProvider', 'mock-text', 'Mock Text Provider', 'mock-text-v1'],
['NovelProvider', 'mock-novel', 'Mock Novel Provider', 'mock-novel-v1'],
['ImageProvider', 'mock-image', 'Mock Image Provider', 'mock-image-v1'],
['VideoProvider', 'mock-video', 'Mock Video Provider', 'mock-video-v1'],
['VoiceProvider', 'mock-voice', 'Mock Voice Provider', 'mock-voice-v1'],
['LipSyncProvider', 'mock-lipsync', 'Mock Lip Sync Provider', 'mock-lipsync-v1'],
['ModerationProvider', 'mock-moderation', 'Mock Moderation Provider', 'mock-moderation-v1'],
['QualityCheckProvider', 'mock-qc', 'Mock Quality Check Provider', 'mock-qc-v1'],
['FileParseProvider', 'mock-file-parse', 'Mock File Parse Provider', 'mock-file-parse-v1'],
['EmbeddingProvider', 'mock-embedding', 'Mock Embedding Provider', 'mock-embedding-v1']
] as const;
async function main() {
const adminPassword = process.env.SEED_ADMIN_PASSWORD || 'Admin123!';
const adminPasswordHash = await hash(adminPassword, 12);
const admin = await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: {
password_hash: adminPasswordHash,
role: 'admin',
status: 'active'
},
create: {
email: 'admin@example.com',
password_hash: adminPasswordHash,
nickname: 'System Admin',
role: 'admin',
status: 'active'
}
});
for (const [providerType, providerCode, displayName, modelName] of mockProviders) {
const isEnabled = providerCode !== 'mock-lipsync';
await prisma.providerConfig.upsert({
where: {
provider_type_provider_code: {
provider_type: providerType,
provider_code: providerCode
}
},
update: {
display_name: displayName,
mode: 'mock',
model_name: modelName,
is_enabled: isEnabled,
priority: 100,
rate_limit_json: {
rpm: 120,
concurrency: 8
},
cost_rule_json: {
flat_cost: 0,
unit: 'mock'
}
},
create: {
provider_type: providerType,
provider_code: providerCode,
display_name: displayName,
mode: 'mock',
model_name: modelName,
is_enabled: isEnabled,
priority: 100,
config_json: {
note: 'Used until the MVP flow is complete.'
},
rate_limit_json: {
rpm: 120,
concurrency: 8
},
cost_rule_json: {
flat_cost: 0,
unit: 'mock'
}
}
});
}
await prisma.systemConfig.upsert({
where: { config_key: 'system_a.current_stage' },
update: {
config_value: {
stage: 'stage-02-database-schema'
}
},
create: {
config_key: 'system_a.current_stage',
config_value: {
stage: 'stage-02-database-schema'
},
description: 'Tracks current System A development stage.'
}
});
await prisma.systemConfig.upsert({
where: { config_key: 'security.api_crypto_enabled' },
update: {},
create: {
config_key: 'security.api_crypto_enabled',
config_value: {
enabled: false
},
description: 'Controls frontend/backend API payload encryption. Default off for testing; enable manually in production.',
is_public: true
}
});
await prisma.systemConfig.upsert({
where: { config_key: 'ai.router.v1' },
update: {},
create: {
config_key: 'ai.router.v1',
config_value: DEFAULT_AI_ROUTER_CONFIG,
description: 'AI Router V1 route config for automatic provider selection by language, shot score and budget.',
is_public: false
}
});
await prisma.quotaAccount.upsert({
where: { user_id: admin.id },
update: {},
create: {
user_id: admin.id,
total_quota: 100,
available_quota: 100,
status: 'active'
}
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (error) => {
console.error(error);
await prisma.$disconnect();
process.exit(1);
});