108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
// 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})
|
||
}
|