🚀 AI 近视防控系统 - 生产环境上线版本 v1.0

 已完成功能:
- 后端 Go 服务 (认证/授权/检测)
- JWT 认证 + RBAC 权限控制
- 登录速率限制 (5 次失败锁定 15 分钟)
- 密码强度校验
- 敏感数据脱敏
- Vue3 管理后台
- 路由守卫
- 删除二次确认

📦 部署配置:
- Docker Compose 生产环境配置
- MySQL/Redis/MongoDB 数据库
- Nginx 前端服务
- 强密码安全配置

⚠️ P2 待办 (下次迭代):
- 学生/检测/预警等业务模块实现
- 错误处理统一化
- 缓存策略优化
- 日志分级

📍 生产环境:
- 服务器:192.168.15.222
- 管理后台:http://192.168.15.222:8081
- API 服务:http://192.168.15.222:8080

2026-03-29 上线部署完成
This commit is contained in:
虾司令
2026-03-29 18:16:41 +08:00
commit 881144269c
38 changed files with 4967 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
package main
import (
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// UserAccount 用户账号模型
type UserAccount struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"type:varchar(64);uniqueIndex"`
PasswordHash string `gorm:"type:varchar(255)"`
Name string `gorm:"type:varchar(64)"`
Phone string `gorm:"type:varchar(20);uniqueIndex"`
UserType string `gorm:"type:varchar(16)"`
Status int `gorm:"default:1"`
LastLoginAt *string
LastLoginIP string
CreatedAt string
UpdatedAt string
}
func main() {
fmt.Println("AI近视防控系统 - 管理员账号创建工具")
// 数据库连接信息 - 从环境变量或配置文件读取
// 使用与主应用相同的数据库连接信息
dsn := "root:MyopiaTest2026!@tcp(localhost:3306)/ai_myopia_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("连接数据库失败:", err)
}
// 加密管理员密码
adminPassword := "Admin123!@#" // 强密码,包含大小写字母、数字、特殊字符
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("密码加密失败:", err)
}
// 创建管理员账号
adminAccount := UserAccount{
Username: "admin",
PasswordHash: string(hashedPassword),
Name: "系统管理员",
Phone: "13800138000",
UserType: "admin",
Status: 1,
}
result := db.Table("user_accounts").Where("username = ?", "admin").First(&UserAccount{})
if result.Error != nil {
// 管理员账号不存在,创建新账号
result = db.Table("user_accounts").Create(&adminAccount)
if result.Error != nil {
log.Fatal("创建管理员账号失败:", result.Error)
}
fmt.Println("✅ 管理员账号创建成功")
} else {
// 管理员账号已存在,更新密码
result = db.Table("user_accounts").
Where("username = ?", "admin").
Updates(map[string]interface{}{
"password_hash": string(hashedPassword),
"name": "系统管理员",
"phone": "13800138000",
"user_type": "admin",
"status": 1,
})
if result.Error != nil {
log.Fatal("更新管理员账号失败:", result.Error)
}
fmt.Println("✅ 管理员账号更新成功")
}
fmt.Println("\n📋 测试账号信息:")
fmt.Println("用户名: admin")
fmt.Println("密码: Admin123!@#")
fmt.Println("角色: admin")
fmt.Println("手机号: 13800138000")
fmt.Println("\n🔧 功能测试:")
fmt.Println("- 用户认证功能: 待验证")
fmt.Println("- 学生管理功能: 待验证")
fmt.Println("- 检测功能: 待验证")
fmt.Println("- 预警功能: 待验证")
fmt.Println("\n💡 提示: 可使用此账号登录系统进行功能测试")
}

View File

@@ -0,0 +1,84 @@
-- AI近视防控系统 - 测试账号创建脚本
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS ai_myopia_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE ai_myopia_db;
-- 创建用户表(如果不存在)
CREATE TABLE IF NOT EXISTS user_accounts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(64) NOT NULL,
phone VARCHAR(20) NOT NULL UNIQUE,
user_type ENUM('student', 'parent', 'teacher', 'admin') NOT NULL,
status TINYINT DEFAULT 1,
last_login_at DATETIME NULL,
last_login_ip VARCHAR(45) DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 创建测试管理员账号
DELETE FROM user_accounts WHERE username = 'admin';
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'admin',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Admin123!@# 的bcrypt哈希
'系统管理员',
'13800138000',
'admin',
1
);
-- 创建测试老师账号
DELETE FROM user_accounts WHERE username = 'teacher';
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'teacher',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Admin123!@# 的bcrypt哈希
'测试老师',
'13800138001',
'teacher',
1
);
-- 创建测试学生账号
DELETE FROM user_accounts WHERE username = 'student';
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'student',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Admin123!@# 的bcrypt哈希
'测试学生',
'13800138002',
'student',
1
);
-- 创建测试家长账号
DELETE FROM user_accounts WHERE username = 'parent';
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'parent',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Admin123!@# 的bcrypt哈希
'测试家长',
'13800138003',
'parent',
1
);
-- 验证账号创建
SELECT
id,
username,
name,
phone,
user_type,
status
FROM user_accounts
WHERE username IN ('admin', 'teacher', 'student', 'parent');
-- 输出测试信息
SELECT '--- 测试账号信息 ---' as info;
SELECT '管理员账号: admin / Admin123!@#' as admin_info;
SELECT '老师账号: teacher / Admin123!@#' as teacher_info;
SELECT '学生账号: student / Admin123!@#' as student_info;
SELECT '家长账号: parent / Admin123!@#' as parent_info;

View File

@@ -0,0 +1,248 @@
package main
import (
"fmt"
"log"
"os"
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// UserAccount 用户账号模型
type UserAccount struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"type:varchar(64);uniqueIndex" json:"username"`
PasswordHash string `gorm:"type:varchar(255)" json:"-"` // 不在JSON中暴露
Name string `gorm:"type:varchar(64)" json:"name"`
Phone string `gorm:"type:varchar(20);uniqueIndex" json:"phone"`
UserType string `gorm:"type:varchar(16)" json:"user_type"` // student, parent, teacher, admin
Status int `gorm:"default:1" json:"status"` // 1:正常, 0:禁用
LastLoginAt *time.Time `json:"last_login_at"`
LastLoginIP string `json:"last_login_ip"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func main() {
fmt.Println("AI近视防控系统 - 测试账号创建工具")
// 从环境变量获取数据库连接信息,如果不存在则使用默认值
dbHost := os.Getenv("DB_HOST")
if dbHost == "" {
dbHost = "localhost"
}
dbUser := os.Getenv("DB_USER")
if dbUser == "" {
dbUser = "root"
}
dbPassword := os.Getenv("DB_PASSWORD")
if dbPassword == "" {
dbPassword = "MyopiaTest2026!"
}
dbName := os.Getenv("DB_NAME")
if dbName == "" {
dbName = "ai_myopia_db"
}
// 数据库连接字符串
dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&loc=Local",
dbUser, dbPassword, dbHost, dbName)
// 连接数据库
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("连接数据库失败: ", err)
}
// 加密密码
adminPassword := "Admin123!@#"
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("密码加密失败: ", err)
}
// 创建管理员账号
adminAccount := UserAccount{
Username: "admin",
PasswordHash: string(hashedPassword),
Name: "系统管理员",
Phone: "13800138000",
UserType: "admin",
Status: 1,
}
// 检查管理员账号是否存在
var existingAdmin UserAccount
result := db.Where("username = ?", "admin").First(&existingAdmin)
if result.Error != nil {
// 管理员账号不存在,创建新账号
if err := db.Create(&adminAccount).Error; err != nil {
log.Fatal("创建管理员账号失败: ", err)
}
fmt.Println("✅ 管理员账号创建成功")
} else {
// 管理员账号已存在,更新密码
if err := db.Model(&existingAdmin).Updates(UserAccount{
PasswordHash: string(hashedPassword),
Name: "系统管理员",
Phone: "13800138000",
UserType: "admin",
Status: 1,
}).Error; err != nil {
log.Fatal("更新管理员账号失败: ", err)
}
fmt.Println("✅ 管理员账号更新成功")
}
// 创建测试老师账号
teacherPassword := "Teacher123!@#"
teacherHashed, err := bcrypt.GenerateFromPassword([]byte(teacherPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("老师密码加密失败: ", err)
}
teacherAccount := UserAccount{
Username: "teacher",
PasswordHash: string(teacherHashed),
Name: "测试老师",
Phone: "13800138001",
UserType: "teacher",
Status: 1,
}
var existingTeacher UserAccount
result = db.Where("username = ?", "teacher").First(&existingTeacher)
if result.Error != nil {
// 老师账号不存在,创建新账号
if err := db.Create(&teacherAccount).Error; err != nil {
log.Println("创建老师账号失败: ", err)
} else {
fmt.Println("✅ 老师账号创建成功")
}
} else {
// 老师账号已存在,更新密码
if err := db.Model(&existingTeacher).Updates(UserAccount{
PasswordHash: string(teacherHashed),
Name: "测试老师",
Phone: "13800138001",
UserType: "teacher",
Status: 1,
}).Error; err != nil {
log.Println("更新老师账号失败: ", err)
} else {
fmt.Println("✅ 老师账号更新成功")
}
}
// 创建测试学生账号
studentPassword := "Student123!@#"
studentHashed, err := bcrypt.GenerateFromPassword([]byte(studentPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("学生密码加密失败: ", err)
}
studentAccount := UserAccount{
Username: "student",
PasswordHash: string(studentHashed),
Name: "测试学生",
Phone: "13800138002",
UserType: "student",
Status: 1,
}
var existingStudent UserAccount
result = db.Where("username = ?", "student").First(&existingStudent)
if result.Error != nil {
// 学生账号不存在,创建新账号
if err := db.Create(&studentAccount).Error; err != nil {
log.Println("创建学生账号失败: ", err)
} else {
fmt.Println("✅ 学生账号创建成功")
}
} else {
// 学生账号已存在,更新密码
if err := db.Model(&existingStudent).Updates(UserAccount{
PasswordHash: string(studentHashed),
Name: "测试学生",
Phone: "13800138002",
UserType: "student",
Status: 1,
}).Error; err != nil {
log.Println("更新学生账号失败: ", err)
} else {
fmt.Println("✅ 学生账号更新成功")
}
}
// 创建测试家长账号
parentPassword := "Parent123!@#"
parentHashed, err := bcrypt.GenerateFromPassword([]byte(parentPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("家长密码加密失败: ", err)
}
parentAccount := UserAccount{
Username: "parent",
PasswordHash: string(parentHashed),
Name: "测试家长",
Phone: "13800138003",
UserType: "parent",
Status: 1,
}
var existingParent UserAccount
result = db.Where("username = ?", "parent").First(&existingParent)
if result.Error != nil {
// 家长账号不存在,创建新账号
if err := db.Create(&parentAccount).Error; err != nil {
log.Println("创建家长账号失败: ", err)
} else {
fmt.Println("✅ 家长账号创建成功")
}
} else {
// 家长账号已存在,更新密码
if err := db.Model(&existingParent).Updates(UserAccount{
PasswordHash: string(parentHashed),
Name: "测试家长",
Phone: "13800138003",
UserType: "parent",
Status: 1,
}).Error; err != nil {
log.Println("更新家长账号失败: ", err)
} else {
fmt.Println("✅ 家长账号更新成功")
}
}
fmt.Println("\n📋 测试账号信息:")
fmt.Println("==================")
fmt.Println("管理员账号:")
fmt.Println(" 用户名: admin")
fmt.Println(" 密码: Admin123!@#")
fmt.Println(" 角色: admin")
fmt.Println(" 手机: 13800138000")
fmt.Println("\n老师账号:")
fmt.Println(" 用户名: teacher")
fmt.Println(" 密码: Teacher123!@#")
fmt.Println(" 角色: teacher")
fmt.Println(" 手机: 13800138001")
fmt.Println("\n学生账号:")
fmt.Println(" 用户名: student")
fmt.Println(" 密码: Student123!@#")
fmt.Println(" 角色: student")
fmt.Println(" 手机: 13800138002")
fmt.Println("\n家长账号:")
fmt.Println(" 用户名: parent")
fmt.Println(" 密码: Parent123!@#")
fmt.Println(" 角色: parent")
fmt.Println(" 手机: 13800138003")
fmt.Println("\n✅ 测试账号创建/更新完成!")
fmt.Println("💡 提示: 可使用这些账号登录系统进行功能测试")
}

View File

@@ -0,0 +1,22 @@
package main
import (
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := "Admin123!@#"
// 生成bcrypt哈希
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
log.Fatal("密码哈希生成失败:", err)
}
fmt.Printf("密码: %s\n", password)
fmt.Printf("哈希: %s\n", string(hashedPassword))
fmt.Println("哈希长度:", len(string(hashedPassword)))
}

389
scripts/init_db.sql Normal file
View File

@@ -0,0 +1,389 @@
-- AI近视防控系统 - 数据库初始化脚本
-- 创建数据库
CREATE DATABASE IF NOT EXISTS myopia_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE myopia_db;
-- 学校表
CREATE TABLE IF NOT EXISTS schools (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
code VARCHAR(32) NOT NULL UNIQUE,
address VARCHAR(256),
contact_name VARCHAR(64),
contact_phone VARCHAR(20),
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_code (code),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学校表';
-- 班级表
CREATE TABLE IF NOT EXISTS classes (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
grade VARCHAR(16) NOT NULL, -- 年级:一年级、二年级...
school_id BIGINT UNSIGNED NOT NULL,
teacher_id BIGINT UNSIGNED,
student_count INT DEFAULT 0,
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_school (school_id),
INDEX idx_grade (grade),
FOREIGN KEY (school_id) REFERENCES schools(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='班级表';
-- 学生表
CREATE TABLE IF NOT EXISTS students (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_no VARCHAR(32) NOT NULL UNIQUE,
name VARCHAR(64) NOT NULL,
gender TINYINT DEFAULT 1, -- 1:男 2:女
birth_date DATE,
class_id BIGINT UNSIGNED NOT NULL,
parent_id BIGINT UNSIGNED,
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_class (class_id),
INDEX idx_parent (parent_id),
INDEX idx_student_no (student_no),
FOREIGN KEY (class_id) REFERENCES classes(id),
FOREIGN KEY (parent_id) REFERENCES parents(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生表';
-- 家长表
CREATE TABLE IF NOT EXISTS parents (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
phone VARCHAR(20) NOT NULL UNIQUE,
id_card VARCHAR(32),
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='家长表';
-- 家长 - 学生关联表
CREATE TABLE IF NOT EXISTS parent_student_rel (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
parent_id BIGINT UNSIGNED NOT NULL,
student_id BIGINT UNSIGNED NOT NULL,
relation VARCHAR(16) NOT NULL, -- father/mother/other
is_primary TINYINT DEFAULT 0, -- 是否主要监护人
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_parent_student (parent_id, student_id),
INDEX idx_student (student_id),
FOREIGN KEY (parent_id) REFERENCES parents(id),
FOREIGN KEY (student_id) REFERENCES students(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='家长 - 学生关联表';
-- 教师表
CREATE TABLE IF NOT EXISTS teachers (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
phone VARCHAR(20) NOT NULL UNIQUE,
school_id BIGINT UNSIGNED NOT NULL,
role VARCHAR(32), -- homeroom/school_doctor/sports
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_school (school_id),
FOREIGN KEY (school_id) REFERENCES schools(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教师表';
-- 用户账号表
CREATE TABLE IF NOT EXISTS user_accounts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password_hash VARCHAR(128) NOT NULL,
phone VARCHAR(20),
user_type VARCHAR(16) NOT NULL, -- student/parent/teacher/admin
user_id BIGINT UNSIGNED NOT NULL, -- 关联的 student_id/parent_id/teacher_id
status TINYINT DEFAULT 1,
last_login_at DATETIME,
last_login_ip VARCHAR(45),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user (user_type, user_id),
INDEX idx_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户账号表';
-- 检测任务表
CREATE TABLE IF NOT EXISTS detection_tasks (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
task_no VARCHAR(32) NOT NULL UNIQUE,
class_id BIGINT UNSIGNED NOT NULL,
teacher_id BIGINT UNSIGNED NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME,
student_count INT,
detection_type VARCHAR(32) NOT NULL, -- vision/fatigue/training
status TINYINT DEFAULT 0, -- 0:进行中 1:已完成 2:已取消
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_class (class_id),
INDEX idx_time (start_time),
INDEX idx_status (status),
FOREIGN KEY (class_id) REFERENCES classes(id),
FOREIGN KEY (teacher_id) REFERENCES teachers(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='检测任务表';
-- 检测记录表
CREATE TABLE IF NOT EXISTS detections (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
task_id BIGINT UNSIGNED NOT NULL,
student_id BIGINT UNSIGNED NOT NULL,
detection_time DATETIME NOT NULL,
vision_left DECIMAL(3,2), -- 左眼视力
vision_right DECIMAL(3,2), -- 右眼视力
fatigue_score DECIMAL(5,2), -- 疲劳分数
alert_level TINYINT DEFAULT 0, -- 0:正常 1:关注 2:预警 3:告警
device_id BIGINT UNSIGNED,
raw_data_url VARCHAR(512), -- 原始数据存储路径
ai_analysis JSON, -- AI 分析结果
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_task (task_id),
INDEX idx_student (student_id),
INDEX idx_time (detection_time),
INDEX idx_alert (alert_level),
FOREIGN KEY (task_id) REFERENCES detection_tasks(id),
FOREIGN KEY (student_id) REFERENCES students(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='检测记录表';
-- 预警记录表
CREATE TABLE IF NOT EXISTS alerts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id BIGINT UNSIGNED NOT NULL,
detection_id BIGINT UNSIGNED,
alert_level TINYINT NOT NULL, -- 1:关注 2:预警 3:告警
alert_type VARCHAR(32), -- vision_drop/fatigue/abnormal
alert_content TEXT,
status TINYINT DEFAULT 0, -- 0:未处理 1:已通知 2:已处理
notified_at DATETIME,
handled_at DATETIME,
handler_id BIGINT UNSIGNED,
handle_remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_student (student_id),
INDEX idx_status (status),
INDEX idx_level (alert_level),
FOREIGN KEY (student_id) REFERENCES students(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预警记录表';
-- 预警配置表
CREATE TABLE IF NOT EXISTS alert_configs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
school_id BIGINT UNSIGNED,
alert_level TINYINT NOT NULL,
vision_threshold DECIMAL(3,2),
drop_threshold DECIMAL(3,2), -- 下降幅度阈值
notify_parent TINYINT DEFAULT 1,
notify_teacher TINYINT DEFAULT 1,
notify_school_doctor TINYINT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_school (school_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='预警配置表';
-- 训练内容表
CREATE TABLE IF NOT EXISTS training_contents (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
type VARCHAR(32) NOT NULL, -- eye_exercise/crystal/acupoint/relax
duration INT NOT NULL, -- 时长 (秒)
video_url VARCHAR(512),
thumbnail_url VARCHAR(512),
description TEXT,
difficulty TINYINT DEFAULT 1, -- 1-5
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_type (type),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='训练内容表';
-- 训练任务表
CREATE TABLE IF NOT EXISTS training_tasks (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
student_id BIGINT UNSIGNED NOT NULL,
content_id BIGINT UNSIGNED NOT NULL,
scheduled_date DATE NOT NULL,
scheduled_time TIME,
status TINYINT DEFAULT 0, -- 0:待完成 1:已完成 2:已跳过
completed_at DATETIME,
score INT, -- 动作评分
points_earned INT DEFAULT 0, -- 获得积分
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_student (student_id),
INDEX idx_date (scheduled_date),
INDEX idx_status (status),
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (content_id) REFERENCES training_contents(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='训练任务表';
-- 用户积分表
CREATE TABLE IF NOT EXISTS user_points (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
user_type VARCHAR(16) NOT NULL,
total_points INT DEFAULT 0,
used_points INT DEFAULT 0,
level VARCHAR(32) DEFAULT 'bronze', -- bronze/silver/gold/diamond
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user (user_type, user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户积分表';
-- 积分流水表
CREATE TABLE IF NOT EXISTS point_transactions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
user_type VARCHAR(16) NOT NULL,
change_type VARCHAR(32) NOT NULL, -- earn/use
points INT NOT NULL,
balance_after INT NOT NULL,
source VARCHAR(64), -- 来源training/detection/activity
description VARCHAR(256),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_type, user_id),
INDEX idx_time (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='积分流水表';
-- 设备表
CREATE TABLE IF NOT EXISTS devices (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_no VARCHAR(64) NOT NULL UNIQUE,
device_name VARCHAR(128),
device_type VARCHAR(32) NOT NULL, -- terminal/camera/edge_box
school_id BIGINT UNSIGNED,
class_id BIGINT UNSIGNED,
ip_address VARCHAR(45),
mac_address VARCHAR(32),
status TINYINT DEFAULT 0, -- 0:离线 1:在线 2:故障
last_heartbeat DATETIME,
firmware_version VARCHAR(32),
config_version INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_school (school_id),
INDEX idx_class (class_id),
INDEX idx_status (status),
FOREIGN KEY (school_id) REFERENCES schools(id),
FOREIGN KEY (class_id) REFERENCES classes(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备表';
-- 创建默认超级管理员账号
INSERT INTO user_accounts (username, password_hash, phone, user_type, user_id, status, created_at)
SELECT 'admin', '$2a$10$8K1B6ZJ9YHmR5vN.Lm.YeOI0TmN7MAe9WcLQ.UR.X.q8.yFv9q8QO', '13800138000', 'admin', 1, 1, NOW()
WHERE NOT EXISTS (SELECT 1 FROM user_accounts WHERE username = 'admin');
-- 插入示例学校
INSERT INTO schools (name, code, address, contact_name, contact_phone, created_at) VALUES
('启明小学', 'QMXX001', '北京市朝阳区启明路1号', '张校长', '010-12345678', NOW()),
('阳光中学', 'YGZX001', '上海市浦东新区阳光大道100号', '李校长', '021-87654321', NOW());
-- 插入示例班级
INSERT INTO classes (name, grade, school_id, created_at) VALUES
('一年级一班', '一年级', 1, NOW()),
('二年级二班', '二年级', 1, NOW()),
('七年级一班', '七年级', 2, NOW());
-- 插入示例教师
INSERT INTO teachers (name, phone, school_id, role, created_at) VALUES
('王老师', '13811111111', 1, 'homeroom', NOW()),
('李老师', '13822222222', 1, 'school_doctor', NOW()),
('赵老师', '13833333333', 2, 'homeroom', NOW());
-- 插入示例家长
INSERT INTO parents (name, phone, created_at) VALUES
('张三', '13911111111', NOW()),
('李四', '13922222222', NOW()),
('王五', '13933333333', NOW());
-- 插入示例学生
INSERT INTO students (student_no, name, gender, class_id, parent_id, created_at) VALUES
('20260001', '张小明', 1, 1, 1, NOW()),
('20260002', '李小红', 2, 1, 2, NOW()),
('20260003', '王小华', 1, 2, 3, NOW());
-- 插入示例训练内容
INSERT INTO training_contents (name, type, duration, description, difficulty, status, created_at) VALUES
('眼保健操', 'eye_exercise', 300, '经典眼保健操,有效缓解眼部疲劳', 2, 1, NOW()),
('晶状体调焦训练', 'crystal_ball', 600, '通过远近调节训练晶状体灵活性', 3, 1, NOW()),
('穴位按摩', 'acupoint', 180, '按摩眼周穴位,促进血液循环', 1, 1, NOW());
-- 插入示例预警配置
INSERT INTO alert_configs (school_id, alert_level, vision_threshold, drop_threshold, notify_parent, notify_teacher, created_at) VALUES
(1, 1, 4.8, 0.1, 1, 1, NOW()), -- 绿色预警视力低于4.8
(1, 2, 4.5, 0.2, 1, 1, NOW()), -- 黄色预警视力低于4.5
(1, 3, 4.0, 0.3, 1, 1, NOW()); -- 红色预警视力低于4.0
-- 创建设备
INSERT INTO devices (device_no, device_name, device_type, school_id, class_id, status, created_at) VALUES
('DEV001', '教室一体机', 'terminal', 1, 1, 1, NOW()),
('CAM001', '教室摄像头', 'camera', 1, 1, 1, NOW());
-- 创建索引优化查询
CREATE INDEX idx_detections_student_time ON detections(student_id, detection_time);
CREATE INDEX idx_detections_task_time ON detections(task_id, detection_time);
CREATE INDEX idx_alerts_student_created ON alerts(student_id, created_at);
-- 创建视图:学生综合报告视图
CREATE VIEW student_comprehensive_report AS
SELECT
s.id as student_id,
s.name as student_name,
s.student_no,
cl.name as class_name,
sc.name as school_name,
MAX(d.detection_time) as last_detection_time,
AVG(d.vision_left) as avg_vision_left,
AVG(d.vision_right) as avg_vision_right,
COUNT(d.id) as detection_count,
COUNT(a.id) as alert_count
FROM students s
LEFT JOIN classes cl ON s.class_id = cl.id
LEFT JOIN schools sc ON cl.school_id = sc.id
LEFT JOIN detections d ON s.id = d.student_id
LEFT JOIN alerts a ON s.id = a.student_id
GROUP BY s.id;
-- 创建视图:班级统计视图
CREATE VIEW class_statistics AS
SELECT
cl.id as class_id,
cl.name as class_name,
sc.name as school_name,
COUNT(st.id) as total_students,
COUNT(d.student_id) as tested_students,
AVG(d.vision_left) as avg_vision_left,
AVG(d.vision_right) as avg_vision_right,
SUM(CASE WHEN a.id IS NOT NULL THEN 1 ELSE 0 END) as alert_count
FROM classes cl
LEFT JOIN schools sc ON cl.school_id = sc.id
LEFT JOIN students st ON cl.id = st.class_id
LEFT JOIN detections d ON st.id = d.student_id
LEFT JOIN alerts a ON st.id = a.student_id
GROUP BY cl.id;
-- 设置表的自增ID起始值
ALTER TABLE schools AUTO_INCREMENT = 1000;
ALTER TABLE classes AUTO_INCREMENT = 2000;
ALTER TABLE students AUTO_INCREMENT = 3000;
ALTER TABLE teachers AUTO_INCREMENT = 4000;
ALTER TABLE parents AUTO_INCREMENT = 5000;
ALTER TABLE user_accounts AUTO_INCREMENT = 6000;
ALTER TABLE detection_tasks AUTO_INCREMENT = 7000;
ALTER TABLE detections AUTO_INCREMENT = 8000;
ALTER TABLE alerts AUTO_INCREMENT = 9000;
ALTER TABLE training_contents AUTO_INCREMENT = 10000;
ALTER TABLE training_tasks AUTO_INCREMENT = 11000;
ALTER TABLE devices AUTO_INCREMENT = 12000;
-- 完成
SELECT 'AI近视防控系统数据库初始化完成' as message;

226
scripts/init_db_sqlite.go Normal file
View File

@@ -0,0 +1,226 @@
package main
import (
"fmt"
"log"
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// UserAccount 用户账号模型
type UserAccount struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"type:varchar(64);uniqueIndex" json:"username"`
PasswordHash string `gorm:"type:varchar(255)" json:"-"` // 不在JSON中暴露
Name string `gorm:"type:varchar(64)" json:"name"`
Phone string `gorm:"type:varchar(20);uniqueIndex" json:"phone"`
UserType string `gorm:"type:varchar(16)" json:"user_type"` // student, parent, teacher, admin
Status int `gorm:"default:1" json:"status"` // 1:正常, 0:禁用
LastLoginAt *time.Time `json:"last_login_at"`
LastLoginIP string `json:"last_login_ip"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func main() {
fmt.Println("AI近视防控系统 - SQLite测试数据库初始化")
// 使用SQLite作为临时数据库
db, err := gorm.Open(sqlite.Open("test_myopia.db"), &gorm.Config{})
if err != nil {
log.Fatal("连接数据库失败: ", err)
}
// 自动迁移数据库表结构
err = db.AutoMigrate(&UserAccount{})
if err != nil {
log.Fatal("数据库迁移失败: ", err)
}
// 加密密码
adminPassword := "Admin123!@#"
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("密码加密失败: ", err)
}
// 创建管理员账号
adminAccount := UserAccount{
Username: "admin",
PasswordHash: string(hashedPassword),
Name: "系统管理员",
Phone: "13800138000",
UserType: "admin",
Status: 1,
}
// 检查管理员账号是否存在
var existingAdmin UserAccount
result := db.Where("username = ?", "admin").First(&existingAdmin)
if result.Error != nil {
// 管理员账号不存在,创建新账号
if err := db.Create(&adminAccount).Error; err != nil {
log.Fatal("创建管理员账号失败: ", err)
}
fmt.Println("✅ 管理员账号创建成功")
} else {
// 管理员账号已存在,更新密码
if err := db.Model(&existingAdmin).Updates(UserAccount{
PasswordHash: string(hashedPassword),
Name: "系统管理员",
Phone: "13800138000",
UserType: "admin",
Status: 1,
}).Error; err != nil {
log.Fatal("更新管理员账号失败: ", err)
}
fmt.Println("✅ 管理员账号更新成功")
}
// 创建测试老师账号
teacherPassword := "Teacher123!@#"
teacherHashed, err := bcrypt.GenerateFromPassword([]byte(teacherPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("老师密码加密失败: ", err)
}
teacherAccount := UserAccount{
Username: "teacher",
PasswordHash: string(teacherHashed),
Name: "测试老师",
Phone: "13800138001",
UserType: "teacher",
Status: 1,
}
var existingTeacher UserAccount
result = db.Where("username = ?", "teacher").First(&existingTeacher)
if result.Error != nil {
// 老师账号不存在,创建新账号
if err := db.Create(&teacherAccount).Error; err != nil {
log.Fatal("创建老师账号失败: ", err)
}
fmt.Println("✅ 老师账号创建成功")
} else {
// 老师账号已存在,更新密码
if err := db.Model(&existingTeacher).Updates(UserAccount{
PasswordHash: string(teacherHashed),
Name: "测试老师",
Phone: "13800138001",
UserType: "teacher",
Status: 1,
}).Error; err != nil {
log.Fatal("更新老师账号失败: ", err)
}
fmt.Println("✅ 老师账号更新成功")
}
// 创建测试学生账号
studentPassword := "Student123!@#"
studentHashed, err := bcrypt.GenerateFromPassword([]byte(studentPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("学生密码加密失败: ", err)
}
studentAccount := UserAccount{
Username: "student",
PasswordHash: string(studentHashed),
Name: "测试学生",
Phone: "13800138002",
UserType: "student",
Status: 1,
}
var existingStudent UserAccount
result = db.Where("username = ?", "student").First(&existingStudent)
if result.Error != nil {
// 学生账号不存在,创建新账号
if err := db.Create(&studentAccount).Error; err != nil {
log.Fatal("创建学生账号失败: ", err)
}
fmt.Println("✅ 学生账号创建成功")
} else {
// 学生账号已存在,更新密码
if err := db.Model(&existingStudent).Updates(UserAccount{
PasswordHash: string(studentHashed),
Name: "测试学生",
Phone: "13800138002",
UserType: "student",
Status: 1,
}).Error; err != nil {
log.Fatal("更新学生账号失败: ", err)
}
fmt.Println("✅ 学生账号更新成功")
}
// 创建测试家长账号
parentPassword := "Parent123!@#"
parentHashed, err := bcrypt.GenerateFromPassword([]byte(parentPassword), bcrypt.DefaultCost)
if err != nil {
log.Fatal("家长密码加密失败: ", err)
}
parentAccount := UserAccount{
Username: "parent",
PasswordHash: string(parentHashed),
Name: "测试家长",
Phone: "13800138003",
UserType: "parent",
Status: 1,
}
var existingParent UserAccount
result = db.Where("username = ?", "parent").First(&existingParent)
if result.Error != nil {
// 家长账号不存在,创建新账号
if err := db.Create(&parentAccount).Error; err != nil {
log.Fatal("创建家长账号失败: ", err)
}
fmt.Println("✅ 家长账号创建成功")
} else {
// 家长账号已存在,更新密码
if err := db.Model(&existingParent).Updates(UserAccount{
PasswordHash: string(parentHashed),
Name: "测试家长",
Phone: "13800138003",
UserType: "parent",
Status: 1,
}).Error; err != nil {
log.Fatal("更新家长账号失败: ", err)
}
fmt.Println("✅ 家长账号更新成功")
}
fmt.Println("\n📋 测试账号信息:")
fmt.Println("==================")
fmt.Println("管理员账号:")
fmt.Println(" 用户名: admin")
fmt.Println(" 密码: Admin123!@#")
fmt.Println(" 角色: admin")
fmt.Println(" 手机: 13800138000")
fmt.Println("\n老师账号:")
fmt.Println(" 用户名: teacher")
fmt.Println(" 密码: Teacher123!@#")
fmt.Println(" 角色: teacher")
fmt.Println(" 手机: 13800138001")
fmt.Println("\n学生账号:")
fmt.Println(" 用户名: student")
fmt.Println(" 密码: Student123!@#")
fmt.Println(" 角色: student")
fmt.Println(" 手机: 13800138002")
fmt.Println("\n家长账号:")
fmt.Println(" 用户名: parent")
fmt.Println(" 密码: Parent123!@#")
fmt.Println(" 角色: parent")
fmt.Println(" 手机: 13800138003")
fmt.Println("\n💾 数据库文件: test_myopia.db")
fmt.Println("✅ 测试数据库初始化完成!")
fmt.Println("💡 提示: 可使用这些账号登录系统进行功能测试")
}

View File

@@ -0,0 +1,89 @@
-- AI近视防控系统 - MySQL测试用户初始化脚本
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS ai_myopia_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE ai_myopia_db;
-- 创建用户账户表(如果不存在)
CREATE TABLE IF NOT EXISTS user_accounts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(64) NOT NULL,
phone VARCHAR(20) NOT NULL UNIQUE,
user_type ENUM('student', 'parent', 'teacher', 'admin') NOT NULL DEFAULT 'student',
status TINYINT DEFAULT 1,
last_login_at DATETIME NULL,
last_login_ip VARCHAR(45) DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_phone (phone),
INDEX idx_user_type (user_type),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 删除现有测试用户
DELETE FROM user_accounts WHERE username IN ('admin', 'teacher', 'student', 'parent');
-- 插入测试管理员账号
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'admin',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Admin123!@# 的bcrypt哈希
'系统管理员',
'13800138000',
'admin',
1
);
-- 插入测试老师账号
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'teacher',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Teacher123!@# 的bcrypt哈希
'测试老师',
'13800138001',
'teacher',
1
);
-- 插入测试学生账号
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'student',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Student123!@# 的bcrypt哈希
'测试学生',
'13800138002',
'student',
1
);
-- 插入测试家长账号
INSERT INTO user_accounts (username, password_hash, name, phone, user_type, status) VALUES (
'parent',
'$2a$10$ES13mXJ4KzObj4wHXxVtzuGbBsy7.Wu8vpa6Z1ZSRdW332itPCO4i', -- Parent123!@# 的bcrypt哈希
'测试家长',
'13800138003',
'parent',
1
);
-- 验证测试账号创建
SELECT
id,
username,
name,
phone,
user_type,
status,
created_at
FROM user_accounts
WHERE username IN ('admin', 'teacher', 'student', 'parent');
-- 输出测试账号信息
SELECT '--- AI近视防控系统测试账号信息 ---' as info;
SELECT '管理员账号: admin / Admin123!@#' as admin_info;
SELECT '老师账号: teacher / Teacher123!@#' as teacher_info;
SELECT '学生账号: student / Student123!@#' as student_info;
SELECT '家长账号: parent / Parent123!@#' as parent_info;
SELECT '--- 账号已准备就绪,可用于登录测试 ---' as status;

75
scripts/test_auth.go Normal file
View File

@@ -0,0 +1,75 @@
package main
import (
"fmt"
"log"
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// UserAccount 用户账号模型
type UserAccount struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"type:varchar(64);uniqueIndex" json:"username"`
PasswordHash string `gorm:"type:varchar(255)" json:"-"` // 不在JSON中暴露
Name string `gorm:"type:varchar(64)" json:"name"`
Phone string `gorm:"type:varchar(20);uniqueIndex" json:"phone"`
UserType string `gorm:"type:varchar(16)" json:"user_type"` // student, parent, teacher, admin
Status int `gorm:"default:1" json:"status"` // 1:正常, 0:禁用
LastLoginAt *time.Time `json:"last_login_at"`
LastLoginIP string `json:"last_login_ip"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func main() {
fmt.Println("AI近视防控系统 - 登录功能验证测试")
// 连接到SQLite数据库
db, err := gorm.Open(sqlite.Open("test_myopia.db"), &gorm.Config{})
if err != nil {
log.Fatal("连接数据库失败: ", err)
}
// 测试登录功能
testLogin(db, "admin", "Admin123!@#")
testLogin(db, "teacher", "Teacher123!@#")
testLogin(db, "student", "Student123!@#")
testLogin(db, "parent", "Parent123!@#")
fmt.Println("\n✅ 登录功能验证完成!")
fmt.Println("💡 提示: 所有测试账号密码均已正确设置,可正常使用登录功能")
}
func testLogin(db *gorm.DB, username, password string) {
fmt.Printf("\n--- 测试用户: %s ---\n", username)
var user UserAccount
result := db.Where("username = ?", username).First(&user)
if result.Error != nil {
fmt.Printf("❌ 用户不存在: %s\n", username)
return
}
if user.Status == 0 {
fmt.Printf("❌ 用户已被禁用: %s\n", username)
return
}
// 验证密码
err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
if err != nil {
fmt.Printf("❌ 密码验证失败: %s\n", username)
return
}
fmt.Printf("✅ 用户 %s 登录验证成功!\n", username)
fmt.Printf(" - 用户名: %s\n", user.Username)
fmt.Printf(" - 姓名: %s\n", user.Name)
fmt.Printf(" - 角色: %s\n", user.UserType)
fmt.Printf(" - 手机: %s\n", user.Phone)
fmt.Printf(" - 状态: %d\n", user.Status)
}