🚀 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:
222
cmd/main.go
Normal file
222
cmd/main.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"ai-myopia-prevention/api/handlers"
|
||||
"ai-myopia-prevention/db/models"
|
||||
"ai-myopia-prevention/internal/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("启明计划 - AI近视防控系统 后端服务启动中...")
|
||||
|
||||
// 初始化Gin路由器
|
||||
r := gin.Default()
|
||||
|
||||
// 初始化数据库连接
|
||||
db, err := initDatabase()
|
||||
if err != nil {
|
||||
log.Fatal("数据库连接失败:", err)
|
||||
}
|
||||
|
||||
// 禁用外键约束自动创建
|
||||
db = db.Set("gorm:table_options", "ENGINE=InnoDB")
|
||||
|
||||
// 自动迁移数据库表结构 (禁用外键)
|
||||
err = migrateDatabase(db)
|
||||
if err != nil {
|
||||
log.Fatal("数据库迁移失败:", err)
|
||||
}
|
||||
|
||||
// 初始化服务
|
||||
authService := handlers.NewAuthService(db)
|
||||
detectionService := handlers.NewDetectionService(db)
|
||||
|
||||
// 设置路由
|
||||
setupRoutes(r, authService, detectionService)
|
||||
|
||||
// 启动服务器
|
||||
port := "8080"
|
||||
log.Printf("服务器将在 :%s 端口启动", port)
|
||||
if err := r.Run(":" + port); err != nil {
|
||||
log.Fatal("服务器启动失败:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initDatabase 初始化数据库连接
|
||||
func initDatabase() (*gorm.DB, error) {
|
||||
// 从环境变量读取数据库配置
|
||||
dbHost := os.Getenv("DB_HOST")
|
||||
if dbHost == "" {
|
||||
dbHost = "mysql" // Docker 网络中的默认主机名
|
||||
}
|
||||
dbUser := os.Getenv("DB_USER")
|
||||
if dbUser == "" {
|
||||
dbUser = "myopia_user"
|
||||
}
|
||||
dbPassword := os.Getenv("DB_PASSWORD")
|
||||
if dbPassword == "" {
|
||||
dbPassword = "MyopiaTest2026!"
|
||||
}
|
||||
dbName := os.Getenv("DB_NAME")
|
||||
if dbName == "" {
|
||||
dbName = "ai_myopia"
|
||||
}
|
||||
|
||||
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{
|
||||
// 配置日志记录
|
||||
Logger: nil, // 在生产环境中可以使用gorm/logger.Default
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接数据库失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置连接池
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取底层sql.DB失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// migrateDatabase 数据库表结构迁移
|
||||
func migrateDatabase(db *gorm.DB) error {
|
||||
// 自动迁移数据库表结构 (禁用外键)
|
||||
// 注意:在生产环境中,通常使用迁移文件而不是自动迁移
|
||||
err := db.Migrator().DropTable(
|
||||
)
|
||||
err = db.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.Student{},
|
||||
&models.Parent{},
|
||||
&models.Teacher{},
|
||||
&models.School{},
|
||||
&models.Class{},
|
||||
&models.UserAccount{},
|
||||
&models.DetectionTask{},
|
||||
&models.Detection{},
|
||||
&models.DetectionReport{},
|
||||
&models.Alert{},
|
||||
&models.AlertConfig{},
|
||||
&models.AlertSummary{},
|
||||
&models.Device{},
|
||||
&models.DeviceConfig{},
|
||||
&models.DeviceLog{},
|
||||
&models.DeviceStatusInfo{},
|
||||
&models.DeviceCommand{},
|
||||
&models.DeviceMessage{},
|
||||
&models.DeviceHeartbeat{},
|
||||
&models.DeviceGroup{},
|
||||
&models.DeviceGroupRelation{},
|
||||
&models.DeviceMaintenance{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("数据库迁移失败: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("数据库表结构迁移完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupRoutes 设置路由
|
||||
func setupRoutes(r *gin.Engine, auth *handlers.AuthService, detection *handlers.DetectionService) {
|
||||
// 健康检查端点
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"service": "ai-myopia-prevention-backend",
|
||||
"time": time.Now().Unix(),
|
||||
})
|
||||
})
|
||||
|
||||
// API版本路由组
|
||||
v1 := r.Group("/api/v1")
|
||||
{
|
||||
// 认证相关路由
|
||||
authGroup := v1.Group("/auth")
|
||||
{
|
||||
authGroup.POST("/login", auth.Login)
|
||||
authGroup.POST("/register", auth.Register)
|
||||
authGroup.GET("/profile", middleware.JWTAuthMiddleware(), auth.GetProfile)
|
||||
authGroup.PUT("/profile", middleware.JWTAuthMiddleware(), auth.UpdateProfile)
|
||||
authGroup.PUT("/password", middleware.JWTAuthMiddleware(), auth.ChangePassword)
|
||||
}
|
||||
|
||||
// 检测相关路由
|
||||
detectionGroup := v1.Group("/detections")
|
||||
{
|
||||
detectionGroup.POST("/start", middleware.JWTAuthMiddleware(), middleware.RBACMiddleware("teacher", "admin"), detection.StartDetection)
|
||||
detectionGroup.POST("/submit", middleware.JWTAuthMiddleware(), middleware.RBACMiddleware("student", "teacher", "admin"), detection.SubmitDetection)
|
||||
detectionGroup.GET("/report/:detection_id/student/:student_id", middleware.JWTAuthMiddleware(), detection.GetDetectionReport)
|
||||
detectionGroup.GET("/history", middleware.JWTAuthMiddleware(), detection.GetDetectionHistory)
|
||||
detectionGroup.GET("/class/:class_id/stats", middleware.JWTAuthMiddleware(), middleware.RBACMiddleware("teacher", "admin"), detection.GetClassStats)
|
||||
}
|
||||
|
||||
// 学生相关路由
|
||||
studentGroup := v1.Group("/students")
|
||||
{
|
||||
studentGroup.Use(middleware.JWTAuthMiddleware())
|
||||
// 学生只能访问自己的信息,家长可以访问孩子的信息,老师可以访问班级学生信息,管理员可以访问所有
|
||||
// TODO: 实现学生相关API
|
||||
}
|
||||
|
||||
// 班级相关路由
|
||||
classGroup := v1.Group("/classes")
|
||||
{
|
||||
classGroup.Use(middleware.JWTAuthMiddleware())
|
||||
// 只有老师和管理员可以访问班级信息
|
||||
// TODO: 实现班级相关API
|
||||
}
|
||||
|
||||
// 预警相关路由
|
||||
alertGroup := v1.Group("/alerts")
|
||||
{
|
||||
alertGroup.Use(middleware.JWTAuthMiddleware())
|
||||
// 学生可以查看自己的预警,家长可以查看孩子的预警,老师可以查看班级预警,管理员可以查看所有
|
||||
// TODO: 实现预警相关API
|
||||
}
|
||||
|
||||
// 训练相关路由
|
||||
trainingGroup := v1.Group("/training")
|
||||
{
|
||||
trainingGroup.Use(middleware.JWTAuthMiddleware())
|
||||
// 学生可以查看自己的训练,家长可以查看孩子的训练,老师可以查看班级训练,管理员可以查看所有
|
||||
// TODO: 实现训练相关API
|
||||
}
|
||||
|
||||
// 设备相关路由
|
||||
deviceGroup := v1.Group("/devices")
|
||||
{
|
||||
deviceGroup.Use(middleware.JWTAuthMiddleware(), middleware.RBACMiddleware("admin"))
|
||||
// 只有管理员可以管理设备
|
||||
// TODO: 实现设备相关API
|
||||
}
|
||||
}
|
||||
|
||||
// 为静态文件提供服务
|
||||
r.Static("/static", "./static")
|
||||
|
||||
// 为上传文件提供服务
|
||||
r.Static("/uploads", "./uploads")
|
||||
|
||||
fmt.Println("路由设置完成")
|
||||
}
|
||||
Reference in New Issue
Block a user