Files
lehuo/apps/api/prisma/migrations/20260812140000_phase6_content_notifications/migration.sql
T
2026-08-12 14:35:12 +08:00

121 lines
7.2 KiB
SQL

-- Phase 6: reviewed public content and durable user notifications.
ALTER TABLE `files` MODIFY `business_type` ENUM('SERVICE_RECORD', 'PROJECT_COVER', 'ACTIVITY_COVER', 'CONTENT_COVER') NOT NULL;
CREATE TABLE `content_posts` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`public_id` CHAR(36) NOT NULL,
`client_request_id` CHAR(36) NOT NULL,
`request_fingerprint` CHAR(64) NOT NULL,
`type` ENUM('ORGANIZATION_INTRO','PUBLIC_WELFARE_NEWS','SERVICE_STORY','NOTICE','FAQ') NOT NULL,
`title` VARCHAR(200) NOT NULL,
`summary` VARCHAR(500) NOT NULL,
`content` TEXT NOT NULL,
`cover_file_id` BIGINT UNSIGNED NULL,
`status` ENUM('DRAFT','PENDING_REVIEW','PUBLISHED','ARCHIVED') NOT NULL DEFAULT 'DRAFT',
`revision` INTEGER UNSIGNED NOT NULL DEFAULT 1,
`privacy_reviewed_at` DATETIME(3) NULL,
`reviewed_by` BIGINT UNSIGNED NULL,
`reviewed_at` DATETIME(3) NULL,
`published_at` DATETIME(3) NULL,
`created_by` BIGINT UNSIGNED NOT NULL,
`updated_by` BIGINT UNSIGNED NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `content_posts_public_id_key` (`public_id`),
UNIQUE INDEX `content_posts_cover_file_id_key` (`cover_file_id`),
UNIQUE INDEX `content_posts_creator_request_key` (`created_by`, `client_request_id`),
INDEX `content_posts_status_type_published_idx` (`status`, `type`, `published_at`),
INDEX `content_posts_creator_status_updated_idx` (`created_by`, `status`, `updated_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `content_post_status_history` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`content_post_id` BIGINT UNSIGNED NOT NULL,
`request_id` CHAR(36) NOT NULL,
`request_fingerprint` CHAR(64) NOT NULL,
`from_status` ENUM('DRAFT','PENDING_REVIEW','PUBLISHED','ARCHIVED') NULL,
`to_status` ENUM('DRAFT','PENDING_REVIEW','PUBLISHED','ARCHIVED') NOT NULL,
`operator_user_id` BIGINT UNSIGNED NOT NULL,
`reason_code` VARCHAR(100) NOT NULL,
`note_encrypted` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `content_post_status_history_request_id_key` (`request_id`),
INDEX `content_status_history_post_created_idx` (`content_post_id`, `created_at`),
INDEX `content_status_history_operator_created_idx` (`operator_user_id`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `notifications` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`public_id` CHAR(36) NOT NULL,
`user_id` BIGINT UNSIGNED NOT NULL,
`event_key` VARCHAR(160) NOT NULL,
`type` ENUM('APPLICATION_SUBMITTED','APPLICATION_ACCEPTED','SERVICE_ARRANGED','SERVICE_COMPLETED','FOLLOW_UP_PENDING','ACTIVITY_REGISTRATION_RESULT') NOT NULL,
`title` VARCHAR(120) NOT NULL,
`body` VARCHAR(500) NOT NULL,
`resource_type` VARCHAR(80) NULL,
`resource_public_id` CHAR(36) NULL,
`read_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `notifications_public_id_key` (`public_id`),
UNIQUE INDEX `notifications_user_event_key` (`user_id`, `event_key`),
INDEX `notifications_user_read_created_idx` (`user_id`, `read_at`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `notification_preferences` (
`user_id` BIGINT UNSIGNED NOT NULL,
`type` ENUM('APPLICATION_SUBMITTED','APPLICATION_ACCEPTED','SERVICE_ARRANGED','SERVICE_COMPLETED','FOLLOW_UP_PENDING','ACTIVITY_REGISTRATION_RESULT') NOT NULL,
`in_app_enabled` BOOLEAN NOT NULL DEFAULT true,
`wechat_enabled` BOOLEAN NOT NULL DEFAULT false,
`authorized_at` DATETIME(3) NULL,
`updated_at` DATETIME(3) NOT NULL,
INDEX `notification_preferences_type_wechat_idx` (`type`, `wechat_enabled`),
PRIMARY KEY (`user_id`, `type`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `notification_deliveries` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`public_id` CHAR(36) NOT NULL,
`notification_id` BIGINT UNSIGNED NOT NULL,
`channel` ENUM('WECHAT_SUBSCRIPTION') NOT NULL,
`status` ENUM('PENDING','PROCESSING','SENT','FAILED','DEAD') NOT NULL DEFAULT 'PENDING',
`attempt_count` INTEGER UNSIGNED NOT NULL DEFAULT 0,
`next_attempt_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`last_attempt_at` DATETIME(3) NULL,
`provider_message_id` VARCHAR(160) NULL,
`error_code` VARCHAR(100) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `notification_deliveries_public_id_key` (`public_id`),
UNIQUE INDEX `notification_deliveries_notification_channel_key` (`notification_id`, `channel`),
INDEX `notification_deliveries_status_next_idx` (`status`, `next_attempt_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `content_posts` ADD CONSTRAINT `content_posts_cover_file_id_fkey` FOREIGN KEY (`cover_file_id`) REFERENCES `files` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `content_posts` ADD CONSTRAINT `content_posts_created_by_fkey` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `content_posts` ADD CONSTRAINT `content_posts_updated_by_fkey` FOREIGN KEY (`updated_by`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `content_posts` ADD CONSTRAINT `content_posts_reviewed_by_fkey` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `content_post_status_history` ADD CONSTRAINT `content_status_history_post_fkey` FOREIGN KEY (`content_post_id`) REFERENCES `content_posts` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `content_post_status_history` ADD CONSTRAINT `content_status_history_operator_fkey` FOREIGN KEY (`operator_user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `notifications` ADD CONSTRAINT `notifications_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `notification_preferences` ADD CONSTRAINT `notification_preferences_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `notification_deliveries` ADD CONSTRAINT `notification_deliveries_notification_id_fkey` FOREIGN KEY (`notification_id`) REFERENCES `notifications` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
INSERT INTO `permissions` (`code`, `name`, `description`) VALUES
('content:read', '查看内容', '查看内容草稿、审核状态及历史'),
('content:manage', '管理内容', '创建和维护内容草稿'),
('content:review', '审核内容', '审核、发布、退回和归档内容'),
('notifications:manage', '管理通知', '查看通知投递状态并安全重试');
INSERT INTO `role_permissions` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `roles` r CROSS JOIN `permissions` p
WHERE r.code IN ('ADMIN','SUPER_ADMIN')
AND p.code IN ('content:read','content:manage','content:review','notifications:manage');
INSERT INTO `role_permissions` (`role_id`, `permission_id`)
SELECT r.id, p.id FROM `roles` r CROSS JOIN `permissions` p
WHERE r.code IN ('STAFF','PROJECT_MANAGER') AND p.code = 'content:read';