Files
bl/modules/base/service/base_sys_login.go
昔念 e75ecd413d feat(fight): 重构战斗系统技能逻辑与精灵切换功能
- 优化技能执行流程,统一使用 SelectSkillAction 作为技能载体
- 移除冗余的技能 ID 字段,简化数据结构
- 调整命中判断和技能效果触发机制,提升准确性
- 修改精灵切换与捕获相关方法参数格式
- 更新技能列表结构为动态数组以支持灵活长度
- 完善睡眠等异常状态的处理逻辑
- 修复战斗中技能 PP 扣减及副本还原问题
- 清理无用代码,如多余的 FindWithIndex 函数定义
- 强化验证码缓存键命名规则,增强安全性
2025-10-26 20:56:03 +08:00

205 lines
6.1 KiB
Go

package service
import (
"context"
"time"
"github.com/golang-jwt/jwt/v4"
"blazing/cool"
v1 "blazing/modules/base/api/v1"
"blazing/modules/base/config"
"blazing/modules/base/model"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/gogf/gf/v2/util/guid"
)
type BaseSysLoginService struct {
*cool.Service
}
type TokenResult struct {
Expire uint `json:"expire"`
Token string `json:"token"`
RefreshExpire uint `json:"refreshExpire"`
RefreshToken string `json:"refreshToken"`
}
// Login 登录
func (s *BaseSysLoginService) Login(ctx context.Context, req *v1.BaseOpenLoginReq) (result *TokenResult, err error) {
var (
captchaId = req.CaptchaId
verifyCode = req.VerifyCode
password = req.Password
username = req.Username
baseSysUser = model.NewBaseSysUser()
)
vcode, _ := cool.CacheManager.Get(ctx, "login:"+captchaId)
if vcode.String() != verifyCode {
err = gerror.New("验证码错误")
return
}
md5password, _ := gmd5.Encrypt(password)
var user *model.BaseSysUser
cool.DBM(baseSysUser).Where("username=?", username).Where("password=?", md5password).Where("status=?", 1).Scan(&user)
if user == nil {
err = gerror.New("账户或密码不正确~")
return
}
result, err = s.generateTokenByUser(ctx, user)
if err != nil {
return
}
return
}
// Captcha 图形验证码
func (*BaseSysLoginService) Captcha(req *v1.BaseOpenCaptchaReq) (interface{}, error) {
type capchaInfo struct {
CaptchaId string `json:"captchaId"`
Data string `json:"data"`
}
var (
ctx g.Ctx
err error
result = &capchaInfo{}
)
captchaText := grand.Digits(4)
svg := `<svg width="150" height="50" xmlns="http://www.w3.org/2000/svg"><text x="75" y="25" text-anchor="middle" font-size="25" fill="#fff">` + captchaText + `</text></svg>`
svgbase64 := gbase64.EncodeString(svg)
result.Data = `data:image/svg+xml;base64,` + svgbase64
result.CaptchaId = guid.S()
cool.CacheManager.Set(ctx, "login:"+result.CaptchaId, captchaText, 1800*time.Second)
cool.Loger.Debug(ctx, "验证码", result.CaptchaId, captchaText)
return result, err
}
// Logout 退出登录
func (*BaseSysLoginService) Logout(ctx context.Context) (err error) {
userId := cool.GetAdmin(ctx).UserId
cool.CacheManager.Remove(ctx, "admin:department:"+gconv.String(userId))
cool.CacheManager.Remove(ctx, "admin:perms:"+gconv.String(userId))
cool.CacheManager.Remove(ctx, "admin:token:"+gconv.String(userId))
cool.CacheManager.Remove(ctx, "admin:token:refresh:"+gconv.String(userId))
return
}
// RefreshToken 刷新token
func (s *BaseSysLoginService) RefreshToken(ctx context.Context, token string) (result *TokenResult, err error) {
tokenClaims, err := jwt.ParseWithClaims(token, &cool.Claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.Config.Jwt.Secret), nil
})
if err != nil {
return
}
claims, ok := tokenClaims.Claims.(*cool.Claims)
if !ok {
err = gerror.New("tokenClaims.Claims.(*Claims) error")
return
}
if !tokenClaims.Valid {
err = gerror.New("tokenClaims.Valid error")
return
}
if !claims.IsRefresh {
err = gerror.New("claims.IsRefresh error")
return
}
if !(claims.UserId > 0) {
err = gerror.New("claims.UserId error")
return
}
var (
user *model.BaseSysUser
baseSysUser = model.NewBaseSysUser()
)
cool.DBM(baseSysUser).Where("id=?", claims.UserId).Where("status=?", 1).Scan(&user)
if user == nil {
err = gerror.New("用户不存在")
return
}
result, err = s.generateTokenByUser(ctx, user)
return
}
// generateToken 生成token
func (*BaseSysLoginService) generateToken(ctx context.Context, user *model.BaseSysUser, roleIds []string, exprire uint, isRefresh bool) (token string) {
err := cool.CacheManager.Set(ctx, "admin:passwordVersion:"+gconv.String(user.ID), gconv.String(user.PasswordV), 0)
if err != nil {
cool.Loger.Error(ctx, "生成token失败", err)
}
claims := &cool.Claims{
IsRefresh: isRefresh,
RoleIds: roleIds,
Username: user.Username,
UserId: user.ID,
PasswordVersion: user.PasswordV,
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(exprire) * time.Second)),
},
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err = tokenClaims.SignedString([]byte(config.Config.Jwt.Secret))
if err != nil {
cool.Loger.Error(ctx, "生成token失败", err)
}
return
}
// 根据用户生成前端需要的Token信息
func (s *BaseSysLoginService) generateTokenByUser(ctx context.Context, user *model.BaseSysUser) (result *TokenResult, err error) {
var (
baseSysRoleService = NewBaseSysRoleService()
baseSysMenuService = NewBaseSysMenuService()
baseSysDepartmentService = NewBaseSysDepartmentService()
)
// 获取用户角色
roleIds := baseSysRoleService.GetByUser(user.ID)
// 如果没有角色,则报错
if len(roleIds) == 0 {
err = gerror.New("该用户未设置任何角色,无法登录~")
return
}
// 生成token
result = &TokenResult{}
result.Expire = config.Config.Jwt.Token.Expire
result.RefreshExpire = config.Config.Jwt.Token.RefreshExpire
result.Token = s.generateToken(ctx, user, roleIds, result.Expire, false)
result.RefreshToken = s.generateToken(ctx, user, roleIds, result.RefreshExpire, true)
// 将用户相关信息保存到缓存
perms := baseSysMenuService.GetPerms(roleIds)
departments := baseSysDepartmentService.GetByRoleIds(roleIds, user.Username == "admin")
cool.CacheManager.Set(ctx, "admin:department:"+gconv.String(user.ID), departments, 0)
cool.CacheManager.Set(ctx, "admin:perms:"+gconv.String(user.ID), perms, 0)
cool.CacheManager.Set(ctx, "admin:token:"+gconv.String(user.ID), result.Token, 0)
cool.CacheManager.Set(ctx, "admin:token:refresh:"+gconv.String(user.ID), result.RefreshToken, 0)
return
}
// NewBaseSysLoginService 创建一个新的BaseSysLoginService
func NewBaseSysLoginService() *BaseSysLoginService {
return &BaseSysLoginService{}
}