-- AlterTable ALTER TABLE `consents` MODIFY `consent_type` ENUM('SERVICE_APPLICATION', 'BENEFICIARY_GUARDIAN', 'VOLUNTEER_APPLICATION') NOT NULL; -- AlterTable ALTER TABLE `files` MODIFY `case_id` BIGINT UNSIGNED NULL, MODIFY `business_type` ENUM('SERVICE_RECORD', 'PROJECT_COVER', 'ACTIVITY_COVER') NOT NULL; -- AlterTable ALTER TABLE `service_cases` ADD COLUMN `project_id` BIGINT UNSIGNED NULL; -- CreateTable CREATE TABLE `volunteers` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `public_id` CHAR(36) NOT NULL, `user_id` BIGINT UNSIGNED NOT NULL, `consent_id` BIGINT UNSIGNED NOT NULL, `real_name_encrypted` VARCHAR(512) NOT NULL, `real_name_hash` CHAR(64) NOT NULL, `mobile_encrypted` VARCHAR(512) NOT NULL, `mobile_hash` CHAR(64) NOT NULL, `region_code` VARCHAR(100) NOT NULL, `availability_note_encrypted` TEXT NOT NULL, `status` ENUM('APPLIED', 'APPROVED', 'REJECTED', 'SUSPENDED', 'INACTIVE') NOT NULL DEFAULT 'APPLIED', `approved_by` BIGINT UNSIGNED NULL, `approved_at` DATETIME(3) NULL, `revision` INTEGER UNSIGNED NOT NULL DEFAULT 1, `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 `volunteers_public_id_key`(`public_id`), UNIQUE INDEX `volunteers_user_id_key`(`user_id`), UNIQUE INDEX `volunteers_consent_id_key`(`consent_id`), INDEX `volunteers_status_created_at_idx`(`status`, `created_at`), INDEX `volunteers_mobile_hash_idx`(`mobile_hash`), INDEX `volunteers_region_status_idx`(`region_code`, `status`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `volunteer_skills` ( `volunteer_id` BIGINT UNSIGNED NOT NULL, `skill_code` VARCHAR(64) NOT NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), INDEX `volunteer_skills_code_idx`(`skill_code`), PRIMARY KEY (`volunteer_id`, `skill_code`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `volunteer_interests` ( `volunteer_id` BIGINT UNSIGNED NOT NULL, `category` ENUM('OLD_AGE', 'WOMEN', 'CHILD', 'DISABILITY', 'LIFE_DIFFICULTY', 'OTHER') NOT NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), INDEX `volunteer_interests_category_idx`(`category`), PRIMARY KEY (`volunteer_id`, `category`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `volunteer_status_history` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `volunteer_id` BIGINT UNSIGNED NOT NULL, `request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `from_status` ENUM('APPLIED', 'APPROVED', 'REJECTED', 'SUSPENDED', 'INACTIVE') NULL, `to_status` ENUM('APPLIED', 'APPROVED', 'REJECTED', 'SUSPENDED', 'INACTIVE') 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 `volunteer_status_history_request_id_key`(`request_id`), INDEX `volunteer_status_history_volunteer_created_at_idx`(`volunteer_id`, `created_at`), INDEX `volunteer_status_history_operator_created_at_idx`(`operator_user_id`, `created_at`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `projects` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `public_id` CHAR(36) NOT NULL, `project_no` VARCHAR(32) NOT NULL, `client_request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `name` VARCHAR(200) NOT NULL, `category` ENUM('OLD_AGE', 'WOMEN', 'CHILD', 'DISABILITY', 'LIFE_DIFFICULTY', 'OTHER') NOT NULL, `summary` VARCHAR(500) NOT NULL, `content` TEXT NOT NULL, `cover_file_id` BIGINT UNSIGNED NULL, `manager_user_id` BIGINT UNSIGNED NOT NULL, `start_date` DATE NOT NULL, `end_date` DATE NOT NULL, `target_people` INTEGER UNSIGNED NULL, `status` ENUM('DRAFT', 'PUBLISHED', 'COMPLETED') NOT NULL DEFAULT 'DRAFT', `is_public` BOOLEAN NOT NULL DEFAULT false, `sort_order` INTEGER NOT NULL DEFAULT 0, `revision` INTEGER UNSIGNED NOT NULL DEFAULT 1, `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 `projects_public_id_key`(`public_id`), UNIQUE INDEX `projects_project_no_key`(`project_no`), UNIQUE INDEX `projects_cover_file_id_key`(`cover_file_id`), INDEX `projects_public_status_sort_idx`(`status`, `is_public`, `sort_order`, `created_at`), INDEX `projects_manager_status_idx`(`manager_user_id`, `status`, `created_at`), UNIQUE INDEX `projects_creator_request_key`(`created_by`, `client_request_id`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `project_status_history` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `project_id` BIGINT UNSIGNED NOT NULL, `request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `from_status` ENUM('DRAFT', 'PUBLISHED', 'COMPLETED') NULL, `to_status` ENUM('DRAFT', 'PUBLISHED', 'COMPLETED') NOT NULL, `operator_user_id` BIGINT UNSIGNED NOT NULL, `reason_code` VARCHAR(100) NOT NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), UNIQUE INDEX `project_status_history_request_id_key`(`request_id`), INDEX `project_status_history_project_created_at_idx`(`project_id`, `created_at`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `activities` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `public_id` CHAR(36) NOT NULL, `activity_no` VARCHAR(32) NOT NULL, `project_id` BIGINT UNSIGNED NULL, `client_request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `title` VARCHAR(200) NOT NULL, `summary` VARCHAR(500) NOT NULL, `content` TEXT NOT NULL, `cover_file_id` BIGINT UNSIGNED NULL, `start_at` DATETIME(3) NOT NULL, `end_at` DATETIME(3) NOT NULL, `registration_deadline` DATETIME(3) NOT NULL, `location_text` VARCHAR(300) NOT NULL, `capacity` INTEGER UNSIGNED NULL, `status` ENUM('DRAFT', 'PUBLISHED', 'REGISTRATION_CLOSED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED') NOT NULL DEFAULT 'DRAFT', `published_at` DATETIME(3) NULL, `revision` INTEGER UNSIGNED NOT NULL DEFAULT 1, `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 `activities_public_id_key`(`public_id`), UNIQUE INDEX `activities_activity_no_key`(`activity_no`), UNIQUE INDEX `activities_cover_file_id_key`(`cover_file_id`), INDEX `activities_status_start_at_idx`(`status`, `start_at`), INDEX `activities_project_start_at_idx`(`project_id`, `start_at`), UNIQUE INDEX `activities_creator_request_key`(`created_by`, `client_request_id`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `activity_status_history` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `activity_id` BIGINT UNSIGNED NOT NULL, `request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `from_status` ENUM('DRAFT', 'PUBLISHED', 'REGISTRATION_CLOSED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED') NULL, `to_status` ENUM('DRAFT', 'PUBLISHED', 'REGISTRATION_CLOSED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED') NOT NULL, `operator_user_id` BIGINT UNSIGNED NOT NULL, `reason_code` VARCHAR(100) NOT NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), UNIQUE INDEX `activity_status_history_request_id_key`(`request_id`), INDEX `activity_status_history_activity_created_at_idx`(`activity_id`, `created_at`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `activity_registrations` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `public_id` CHAR(36) NOT NULL, `activity_id` BIGINT UNSIGNED NOT NULL, `user_id` BIGINT UNSIGNED NOT NULL, `volunteer_id` BIGINT UNSIGNED NOT NULL, `client_request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `status` ENUM('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED') NOT NULL DEFAULT 'PENDING', `check_in_status` ENUM('NOT_CHECKED_IN', 'CHECKED_IN', 'ABSENT') NOT NULL DEFAULT 'NOT_CHECKED_IN', `registered_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `reviewed_by` BIGINT UNSIGNED NULL, `reviewed_at` DATETIME(3) NULL, `review_note_encrypted` TEXT NULL, `checked_in_at` DATETIME(3) NULL, `checked_in_by` BIGINT UNSIGNED NULL, `service_minutes` INTEGER UNSIGNED NULL, `service_minutes_recorded_at` DATETIME(3) NULL, `service_minutes_recorded_by` BIGINT UNSIGNED NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `updated_at` DATETIME(3) NOT NULL, UNIQUE INDEX `activity_registrations_public_id_key`(`public_id`), INDEX `activity_registrations_activity_status_idx`(`activity_id`, `status`, `registered_at`), INDEX `activity_registrations_volunteer_registered_idx`(`volunteer_id`, `registered_at`), UNIQUE INDEX `activity_registrations_activity_user_key`(`activity_id`, `user_id`), UNIQUE INDEX `activity_registrations_user_request_key`(`user_id`, `client_request_id`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE `activity_registration_history` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `registration_id` BIGINT UNSIGNED NOT NULL, `request_id` CHAR(36) NOT NULL, `request_fingerprint` CHAR(64) NOT NULL, `from_status` ENUM('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED') NULL, `to_status` ENUM('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED') NULL, `from_check_in_status` ENUM('NOT_CHECKED_IN', 'CHECKED_IN', 'ABSENT') NULL, `to_check_in_status` ENUM('NOT_CHECKED_IN', 'CHECKED_IN', 'ABSENT') NULL, `service_minutes_before` INTEGER UNSIGNED NULL, `service_minutes_after` INTEGER UNSIGNED NULL, `operator_user_id` BIGINT UNSIGNED NOT NULL, `action_code` VARCHAR(100) NOT NULL, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), UNIQUE INDEX `activity_registration_history_request_id_key`(`request_id`), INDEX `registration_history_registration_created_at_idx`(`registration_id`, `created_at`), PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX `service_cases_project_created_at_idx` ON `service_cases`(`project_id`, `created_at`); ALTER TABLE `service_cases` ADD CONSTRAINT `service_cases_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteers` ADD CONSTRAINT `volunteers_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteers` ADD CONSTRAINT `volunteers_consent_id_fkey` FOREIGN KEY (`consent_id`) REFERENCES `consents`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteers` ADD CONSTRAINT `volunteers_approved_by_fkey` FOREIGN KEY (`approved_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteers` ADD CONSTRAINT `volunteers_created_by_fkey` FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteers` ADD CONSTRAINT `volunteers_updated_by_fkey` FOREIGN KEY (`updated_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteer_skills` ADD CONSTRAINT `volunteer_skills_volunteer_id_fkey` FOREIGN KEY (`volunteer_id`) REFERENCES `volunteers`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteer_interests` ADD CONSTRAINT `volunteer_interests_volunteer_id_fkey` FOREIGN KEY (`volunteer_id`) REFERENCES `volunteers`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteer_status_history` ADD CONSTRAINT `volunteer_status_history_volunteer_id_fkey` FOREIGN KEY (`volunteer_id`) REFERENCES `volunteers`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `volunteer_status_history` ADD CONSTRAINT `volunteer_status_history_operator_user_id_fkey` FOREIGN KEY (`operator_user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `projects` ADD CONSTRAINT `projects_cover_file_id_fkey` FOREIGN KEY (`cover_file_id`) REFERENCES `files`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `projects` ADD CONSTRAINT `projects_manager_user_id_fkey` FOREIGN KEY (`manager_user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `projects` ADD CONSTRAINT `projects_created_by_fkey` FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `projects` ADD CONSTRAINT `projects_updated_by_fkey` FOREIGN KEY (`updated_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `project_status_history` ADD CONSTRAINT `project_status_history_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `project_status_history` ADD CONSTRAINT `project_status_history_operator_user_id_fkey` FOREIGN KEY (`operator_user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activities` ADD CONSTRAINT `activities_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activities` ADD CONSTRAINT `activities_cover_file_id_fkey` FOREIGN KEY (`cover_file_id`) REFERENCES `files`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activities` ADD CONSTRAINT `activities_created_by_fkey` FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activities` ADD CONSTRAINT `activities_updated_by_fkey` FOREIGN KEY (`updated_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_status_history` ADD CONSTRAINT `activity_status_history_activity_id_fkey` FOREIGN KEY (`activity_id`) REFERENCES `activities`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_status_history` ADD CONSTRAINT `activity_status_history_operator_user_id_fkey` FOREIGN KEY (`operator_user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_activity_id_fkey` FOREIGN KEY (`activity_id`) REFERENCES `activities`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_volunteer_id_fkey` FOREIGN KEY (`volunteer_id`) REFERENCES `volunteers`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_reviewed_by_fkey` FOREIGN KEY (`reviewed_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_checked_in_by_fkey` FOREIGN KEY (`checked_in_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registrations` ADD CONSTRAINT `activity_registrations_service_minutes_recorded_by_fkey` FOREIGN KEY (`service_minutes_recorded_by`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registration_history` ADD CONSTRAINT `activity_registration_history_registration_id_fkey` FOREIGN KEY (`registration_id`) REFERENCES `activity_registrations`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `activity_registration_history` ADD CONSTRAINT `activity_registration_history_operator_user_id_fkey` FOREIGN KEY (`operator_user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; INSERT INTO `permissions` (`code`, `name`, `description`) VALUES ('volunteers:read', '查看志愿者', '按授权范围查看志愿者脱敏列表与详情'), ('volunteers:review', '审核志愿者', '审核、暂停和恢复志愿者资格'), ('projects:read', '查看公益项目', '按授权范围查看公益项目'), ('projects:manage', '管理公益项目', '创建和维护公益项目及关联工单'), ('projects:publish', '发布公益项目', '执行公益项目发布和完成状态迁移'), ('activities:read', '查看公益活动', '按授权范围查看活动和报名'), ('activities:manage', '管理公益活动', '创建和维护公益活动'), ('activities:publish', '发布公益活动', '执行活动发布及状态迁移'), ('registrations: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 ('PROJECT_MANAGER', 'ADMIN', 'SUPER_ADMIN') AND p.code IN ('volunteers:read', 'volunteers:review', 'projects:read', 'projects:manage', 'projects:publish', 'activities:read', 'activities:manage', 'activities:publish', 'registrations:manage'); INSERT INTO `role_permissions` (`role_id`, `permission_id`) SELECT r.id, p.id FROM roles r CROSS JOIN permissions p WHERE r.code = 'STAFF' AND p.code IN ('volunteers:read', 'projects:read', 'activities:read', 'registrations:manage');