add
This commit is contained in:
166
backend/src/logic4user/login.go
Normal file
166
backend/src/logic4user/login.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package logic4user
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"user/db"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// LoginRequest 登录请求参数结构
|
||||
// 用于接收前端传递的登录账号和密码
|
||||
// json标签指定JSON序列化/反序列化的字段名
|
||||
// binding:"required"表示该字段为必填项,用于参数校验
|
||||
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"` // 用户ID,omitempty表示为空时不序列化
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// LoginHandler 处理用户登录请求的处理器函数
|
||||
// 参数c是gin.Context,用于获取请求信息和返回响应
|
||||
func LoginHandler(c *gin.Context) {
|
||||
// 获取请求ID,用于追踪请求链路,若请求头中没有则生成一个新的UUID
|
||||
reqID := c.Request.Header.Get("X-LoginRequest-ID")
|
||||
if reqID == "" {
|
||||
reqID = uuid.New().String()
|
||||
}
|
||||
// 记录收到登录请求的日志,包含请求ID和客户端IP
|
||||
zap.L().Info("💡 收到登录请求",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("clientIP", c.ClientIP()),
|
||||
)
|
||||
|
||||
// 声明一个LoginRequest类型变量用于接收请求参数
|
||||
var req LoginRequest
|
||||
// 绑定并验证请求参数(JSON格式)
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// 绑定失败时记录警告日志,并返回错误响应
|
||||
zap.L().Warn("❗️ 请求参数绑定失败",
|
||||
zap.String("reqID", reqID),
|
||||
zap.Error(err),
|
||||
zap.Any("请求体", c.Request.Body),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, LoginResponse{
|
||||
Success: false,
|
||||
Message: "账号或密码不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
// 记录参数绑定成功的日志
|
||||
zap.L().Info("✅ 请求参数绑定成功",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
)
|
||||
|
||||
// 1. 二次校验账号和密码是否为空(双重保险,防止校验规则被绕过)
|
||||
if req.Account == "" || req.Password == "" {
|
||||
zap.L().Warn("❗️ 账号或密码为空",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, LoginResponse{
|
||||
Success: false,
|
||||
Message: "账号或密码不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 从数据库查询账号对应的密码和用户ID
|
||||
var storedPassword string // 数据库中存储的加密密码
|
||||
var userID string // 用户ID
|
||||
// 查询语句:从用户账号密码视图中查询指定账号(未删除)的密码和用户ID
|
||||
query := `
|
||||
SELECT password, user_id
|
||||
FROM user_info_view
|
||||
WHERE account = $1 AND deleted = false
|
||||
`
|
||||
zap.L().Info("💡 执行查询",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("query", query),
|
||||
zap.String("参数", req.Account),
|
||||
)
|
||||
// 执行查询并将结果扫描到变量中
|
||||
err := db.DB.QueryRow(query, req.Account).Scan(&storedPassword, &userID)
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
// 账号不存在或已被删除的情况
|
||||
zap.L().Warn("❗️ 账号不存在或已删除",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
)
|
||||
c.JSON(http.StatusOK, LoginResponse{
|
||||
Success: false,
|
||||
Message: "账号不存在",
|
||||
})
|
||||
return
|
||||
case err != nil:
|
||||
// 查询过程发生错误的情况
|
||||
zap.L().Error("❌ 查询账号信息失败",
|
||||
zap.String("reqID", reqID),
|
||||
zap.Error(err),
|
||||
zap.String("账号", req.Account),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, LoginResponse{
|
||||
Success: false,
|
||||
Message: "查询账号信息失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
// 记录查询账号信息成功的日志
|
||||
zap.L().Info("✅ 查询账号信息成功",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
|
||||
// 3. 验证密码(使用bcrypt比较原始密码和存储的加密密码)
|
||||
err = bcrypt.CompareHashAndPassword([]byte(storedPassword), []byte(req.Password))
|
||||
if err != nil {
|
||||
// 密码不匹配的情况
|
||||
zap.L().Warn("❗️ 密码验证失败",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusOK, LoginResponse{
|
||||
Success: false,
|
||||
Message: "密码错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
// 记录密码验证成功的日志
|
||||
zap.L().Info("✅ 密码验证成功",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
)
|
||||
|
||||
// 4. 登录成功,返回成功响应并包含用户ID
|
||||
zap.L().Info("✅ 登录成功",
|
||||
zap.String("reqID", reqID),
|
||||
zap.String("账号", req.Account),
|
||||
zap.String("userID", userID),
|
||||
)
|
||||
c.JSON(http.StatusOK, LoginResponse{
|
||||
Success: true,
|
||||
Message: "登录成功",
|
||||
Data: struct {
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
}{
|
||||
UserID: userID,
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user