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'; 显示前 % 的学生 - - + +
| ${escapeHtml(hw.title)} | 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 += `|||||||
| ${escapeHtml(student.student_no)} | -${escapeHtml(student.name)} | +${escapeHtml(student.name)} | ${escapeHtml(student.dormitory_number || '-')} | ${student.total_points} | ${userRole === '班主任' ? `${student.parent_phone ? student.parent_phone.slice(0,3) + '******' + student.parent_phone.slice(-2) : '-'} | ` : ''}
-
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();
+}
+
+?>
+
+
+
+
+
+ 考勤统计
- 出勤 ${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}
+
+
+
+
+
+
+
+
+ 班级操行分管理系统 - 数据库升级+自动检测版本并执行增量升级 +
+
+
+
+ 当前数据库版本
+
+
+
+ 目标版本
+
+
+
+
+
+ 错误:
+
+
+
+ ✓ 数据库已是最新版本,无需升级。
+
+
+ 待执行升级步骤
+ $sqlFile): ?>
+
+
+
+
+
+
+
+ ⚠️ 升级前请确保已备份数据库,升级过程中请勿关闭页面。
+
+
+
+
+
+
+
+ |