Files

108 lines
3.3 KiB
Go
Raw Permalink Normal View History

2026-08-14 13:17:03 +08:00
// 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"` // 昵称:可选,默认同用户名
2026-08-14 17:50:37 +08:00
Avatar string `json:"avatar"` // 头像:像素编码 px:01~px:32旧 emoji 兼容)
2026-08-14 13:17:03 +08:00
}
// 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 == "" {
2026-08-14 17:50:37 +08:00
req.Avatar = "px:01"
2026-08-14 13:17:03 +08:00
}
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})
}