From f84c9d3efb6049eed8320700882f60321d79ccc8 Mon Sep 17 00:00:00 2001 From: canglan Date: Tue, 26 May 2026 13:47:01 +0800 Subject: [PATCH] =?UTF-8?q?v2.1=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- README.md | 1 + VERSION | 1 + frontend/admin/dashboard.php | 151 ++++++- frontend/admin/history.php | 2 +- frontend/admin/homework.php | 47 ++- frontend/admin/semesters.php | 4 +- frontend/admin/students.php | 2 +- frontend/api/check_upgrade.php | 103 +++++ frontend/assets/css/admin.css | 24 +- frontend/assets/css/style.css | 286 ++++++++----- frontend/assets/js/conduct.js | 4 +- frontend/assets/js/dashboard.js | 2 +- frontend/assets/js/homework-manage.js | 29 +- frontend/assets/js/student-homework.js | 2 +- frontend/assets/js/students-manage.js | 4 +- frontend/assets/js/subjects.js | 6 +- frontend/student/semester_history.php | 22 +- sql/init.sql | 264 +----------- sql/upgrade_v2.0.sql | 143 ------- sql/upgrades/v1.7.sql | 31 ++ sql/upgrades/v1.8.sql | 131 ++++++ sql/upgrades/v2.0.1.sql | 6 + sql/upgrades/v2.0.sql | 145 +++++++ sql/upgrades/v2.1.sql | 102 +++++ upgrade.php | 532 +++++++++++++++++++++++++ 26 files changed, 1482 insertions(+), 567 deletions(-) create mode 100644 VERSION create mode 100644 frontend/api/check_upgrade.php delete mode 100644 sql/upgrade_v2.0.sql create mode 100644 sql/upgrades/v1.7.sql create mode 100644 sql/upgrades/v1.8.sql create mode 100644 sql/upgrades/v2.0.1.sql create mode 100644 sql/upgrades/v2.0.sql create mode 100644 sql/upgrades/v2.1.sql create mode 100644 upgrade.php diff --git a/.gitignore b/.gitignore index 694db01..0e10557 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,7 @@ docs/guide/cadre.pdf docs/guide/parent.pdf docs/guide/student.pdf docs/guide/teacher.pdf -qrcode.png \ No newline at end of file +qrcode.png + +# 展示内容 +example \ No newline at end of file diff --git a/README.md b/README.md index 21fed3b..cfd8f91 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,7 @@ classmanager/ | v1.7 | 2026.5.21 | 全量一致性审计:前后端配置统一(.env.example/config.py/config.php)、清理废弃全局变量、角色权限表精确化 | | v1.8 | 2026.5.22 | 科目管理融入作业管理页、科目删除数据依赖检查、加减分记录类型区分(manual/homework/attendance)、学生端作业详情优化 | | v2.0.1 | 2026.5.23 | 操作列折叠优化、扣分类型大类区分、科目选择修复、改名作业扣分、记录人优化、家长端优化、学期管理优化 | +| v2.1 | 2025.7.14 | CSS变量化统一配色方案、简化按钮系统、操作列按钮风格统一、清理内联颜色、修复科目管理面板无法展开、数据库索引优化、清理init.sql冗余迁移代码、安全审计通过 | ## 许可证 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..879b416 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.1 diff --git a/frontend/admin/dashboard.php b/frontend/admin/dashboard.php index 7e6a6ca..0f11295 100644 --- a/frontend/admin/dashboard.php +++ b/frontend/admin/dashboard.php @@ -39,8 +39,8 @@ include __DIR__ . '/../includes/header.php'; 显示前 % 的学生 - - + + @@ -52,7 +52,154 @@ include __DIR__ . '/../includes/header.php'; + + + + + + + + + \ No newline at end of file diff --git a/frontend/admin/history.php b/frontend/admin/history.php index 33a60ec..6f417e5 100644 --- a/frontend/admin/history.php +++ b/frontend/admin/history.php @@ -83,7 +83,7 @@ include __DIR__ . '/../includes/header.php'; - + diff --git a/frontend/admin/homework.php b/frontend/admin/homework.php index eaa6972..1a5f4b1 100644 --- a/frontend/admin/homework.php +++ b/frontend/admin/homework.php @@ -32,12 +32,12 @@ include __DIR__ . '/../includes/header.php';
-
-
+
+

📚 科目管理

▶ 展开
-
- - + +
diff --git a/frontend/admin/students.php b/frontend/admin/students.php index ce30d0e..62c6830 100644 --- a/frontend/admin/students.php +++ b/frontend/admin/students.php @@ -30,7 +30,7 @@ include __DIR__ . '/../includes/header.php';
- +
- + - + `; }); if (res.data.students.length === 0) { diff --git a/frontend/assets/js/dashboard.js b/frontend/assets/js/dashboard.js index cec02a4..7695740 100644 --- a/frontend/assets/js/dashboard.js +++ b/frontend/assets/js/dashboard.js @@ -29,7 +29,7 @@ async function loadDashboard() { quickActions += ''; } if (role === '班主任') { - quickActions += ''; + quickActions += ''; quickActions += ''; } document.getElementById('quickActions').innerHTML = quickActions || '

暂无快捷操作

'; diff --git a/frontend/assets/js/homework-manage.js b/frontend/assets/js/homework-manage.js index c550650..723ebb5 100644 --- a/frontend/assets/js/homework-manage.js +++ b/frontend/assets/js/homework-manage.js @@ -50,7 +50,7 @@ async function loadStudents() { - + `; }); if (res.data.students.length === 0) { @@ -110,13 +110,18 @@ function handleSubmitPoints() { function toggleSubjectPanel() { const content = document.getElementById('subjectPanelContent'); const toggle = document.getElementById('subjectPanelToggle'); - if (content.style.display === 'none') { - content.style.display = 'block'; + if (!content || !toggle) return; + + const isExpanded = content.classList.contains('expanded'); + if (isExpanded) { + content.classList.remove('expanded'); + toggle.classList.remove('expanded'); + toggle.textContent = '▶ 展开'; + } else { + content.classList.add('expanded'); + toggle.classList.add('expanded'); toggle.textContent = '▼ 收起'; loadSubjectList(); - } else { - content.style.display = 'none'; - toggle.textContent = '▶ 展开'; } } @@ -132,11 +137,11 @@ async function loadSubjectList() { ${sub.is_active ? '启用' : '禁用'} - - + - + `; }); @@ -234,6 +239,12 @@ async function submitEditSubject() { } } +// 绑定科目管理折叠面板 +var subjectHeader = document.getElementById('subjectPanelHeader'); +if (subjectHeader) { + subjectHeader.addEventListener('click', toggleSubjectPanel); +} + loadStudents(); loadSubjectsForHomework(); diff --git a/frontend/assets/js/student-homework.js b/frontend/assets/js/student-homework.js index 2b2a57c..1860f23 100644 --- a/frontend/assets/js/student-homework.js +++ b/frontend/assets/js/student-homework.js @@ -23,7 +23,7 @@ async function loadHomework() { statusDisplay = getStatusBadge(hw.status, 'homework'); } // 扣分显示 - const pointsDisplay = hw.points ? `${hw.points}分` : '-'; + const pointsDisplay = hw.points ? `${hw.points}分` : '-'; html += ` diff --git a/frontend/assets/js/students-manage.js b/frontend/assets/js/students-manage.js index 59f2522..4cf17ce 100644 --- a/frontend/assets/js/students-manage.js +++ b/frontend/assets/js/students-manage.js @@ -25,13 +25,13 @@ async function loadStudents(page = 1) { html += ` - + ${userRole === '班主任' ? `` : ''}
${escapeHtml(student.student_no)}${escapeHtml(student.name)}${escapeHtml(student.name)} ${student.total_points}
${escapeHtml(student.student_no)} ${escapeHtml(student.name)} ${student.total_points}
${escapeHtml(hw.title)}
${escapeHtml(student.student_no)}${escapeHtml(student.name)}${escapeHtml(student.name)} ${escapeHtml(student.dormitory_number || '-')} ${student.total_points}${student.parent_phone ? student.parent_phone.slice(0,3) + '******' + student.parent_phone.slice(-2) : '-'}
- + ${userRole === '班主任' ? `
编辑 diff --git a/frontend/assets/js/subjects.js b/frontend/assets/js/subjects.js index 4e83ed3..628c80a 100644 --- a/frontend/assets/js/subjects.js +++ b/frontend/assets/js/subjects.js @@ -22,11 +22,11 @@ async function loadSubjects() { ${sub.is_active ? '启用' : '禁用'} - - + - +
`; }); diff --git a/frontend/student/semester_history.php b/frontend/student/semester_history.php index 7c5882a..257960c 100644 --- a/frontend/student/semester_history.php +++ b/frontend/student/semester_history.php @@ -37,8 +37,8 @@ include __DIR__ . '/../includes/header.php'; border-bottom: 2px solid transparent; } .nav .nav-item.active { - color: #667eea; - border-bottom-color: #667eea; + color: var(--color-primary); + border-bottom-color: var(--color-primary); font-weight: bold; } .semester-card { @@ -54,7 +54,7 @@ include __DIR__ . '/../includes/header.php'; align-items: center; margin-bottom: 12px; padding-bottom: 12px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--color-border-light); } .semester-name { font-size: 18px; @@ -77,7 +77,7 @@ include __DIR__ . '/../includes/header.php'; .semester-stat-value { font-size: 24px; font-weight: bold; - color: #667eea; + color: var(--color-primary); } .semester-stat-label { font-size: 12px; @@ -169,18 +169,18 @@ async function loadSemesterRecords() {
考勤统计
- 出勤 ${record.attendance_present || 0} - 缺勤 ${record.attendance_absent || 0} - 迟到 ${record.attendance_late || 0} - 请假 ${record.attendance_leave || 0} + 出勤 ${record.attendance_present || 0} + 缺勤 ${record.attendance_absent || 0} + 迟到 ${record.attendance_late || 0} + 请假 ${record.attendance_leave || 0}
作业统计
- 已交 ${record.homework_submitted || 0} - 未交 ${record.homework_not_submitted || 0} - 迟交 ${record.homework_late || 0} + 已交 ${record.homework_submitted || 0} + 未交 ${record.homework_not_submitted || 0} + 迟交 ${record.homework_late || 0}
diff --git a/sql/init.sql b/sql/init.sql index aed8b66..99f7dc0 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -204,272 +204,10 @@ CREATE TABLE IF NOT EXISTS `semester_archives` ( SET FOREIGN_KEY_CHECKS = 1; --- =========================================== --- 迁移语句:为现有表新增字段(仅在字段不存在时添加) --- =========================================== - --- conduct_records 表:添加 semester_id 字段(如不存在) -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'conduct_records' - AND COLUMN_NAME = 'semester_id' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `conduct_records` ADD COLUMN `semester_id` INT DEFAULT NULL COMMENT ''所属学期ID'' AFTER `revoked_at`', - 'SELECT ''conduct_records.semester_id already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 为新增的 semester_id 添加外键(如不存在) -SET @fk_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'conduct_records' - AND COLUMN_NAME = 'semester_id' - AND REFERENCED_TABLE_NAME IS NOT NULL -); -SET @sql = IF(@fk_exists = 0, - 'ALTER TABLE `conduct_records` ADD FOREIGN KEY (`semester_id`) REFERENCES `semesters`(`semester_id`) ON DELETE SET NULL', - 'SELECT ''conduct_records semester_id FK already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- attendance_records 表:添加 semester_id 字段(如不存在) -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND COLUMN_NAME = 'semester_id' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `attendance_records` ADD COLUMN `semester_id` INT DEFAULT NULL COMMENT ''所属学期ID'' AFTER `deduction_record_id`', - 'SELECT ''attendance_records.semester_id already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 为新增的 semester_id 添加外键(如不存在) -SET @fk_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND COLUMN_NAME = 'semester_id' - AND REFERENCED_TABLE_NAME IS NOT NULL -); -SET @sql = IF(@fk_exists = 0, - 'ALTER TABLE `attendance_records` ADD FOREIGN KEY (`semester_id`) REFERENCES `semesters`(`semester_id`) ON DELETE SET NULL', - 'SELECT ''attendance_records semester_id FK already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- attendance_records 表:添加 slot 字段(如不存在) -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND COLUMN_NAME = 'slot' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `attendance_records` ADD COLUMN `slot` VARCHAR(20) DEFAULT ''morning'' COMMENT ''时段: morning/afternoon/evening'' AFTER `date`', - 'SELECT ''attendance_records.slot already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 删除旧唯一键并添加新唯一键(含slot) -SET @uk_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND CONSTRAINT_NAME = 'uk_student_date' -); -SET @sql = IF(@uk_exists > 0, - 'ALTER TABLE `attendance_records` DROP INDEX `uk_student_date`, ADD UNIQUE KEY `uk_student_date_slot` (`student_id`, `date`, `slot`)', - 'SELECT ''uk_student_date does not exist, skipping'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 attendance_present 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'attendance_present' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `attendance_present` INT DEFAULT 0 COMMENT ''出勤次数'' AFTER `total_students`', - 'SELECT ''semester_archives.attendance_present already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 attendance_absent 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'attendance_absent' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `attendance_absent` INT DEFAULT 0 COMMENT ''缺勤次数'' AFTER `attendance_present`', - 'SELECT ''semester_archives.attendance_absent already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 attendance_late 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'attendance_late' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `attendance_late` INT DEFAULT 0 COMMENT ''迟到次数'' AFTER `attendance_absent`', - 'SELECT ''semester_archives.attendance_late already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 attendance_leave 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'attendance_leave' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `attendance_leave` INT DEFAULT 0 COMMENT ''请假次数'' AFTER `attendance_late`', - 'SELECT ''semester_archives.attendance_leave already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 homework_submitted 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'homework_submitted' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `homework_submitted` INT DEFAULT 0 COMMENT ''已交作业数'' AFTER `attendance_leave`', - 'SELECT ''semester_archives.homework_submitted already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 homework_not_submitted 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'homework_not_submitted' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `homework_not_submitted` INT DEFAULT 0 COMMENT ''未交作业数'' AFTER `homework_submitted`', - 'SELECT ''semester_archives.homework_not_submitted already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:semester_archives 表新增 homework_late 字段 -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'semester_archives' - AND COLUMN_NAME = 'homework_late' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `semester_archives` ADD COLUMN `homework_late` INT DEFAULT 0 COMMENT ''迟交作业数'' AFTER `homework_not_submitted`', - 'SELECT ''semester_archives.homework_late already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:attendance_records 表新增 slot 字段(考勤时段系统 v1.3) -SET @column_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND COLUMN_NAME = 'slot' -); -SET @sql = IF(@column_exists = 0, - 'ALTER TABLE `attendance_records` ADD COLUMN `slot` ENUM(''morning'', ''afternoon'', ''evening'') DEFAULT ''morning'' COMMENT ''考勤时段:早上/中午/晚修'' AFTER `date`', - 'SELECT ''attendance_records.slot already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 迁移:attendance_records 唯一索引从 (student_id, date) 改为 (student_id, date, slot) -SET @index_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = 'classmanagerdb' - AND TABLE_NAME = 'attendance_records' - AND INDEX_NAME = 'uk_student_date_slot' -); -SET @sql = IF(@index_exists = 0, - 'ALTER TABLE `attendance_records` DROP INDEX `uk_student_date`, ADD UNIQUE KEY `uk_student_date_slot` (`student_id`, `date`, `slot`)', - 'SELECT ''attendance_records.uk_student_date_slot already exists'' AS message' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -- 插入初始科目(仅语数英,如不存在) INSERT IGNORE INTO `subjects` (`subject_name`, `subject_code`, `sort_order`) VALUES ('语文', 'CHI', 1), ('数学', 'MATH', 2), ('英语', 'ENG', 3); -SELECT '数据库初始化/迁移完成!' AS message; - --- =========================================== --- v2.0 迁移脚本 --- =========================================== - --- 扩展密码哈希字段长度以支持bcrypt -ALTER TABLE users MODIFY COLUMN password_hash VARCHAR(255) NOT NULL; - --- 添加宿舍号字段 -SET @dbname = DATABASE(); -SET @tablename = 'students'; -SET @columnname = 'dormitory_number'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0, - 'SELECT 1', - 'ALTER TABLE students ADD COLUMN dormitory_number VARCHAR(20) DEFAULT NULL COMMENT ''宿舍号'' AFTER parent_phone' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加分数最后更新时间字段 -SET @columnname = 'points_updated_at'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0, - 'SELECT 1', - 'ALTER TABLE students ADD COLUMN points_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT ''分数最后更新时间''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; +SELECT '数据库初始化完成!' AS message; diff --git a/sql/upgrade_v2.0.sql b/sql/upgrade_v2.0.sql deleted file mode 100644 index 3769b7f..0000000 --- a/sql/upgrade_v2.0.sql +++ /dev/null @@ -1,143 +0,0 @@ --- =========================================== --- 班级操行分管理系统 - v2.0 数据库迁移脚本 --- 适用版本: v1.8 → v2.0 --- 字符集: utf8mb4 --- --- 说明: --- v2.0 主要为应用层代码变更(UI折叠菜单、扣分类型扩展、 --- 科目选择修复、页面改名、记录人优化、学期管理优化等), --- 数据库 schema 变更较少。本脚本主要处理历史数据迁移 --- 和索引优化。 --- --- 迁移内容: --- 1. 将 conduct_records.recorder_name 从用户名更新为真实姓名 --- 2. 将 attendance_records 相关记录中的 recorder_name 同步更新 --- 3. 确保 semester_id 索引存在(学期记录数统计优化) --- 4. 数据验证 --- --- 重要: 执行前请备份数据库! --- =========================================== - -USE `classmanagerdb`; - --- =========================================== --- 1. 更新 conduct_records 中的 recorder_name --- v2.0 将 recorder_name 从用户名(username)改为真实姓名(real_name) --- 需要将历史记录中的用户名更新为对应的真实姓名 --- =========================================== - --- 通过 JOIN users 表将 recorder_name 从 username 更新为 real_name -UPDATE conduct_records cr -INNER JOIN users u ON cr.recorder_id = u.user_id -SET cr.recorder_name = u.real_name -WHERE cr.recorder_name != u.real_name - OR cr.recorder_name IS NULL; - --- =========================================== --- 2. 确保学期相关索引存在 --- v2.0 新增学期记录数统计功能,需要 semester_id 索引优化查询 --- =========================================== - -SET @dbname = DATABASE(); - --- conduct_records 表 semester_id 索引 -SET @indexname = 'idx_conduct_semester_id'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = 'conduct_records' AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'ALTER TABLE conduct_records ADD INDEX idx_conduct_semester_id (semester_id)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- attendance_records 表 semester_id 索引 -SET @indexname = 'idx_attendance_semester_id'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = 'attendance_records' AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'ALTER TABLE attendance_records ADD INDEX idx_attendance_semester_id (semester_id)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- =========================================== --- 3. 确保之前版本的索引也存在(幂等兼容) --- =========================================== - --- assignments 表 subject_id 索引(v1.8 科目删除数据检查) -SET @indexname = 'idx_assignments_subject_id'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = 'assignments' AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'ALTER TABLE assignments ADD INDEX idx_assignments_subject_id (subject_id)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- conduct_records 表 student_id 索引 -SET @indexname = 'idx_conduct_student_id'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = 'conduct_records' AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'ALTER TABLE conduct_records ADD INDEX idx_conduct_student_id (student_id)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- conduct_records 表 created_at 索引 -SET @indexname = 'idx_conduct_created_at'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = 'conduct_records' AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'ALTER TABLE conduct_records ADD INDEX idx_conduct_created_at (created_at)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- =========================================== --- 4. 验证迁移结果 --- =========================================== - -SELECT '===== v2.0 数据库迁移验证 =====' AS ''; - --- 检查 recorder_name 是否已更新为真实姓名 -SELECT - 'recorder_name 迁移验证' AS `检查项`, - COUNT(*) AS `总记录数`, - SUM(CASE WHEN cr.recorder_name = u.real_name THEN 1 ELSE 0 END) AS `已匹配真实姓名`, - SUM(CASE WHEN cr.recorder_name != u.real_name AND u.real_name IS NOT NULL THEN 1 ELSE 0 END) AS `仍为用户名` -FROM conduct_records cr -INNER JOIN users u ON cr.recorder_id = u.user_id; - --- 检查学期相关索引 -SELECT - '学期索引验证' AS `检查项`, - TABLE_NAME AS `表名`, - INDEX_NAME AS `索引名` -FROM INFORMATION_SCHEMA.STATISTICS -WHERE TABLE_SCHEMA = @dbname - AND INDEX_NAME IN ('idx_conduct_semester_id', 'idx_attendance_semester_id') -GROUP BY TABLE_NAME, INDEX_NAME; - --- 检查学期记录数统计示例 -SELECT - '学期记录统计示例' AS `检查项`, - s.semester_name, - s.is_active, - (SELECT COUNT(*) FROM conduct_records WHERE semester_id = s.semester_id) AS `操行分记录数`, - (SELECT COUNT(*) FROM attendance_records WHERE semester_id = s.semester_id) AS `考勤记录数` -FROM semesters s -ORDER BY s.is_active DESC, s.created_at DESC -LIMIT 5; - -SELECT 'v2.0 数据库迁移完成!' AS message; diff --git a/sql/upgrades/v1.7.sql b/sql/upgrades/v1.7.sql new file mode 100644 index 0000000..927fd05 --- /dev/null +++ b/sql/upgrades/v1.7.sql @@ -0,0 +1,31 @@ +-- =========================================== +-- 班级操行分管理系统 - 升级至 v1.7 +-- 字符集: utf8mb4 +-- +-- 说明: 此脚本用于在已有数据库中创建版本管理系统。 +-- 适用于从 v1.7 之前的版本升级。 +-- +-- 变更内容: +-- 1. 创建 system_settings 表(用于存储版本号等系统配置) +-- 2. 插入初始版本号 1.7 +-- +-- 兼容性: 使用 IF NOT EXISTS 实现幂等,phpMyAdmin 可直接执行 +-- =========================================== + +-- =========================================== +-- 升级步骤 1: 创建 system_settings 表 +-- =========================================== + +CREATE TABLE IF NOT EXISTS `system_settings` ( + `setting_key` VARCHAR(50) PRIMARY KEY, + `setting_value` VARCHAR(255) NOT NULL, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- =========================================== +-- 升级步骤 2: 插入初始版本号 +-- =========================================== + +INSERT INTO `system_settings` (`setting_key`, `setting_value`) +VALUES ('db_version', '1.7') +ON DUPLICATE KEY UPDATE `setting_value` = '1.7'; diff --git a/sql/upgrades/v1.8.sql b/sql/upgrades/v1.8.sql new file mode 100644 index 0000000..b63c365 --- /dev/null +++ b/sql/upgrades/v1.8.sql @@ -0,0 +1,131 @@ +-- =========================================== +-- 班级操行分管理系统 - v1.7 → v1.8 升级脚本 +-- 字符集: utf8mb4 +-- +-- 变更内容: +-- 1. conduct_records 表添加 related_type 列 +-- 2. conduct_records 表添加 semester_id 列 + 外键 +-- 3. attendance_records 表添加 semester_id 列 + 外键 +-- +-- 兼容性: 使用存储过程实现幂等,phpMyAdmin 可直接执行 +-- =========================================== + +-- =========================================== +-- 升级步骤 1: conduct_records 添加 related_type 列 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND COLUMN_NAME = 'related_type' + ) THEN + ALTER TABLE `conduct_records` + ADD COLUMN `related_type` ENUM('manual', 'homework', 'attendance') DEFAULT 'manual' + AFTER `recorder_name`; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 2: conduct_records 添加 semester_id 列 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND COLUMN_NAME = 'semester_id' + ) THEN + ALTER TABLE `conduct_records` + ADD COLUMN `semester_id` INT DEFAULT NULL COMMENT '所属学期ID' + AFTER `revoked_at`; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 3: conduct_records 添加 semester_id 外键 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND COLUMN_NAME = 'semester_id' + AND REFERENCED_TABLE_NAME IS NOT NULL + ) THEN + ALTER TABLE `conduct_records` + ADD FOREIGN KEY (`semester_id`) REFERENCES `semesters`(`semester_id`) ON DELETE SET NULL; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 4: attendance_records 添加 semester_id 列 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'attendance_records' + AND COLUMN_NAME = 'semester_id' + ) THEN + ALTER TABLE `attendance_records` + ADD COLUMN `semester_id` INT DEFAULT NULL COMMENT '所属学期ID' + AFTER `deduction_record_id`; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 5: attendance_records 添加 semester_id 外键 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'attendance_records' + AND COLUMN_NAME = 'semester_id' + AND REFERENCED_TABLE_NAME IS NOT NULL + ) THEN + ALTER TABLE `attendance_records` + ADD FOREIGN KEY (`semester_id`) REFERENCES `semesters`(`semester_id`) ON DELETE SET NULL; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; diff --git a/sql/upgrades/v2.0.1.sql b/sql/upgrades/v2.0.1.sql new file mode 100644 index 0000000..227317d --- /dev/null +++ b/sql/upgrades/v2.0.1.sql @@ -0,0 +1,6 @@ +-- =========================================== +-- 班级操行分管理系统 - v2.0 → v2.0.1 升级脚本 +-- 字符集: utf8mb4 +-- +-- 说明: v2.0.1 为纯前端修改版本,无数据库 schema 变更。 +-- =========================================== diff --git a/sql/upgrades/v2.0.sql b/sql/upgrades/v2.0.sql new file mode 100644 index 0000000..dc0c1a4 --- /dev/null +++ b/sql/upgrades/v2.0.sql @@ -0,0 +1,145 @@ +-- =========================================== +-- 班级操行分管理系统 - v1.8 → v2.0 升级脚本 +-- 字符集: utf8mb4 +-- +-- 变更内容: +-- 1. 添加性能索引: conduct_records.semester_id +-- 2. 添加性能索引: attendance_records.semester_id +-- 3. 添加性能索引: conduct_records.student_id +-- 4. 数据迁移: recorder_name 从 username 更新为 real_name +-- 5. students 表添加 dormitory_number 列 +-- 6. students 表添加 points_updated_at 列 +-- +-- 兼容性: 使用存储过程实现幂等,phpMyAdmin 可直接执行 +-- =========================================== + +-- =========================================== +-- 升级步骤 1: 添加 conduct_records.semester_id 索引 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND INDEX_NAME = 'idx_conduct_semester' + ) THEN + CREATE INDEX `idx_conduct_semester` ON `conduct_records`(`semester_id`); + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 2: 添加 attendance_records.semester_id 索引 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'attendance_records' + AND INDEX_NAME = 'idx_attendance_semester' + ) THEN + CREATE INDEX `idx_attendance_semester` ON `attendance_records`(`semester_id`); + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 3: 添加 conduct_records.student_id 索引 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND INDEX_NAME = 'idx_conduct_student' + ) THEN + CREATE INDEX `idx_conduct_student` ON `conduct_records`(`student_id`); + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 4: 数据迁移 - recorder_name 从 username 更新为 real_name +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + UPDATE `conduct_records` cr + INNER JOIN `users` u ON cr.recorder_id = u.user_id + SET cr.recorder_name = u.real_name + WHERE cr.recorder_name = u.username; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 5: students 表添加 dormitory_number 列 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'students' + AND COLUMN_NAME = 'dormitory_number' + ) THEN + ALTER TABLE `students` + ADD COLUMN `dormitory_number` VARCHAR(20) DEFAULT NULL COMMENT '宿舍号' + AFTER `parent_phone`; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; + +-- =========================================== +-- 升级步骤 6: students 表添加 points_updated_at 列 +-- =========================================== + +DROP PROCEDURE IF EXISTS `upgrade_step`; +DELIMITER $$ +CREATE PROCEDURE `upgrade_step`() +BEGIN + IF NOT EXISTS ( + SELECT * FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'students' + AND COLUMN_NAME = 'points_updated_at' + ) THEN + ALTER TABLE `students` + ADD COLUMN `points_updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '分数最后更新时间'; + END IF; +END$$ +DELIMITER ; + +CALL `upgrade_step`(); +DROP PROCEDURE IF EXISTS `upgrade_step`; diff --git a/sql/upgrades/v2.1.sql b/sql/upgrades/v2.1.sql new file mode 100644 index 0000000..f3ff3b9 --- /dev/null +++ b/sql/upgrades/v2.1.sql @@ -0,0 +1,102 @@ +-- =========================================== +-- 班级操行分管理系统 - v2.0.1 → v2.1 升级脚本 +-- 主要内容:添加缺失的数据库索引,优化查询性能 +-- =========================================== + +DELIMITER $$ + +-- conduct_records 表:添加 student_id + created_at 联合索引(学生端查询历史记录) +CREATE PROCEDURE upgrade_add_conduct_student_created_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND INDEX_NAME = 'idx_student_created' + ) THEN + ALTER TABLE `conduct_records` ADD INDEX `idx_student_created` (`student_id`, `created_at`); + END IF; +END$$ + +-- conduct_records 表:添加 recorder_id 索引(班干查询自己记录的) +CREATE PROCEDURE upgrade_add_conduct_recorder_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'conduct_records' + AND INDEX_NAME = 'idx_recorder_id' + ) THEN + ALTER TABLE `conduct_records` ADD INDEX `idx_recorder_id` (`recorder_id`); + END IF; +END$$ + +-- attendance_records 表:添加 date 索引(按日期查询考勤记录) +CREATE PROCEDURE upgrade_add_attendance_date_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'attendance_records' + AND INDEX_NAME = 'idx_date' + ) THEN + ALTER TABLE `attendance_records` ADD INDEX `idx_date` (`date`); + END IF; +END$$ + +-- login_logs 表:添加 username + created_at 联合索引(查询登录历史) +CREATE PROCEDURE upgrade_add_login_username_created_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'login_logs' + AND INDEX_NAME = 'idx_username_created' + ) THEN + ALTER TABLE `login_logs` ADD INDEX `idx_username_created` (`username`, `created_at`); + END IF; +END$$ + +-- operation_logs 表:添加 operator_id + created_at 联合索引(查询操作历史) +CREATE PROCEDURE upgrade_add_operation_operator_created_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'operation_logs' + AND INDEX_NAME = 'idx_operator_created' + ) THEN + ALTER TABLE `operation_logs` ADD INDEX `idx_operator_created` (`operator_id`, `created_at`); + END IF; +END$$ + +-- semester_archives 表:添加 semester_id 索引(查询学期归档数据) +CREATE PROCEDURE upgrade_add_archive_semester_idx() +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'semester_archives' + AND INDEX_NAME = 'idx_semester_id' + ) THEN + ALTER TABLE `semester_archives` ADD INDEX `idx_semester_id` (`semester_id`); + END IF; +END$$ + +DELIMITER ; + +-- 执行所有升级存储过程 +CALL upgrade_add_conduct_student_created_idx(); +CALL upgrade_add_conduct_recorder_idx(); +CALL upgrade_add_attendance_date_idx(); +CALL upgrade_add_login_username_created_idx(); +CALL upgrade_add_operation_operator_created_idx(); +CALL upgrade_add_archive_semester_idx(); + +-- 清理存储过程 +DROP PROCEDURE IF EXISTS upgrade_add_conduct_student_created_idx; +DROP PROCEDURE IF EXISTS upgrade_add_conduct_recorder_idx; +DROP PROCEDURE IF EXISTS upgrade_add_attendance_date_idx; +DROP PROCEDURE IF EXISTS upgrade_add_login_username_created_idx; +DROP PROCEDURE IF EXISTS upgrade_add_operation_operator_created_idx; +DROP PROCEDURE IF EXISTS upgrade_add_archive_semester_idx; diff --git a/upgrade.php b/upgrade.php new file mode 100644 index 0000000..5258b9c --- /dev/null +++ b/upgrade.php @@ -0,0 +1,532 @@ +query("SELECT setting_value FROM system_settings WHERE setting_key = 'db_version'"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row ? $row['setting_value'] : '0.0.0'; + } catch (PDOException $e) { + return '0.0.0'; + } +} + +/** + * 获取需要执行的升级步骤 + */ +function getUpgradeSteps($currentVersion, $targetVersion) { + $allVersions = [ + '1.7' => __DIR__ . '/sql/upgrades/v1.7.sql', + '1.8' => __DIR__ . '/sql/upgrades/v1.8.sql', + '2.0' => __DIR__ . '/sql/upgrades/v2.0.sql', + '2.0.1' => __DIR__ . '/sql/upgrades/v2.0.1.sql', + '2.1' => __DIR__ . '/sql/upgrades/v2.1.sql', + ]; + + $steps = []; + foreach ($allVersions as $version => $sqlFile) { + if (version_compare($version, $currentVersion, '>') && + version_compare($version, $targetVersion, '<=')) { + $steps[$version] = $sqlFile; + } + } + + uksort($steps, 'version_compare'); + return $steps; +} + +/** + * 执行单个版本的升级 SQL + */ +function executeUpgrade($pdo, $version, $sqlFile) { + if (!file_exists($sqlFile)) { + throw new RuntimeException("SQL 文件不存在: {$sqlFile}"); + } + + $sql = file_get_contents($sqlFile); + + if (trim($sql) === '' || trim($sql) === '--') { + // 空文件或纯注释,无需执行 SQL,仅更新版本号 + } else { + $pdo->exec($sql); + } + + // 更新版本号(使用预处理语句防止 SQL 注入) + $stmt = $pdo->prepare( + "INSERT INTO system_settings (setting_key, setting_value) VALUES ('db_version', :version) + ON DUPLICATE KEY UPDATE setting_value = :version" + ); + $stmt->execute([':version' => $version]); +} + +// =========================================== +// 主逻辑 +// =========================================== + +// POST 模式:执行单个升级步骤(依次执行) +if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_GET['action'] ?? '') === 'step') { + header('Content-Type: application/json; charset=utf-8'); + + $stepVersion = $_GET['version'] ?? ''; + if (empty($stepVersion)) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => '缺少版本号参数']); + exit(); + } + + try { + $envPath = __DIR__ . '/backend/.env'; + $config = readEnvConfig($envPath); + + $dsn = "mysql:host={$config['DB_HOST']};port={$config['DB_PORT']};dbname={$config['DB_NAME']};charset=utf8mb4"; + $pdo = new PDO($dsn, $config['DB_USER'], $config['DB_PASSWORD'], [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ]); + + // 获取该版本对应的 SQL 文件 + $allVersions = [ + '1.7' => __DIR__ . '/sql/upgrades/v1.7.sql', + '1.8' => __DIR__ . '/sql/upgrades/v1.8.sql', + '2.0' => __DIR__ . '/sql/upgrades/v2.0.sql', + '2.0.1' => __DIR__ . '/sql/upgrades/v2.0.1.sql', + '2.1' => __DIR__ . '/sql/upgrades/v2.1.sql', + ]; + + if (!isset($allVersions[$stepVersion])) { + throw new RuntimeException("未知版本: {$stepVersion}"); + } + + $sqlFile = $allVersions[$stepVersion]; + $shortFile = basename($sqlFile); + + executeUpgrade($pdo, $stepVersion, $sqlFile); + + // 重新检测当前版本 + $newVersion = detectCurrentVersion($pdo); + + echo json_encode([ + 'success' => true, + 'version' => $stepVersion, + 'message' => "升级至 v{$stepVersion} 成功 ({$shortFile})", + 'current' => $newVersion + ]); + } catch (Exception $e) { + http_response_code(500); + echo json_encode([ + 'success' => false, + 'version' => $stepVersion, + 'error' => "升级至 v{$stepVersion} 失败: " . $e->getMessage() + ]); + } + exit(); +} + +// POST 模式:执行升级(AJAX 请求) +if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_GET['action'] ?? '') === 'execute') { + header('Content-Type: application/json; charset=utf-8'); + + $upgradeLog = []; + $currentVersion = '未知'; + $targetVersion = '未知'; + + try { + $envPath = __DIR__ . '/backend/.env'; + $config = readEnvConfig($envPath); + + $dsn = "mysql:host={$config['DB_HOST']};port={$config['DB_PORT']};dbname={$config['DB_NAME']};charset=utf8mb4"; + $pdo = new PDO($dsn, $config['DB_USER'], $config['DB_PASSWORD'], [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ]); + + $currentVersion = detectCurrentVersion($pdo); + + $versionFile = __DIR__ . '/VERSION'; + if (!file_exists($versionFile)) { + throw new RuntimeException('VERSION 文件不存在'); + } + $targetVersion = trim(file_get_contents($versionFile)); + + $upgradeSteps = getUpgradeSteps($currentVersion, $targetVersion); + + if (empty($upgradeSteps)) { + echo json_encode([ + 'success' => true, + 'current' => $currentVersion, + 'target' => $targetVersion, + 'steps' => [['version' => '', 'status' => 'uptodate', 'message' => '数据库已是最新版本,无需升级。']] + ]); + exit(); + } + + $pdo->beginTransaction(); + try { + foreach ($upgradeSteps as $version => $sqlFile) { + $shortFile = basename($sqlFile); + try { + executeUpgrade($pdo, $version, $sqlFile); + $upgradeLog[] = [ + 'version' => $version, + 'status' => 'success', + 'message' => "升级至 v{$version} 成功 ({$shortFile})" + ]; + } catch (Exception $e) { + $upgradeLog[] = [ + 'version' => $version, + 'status' => 'error', + 'message' => "升级至 v{$version} 失败 ({$shortFile}): " . $e->getMessage() + ]; + throw $e; + } + } + $pdo->commit(); + } catch (Exception $e) { + $pdo->rollBack(); + throw $e; + } + + echo json_encode([ + 'success' => true, + 'current' => $currentVersion, + 'target' => $targetVersion, + 'steps' => $upgradeLog + ]); + } catch (Exception $e) { + http_response_code(500); + echo json_encode([ + 'success' => false, + 'current' => $currentVersion, + 'target' => $targetVersion, + 'steps' => $upgradeLog, + 'error' => $e->getMessage() + ]); + } + exit(); +} + +// GET 模式:显示升级信息页面 +$currentVersion = '未知'; +$targetVersion = '未知'; +$upgradeSteps = []; +$hasError = false; +$errorMessage = ''; +$isUpToDate = false; + +try { + $envPath = __DIR__ . '/backend/.env'; + $config = readEnvConfig($envPath); + + $dsn = "mysql:host={$config['DB_HOST']};port={$config['DB_PORT']};dbname={$config['DB_NAME']};charset=utf8mb4"; + $pdo = new PDO($dsn, $config['DB_USER'], $config['DB_PASSWORD'], [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ]); + + $currentVersion = detectCurrentVersion($pdo); + + $versionFile = __DIR__ . '/VERSION'; + if (!file_exists($versionFile)) { + throw new RuntimeException('VERSION 文件不存在: ' . $versionFile); + } + $targetVersion = trim(file_get_contents($versionFile)); + + $upgradeSteps = getUpgradeSteps($currentVersion, $targetVersion); + $isUpToDate = empty($upgradeSteps); +} catch (Exception $e) { + $hasError = true; + $errorMessage = $e->getMessage(); +} + +?> + + + + + + 系统升级 - 班级操行分管理系统 + + + +
+
+

班级操行分管理系统 - 数据库升级

+

自动检测版本并执行增量升级

+
+
+
+ 当前数据库版本 + +
+
+ 目标版本 + +
+ + +
+ 错误: +
+ +
+ ✓ 数据库已是最新版本,无需升级。 +
+ +
待执行升级步骤
+ $sqlFile): ?> +
+ + 升级至 v () +
+ + +
+ ⚠️ 升级前请确保已备份数据库,升级过程中请勿关闭页面。 +
+ +
+ +
+ + + +
+ +
+ + + + + +