Files
nl-game-api/internal/handler/auth.go
2026-08-14 17:50:37 +08:00

108 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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})
}