121 lines
7.2 KiB
SQL
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';
|