package main import ( "database/sql" "fmt" "log" "net/http" "os" "github.com/gin-gonic/gin" _ "github.com/lib/pq" "golang.org/x/crypto/bcrypt" ) // LoginRequest 登录请求参数结构 type LoginRequest struct { Account string `json:"account" binding:"required"` Password string `json:"password" binding:"required"` } // LoginResponse 登录响应结构 type LoginResponse struct { Success bool `json:"success"` Message string `json:"message"` Data struct { UserID string `json:"user_id,omitempty"` } `json:"data,omitempty"` } var db *sql.DB func main() { log.Println("开始初始化应用程序") // 初始化Gin引擎 r := gin.Default() log.Println("Gin引擎初始化完成") // 从环境变量获取数据库配置 dbHost := os.Getenv("DB_HOST") dbPort := os.Getenv("DB_PORT") dbUser := os.Getenv("DB_USER") dbPassword := os.Getenv("DB_PASSWORD") dbName := os.Getenv("DB_NAME") log.Printf("获取数据库配置: host=%s, port=%s, user=%s, dbname=%s", dbHost, dbPort, dbUser, dbName) // 构建数据库连接字符串 connStr := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", dbHost, dbPort, dbUser, dbPassword, dbName, ) log.Printf("数据库连接字符串构建完成: %s", connStr) var err error db, err = sql.Open("postgres", connStr) if err != nil { log.Fatalf("无法连接数据库: %v", err) panic(fmt.Sprintf("无法连接数据库: %v", err)) } defer db.Close() log.Println("数据库连接对象创建成功") // 验证数据库连接 if err := db.Ping(); err != nil { log.Fatalf("数据库连接失败: %v", err) panic(fmt.Sprintf("数据库连接失败: %v", err)) } log.Println("数据库连接验证成功") // 登录接口 r.POST("/user/login", loginHandler) log.Println("登录接口注册完成: POST /user/login") // 启动服务,监听80端口 log.Println("服务启动在80端口") r.Run(":80") } // loginHandler 处理登录逻辑 func loginHandler(c *gin.Context) { reqID := c.Request.Header.Get("X-Request-ID") if reqID == "" { reqID = fmt.Sprintf("%d", gin.Mode()) // 简单生成一个请求标识 } log.Printf("[%s] 收到登录请求 from %s", reqID, c.ClientIP()) var req LoginRequest // 绑定并验证请求参数 if err := c.ShouldBindJSON(&req); err != nil { log.Printf("[%s] 请求参数绑定失败: %v, 请求体: %+v", reqID, err, c.Request.Body) c.JSON(http.StatusBadRequest, LoginResponse{ Success: false, Message: "账号或密码不能为空", }) return } log.Printf("[%s] 请求参数绑定成功: 账号=%s", reqID, req.Account) // 1. 判断账号和密码是否为空(双重保险) if req.Account == "" || req.Password == "" { log.Printf("[%s] 账号或密码为空: 账号=%s", reqID, req.Account) c.JSON(http.StatusBadRequest, LoginResponse{ Success: false, Message: "账号或密码不能为空", }) return } // 2. 查询账号对应的密码和用户ID var storedPassword string var userID string query := ` SELECT password, user_id FROM user_account_password_view WHERE account = $1 AND deleted = false ` log.Printf("[%s] 执行查询: %s, 参数: %s", reqID, query, req.Account) err := db.QueryRow(query, req.Account).Scan(&storedPassword, &userID) switch { case err == sql.ErrNoRows: // 账号不存在或已被删除 log.Printf("[%s] 账号不存在或已删除: %s", reqID, req.Account) c.JSON(http.StatusOK, LoginResponse{ Success: false, Message: "账号不存在", }) return case err != nil: log.Printf("[%s] 查询账号信息失败: %v, 账号=%s", reqID, err, req.Account) c.JSON(http.StatusInternalServerError, LoginResponse{ Success: false, Message: "查询账号信息失败", }) return } log.Printf("[%s] 查询账号信息成功: 账号=%s, userID=%s", reqID, req.Account, userID) // 3. 验证密码(使用bcrypt比较原始密码和存储的加密密码) err = bcrypt.CompareHashAndPassword([]byte(storedPassword), []byte(req.Password)) if err != nil { // 密码不匹配 log.Printf("[%s] 密码验证失败: 账号=%s, 错误=%v", reqID, req.Account, err) c.JSON(http.StatusOK, LoginResponse{ Success: false, Message: "密码错误", }) return } log.Printf("[%s] 密码验证成功: 账号=%s", reqID, req.Account) // 4. 登录成功 log.Printf("[%s] 登录成功: 账号=%s, userID=%s", reqID, req.Account, userID) c.JSON(http.StatusOK, LoginResponse{ Success: true, Message: "登录成功", Data: struct { UserID string `json:"user_id,omitempty"` }{ UserID: userID, }, }) }