Files
bl/modules/base/service/base_sys_login.go
昔念 5320dffdd8 ```
feat(user): 添加QQ绑定功能并重构用户登录逻辑

- 在BaseSysUser模型中添加QQ字段,移除密码字段(暂时注释)
- 移除base.bbs.go中的GetUserInfo函数,将其迁移至base_sys_user.go
- 将登录服务中的外部API调用逻辑整合到BaseSysUserService
- 新增BindQQ方法实现QQ号绑定功能,包含重复绑定检查
- 更新GetUserInfo方法,完善用户信息获取和同步逻辑
- 优化导入包,移除未使用的依赖项
```
2026-03-24 11:56:36 +08:00

202 lines
5.8 KiB
Go

package service
import (
"context"
"strings"
"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/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
)
username = strings.ToLower(username)
vcode, _ := cool.CacheManager.Get(ctx, "login:"+captchaId)
if vcode.String() != verifyCode {
err = gerror.New("验证码错误")
return
}
// 调用方法
user, err := NewBaseSysUserService().GetUserInfo(username, password)
if err != nil {
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)
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.Logger.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.Logger.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{}
}