// Package handler 存放全部 HTTP 接口处理函数 package handler import ( "strings" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" "nl-game-api-gin/internal/database" "nl-game-api-gin/internal/model" "nl-game-api-gin/internal/service" "nl-game-api-gin/pkg/jwtutil" "nl-game-api-gin/pkg/resp" ) // registerReq 注册请求体 type registerReq struct { Username string `json:"username" binding:"required,min=3,max=20"` // 用户名:3-20位 Password string `json:"password" binding:"required,min=6,max=32"` // 密码:6-32位 Nickname string `json:"nickname" binding:"max=20"` // 昵称:可选,默认同用户名 Avatar string `json:"avatar"` // 头像:像素编码 px:01~px:32(旧 emoji 兼容) } // Register 用户注册:校验重名、加密密码、发注册礼积分 func Register(c *gin.Context) { var req registerReq if err := c.ShouldBindJSON(&req); err != nil { resp.Fail(c, "参数有误:用户名3-20位、密码6-32位") return } req.Username = strings.TrimSpace(req.Username) // 用户名查重 var count int64 database.DB.Model(&model.User{}).Where("username = ?", req.Username).Count(&count) if count > 0 { resp.Fail(c, "用户名已被占用") return } // bcrypt 加密密码 hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { resp.Fail(c, "密码加密失败") return } if req.Nickname == "" { req.Nickname = req.Username } if req.Avatar == "" { req.Avatar = "px:01" } user := model.User{ Username: req.Username, Password: string(hash), Nickname: req.Nickname, Avatar: req.Avatar, Role: model.RoleNormal, } if err := database.DB.Create(&user).Error; err != nil { resp.Fail(c, "注册失败,请稍后重试") return } // 注册礼积分(金额可在后台配置) gift := service.GetConfigInt(model.ConfKeyRegisterGift, 200) if gift > 0 { service.ChangePoints(nil, user.ID, gift, model.PointTypeRegister, 0, "新用户注册礼") user.Points = gift } token, _ := jwtutil.Generate(user.ID, user.Role) resp.OK(c, gin.H{"token": token, "user": user}) } // loginReq 登录请求体 type loginReq struct { Username string `json:"username" binding:"required"` // 用户名 Password string `json:"password" binding:"required"` // 密码 } // Login 用户登录:校验密码、记录登录日志、签发 Token func Login(c *gin.Context) { var req loginReq if err := c.ShouldBindJSON(&req); err != nil { resp.Fail(c, "请输入用户名和密码") return } var user model.User if err := database.DB.Where("username = ?", strings.TrimSpace(req.Username)).First(&user).Error; err != nil { resp.Fail(c, "用户名或密码错误") return } // bcrypt 校验密码 if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)) != nil { resp.Fail(c, "用户名或密码错误") return } if user.Status == model.UserStatusBanned { resp.Fail(c, "账号已被封禁,请联系管理员") return } // 写登录日志(IP + 浏览器UA) database.DB.Create(&model.LoginLog{ UserID: user.ID, IP: c.ClientIP(), UserAgent: c.GetHeader("User-Agent"), }) token, err := jwtutil.Generate(user.ID, user.Role) if err != nil { resp.Fail(c, "登录凭证生成失败") return } resp.OK(c, gin.H{"token": token, "user": user}) }