Files
bl/modules/base/service/base_sys_user.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

543 lines
15 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 service
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"blazing/cool"
"blazing/modules/base/model"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type BaseSysUserService struct {
*cool.Service
}
// Person 方法 返回不带密码的用户信息
func (s *BaseSysUserService) Person(userId uint) (res gdb.Record, err error) {
m := cool.DBM(s.Model)
res, err = m.Where("id", userId).FieldsEx("password").One()
return
}
func (s *BaseSysUserService) GetSession(email string, password string) (res *model.BaseSysUser, err error) {
m := cool.DBM(s.Model)
m.Where("email", email).Where("password", password).Where("status", 1).Scan(&res)
if res == nil {
err = gerror.New("账户或密码不正确~")
return
}
return
}
func (s *BaseSysUserService) GetPerson(userId uint32) (res *model.BaseSysUser) {
m := cool.DBM(s.Model)
m.Where("id", userId).FieldsEx("password").Scan(&res)
return
}
func (s *BaseSysUserService) SetdepartmentId(userId, departmentId uint32) (res *model.BaseSysUser) {
m := cool.DBM(s.Model)
m.Where("id", userId).Data("departmentId", departmentId).Update()
return
}
func (s *BaseSysUserService) DuihuanFreeGold(userId uint32, gold, free int64) {
m := cool.DBM(s.Model).Where("id", userId)
m.Data(g.Map{
"goldbean": gdb.Raw("goldbean-" + gconv.String(gold)),
"free_gold": gdb.Raw("free_gold+" + gconv.String(free)),
}).Update()
}
func (s *BaseSysUserService) DuihuanGold(userId uint32, gold, free int64) {
m := cool.DBM(s.Model).Where("id", userId)
m.Data(g.Map{
"goldbean": gdb.Raw("goldbean+" + gconv.String(gold)),
"free_gold": gdb.Raw("free_gold-" + gconv.String(free)),
}).Update()
}
func (s *BaseSysUserService) BindQQ(username string, passworid string, qq int64) (err error) {
var res *model.BaseSysUser
cool.DBM(s.Model).Where("qq", qq).Scan(&res)
if res != nil {
err = gerror.New("该QQ已绑定其他账号" + gconv.String(res.ID))
return
}
var res1 *model.BaseSysUser
cool.DBM(s.Model).Where("id", username).Scan(&res1)
if res1 == nil {
return gerror.New("用户未激活")
}
r, err := s.GetUserInfo(username, passworid)
if err != nil {
return
}
if r == nil {
err = gerror.New("用户不存在")
return
}
_, err = cool.DBM(s.Model).Where("id", username).Data("qq", qq).Update()
if err != nil {
return err
}
return
}
func (s *BaseSysUserService) UpdateFreeGold(userId uint32, gold int64) {
m := cool.DBM(s.Model).Where("id", userId)
m.Increment("free_gold", gold)
}
// 单位是分
func (s *BaseSysUserService) UpdateGold(userId uint32, gold int64) {
if cool.Config.ServerInfo.IsVip != 0 {
cool.Logger.Info(context.TODO(), "测试服不操作金币")
return
}
m := cool.DBM(s.Model).Where("id", userId)
m.Increment("goldbean", gold)
}
func (s *BaseSysUserService) GetGold(userId uint) (res int64) {
var res1 model.BaseSysUser
m := cool.DBM(s.Model)
m.Where("id", userId).Fields("goldbean").Scan(&res1)
return res1.GoldBean
}
func (s *BaseSysUserService) GetFreeGold(userId uint) (res int64) {
var res1 model.BaseSysUser
m := cool.DBM(s.Model)
m.Where("id", userId).Fields("free_gold").Scan(&res1)
return res1.FreeGold
}
func (s *BaseSysUserService) GetEamil(userId string) (res *model.BaseSysUser) {
m := cool.DBM(s.Model)
m.Where("email", userId).FieldsEx("password").Scan(&res)
return
}
func (s *BaseSysUserService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
if method == "Delete" {
// 禁止删除超级管理员
userIds := garray.NewIntArrayFrom(gconv.Ints(param["ids"]))
currentId, found := userIds.Get(0)
superAdminId := 10001
if userIds.Len() == 1 && found && currentId == superAdminId {
err = gerror.New("超级管理员不能删除")
return
}
// 删除超级管理员
userIds.RemoveValue(10001)
g.RequestFromCtx(ctx).SetParam("ids", userIds.Slice())
}
return
}
func (s *BaseSysUserService) ModifyAfter(ctx context.Context, method string, param g.MapStrAny) (err error) {
if method == "Delete" {
userIds := garray.NewIntArrayFrom(gconv.Ints(param["ids"]))
userIds.RemoveValue(1)
// 删除用户时删除相关数据
cool.DBM(model.NewBaseSysUserRole()).WhereIn("userId", userIds.Slice()).Delete()
}
return
}
func (s *BaseSysUserService) Gen(user UserAttributes) (data interface{}, err error) {
var (
m = cool.DBM(s.Model)
)
lastInsertId, err := m.Data(user).Data(
g.Map{
"username": strings.ToLower(user.Username),
"headImg": user.AvatarUrl,
"departmentId": 1,
},
).InsertAndGetId()
if err != nil {
return
}
data = g.Map{"id": lastInsertId}
roleArray := garray.NewArray()
roleArray.PushRight(g.Map{
"userId": gconv.Uint(lastInsertId),
"roleId": gconv.Uint(13),
})
_, err = cool.DBM(model.NewBaseSysUserRole()).Fields("userId,roleId").Insert(roleArray)
return
}
func (s *BaseSysUserService) GetUserInfo(username, password string) (*model.BaseSysUser, error) {
// 创建带 Cookie 存储的 HTTP 客户端
jar, err := cookiejar.New(nil)
if err != nil {
return nil, fmt.Errorf("创建 CookieJar 失败: %w", err)
}
client := &http.Client{Jar: jar}
// 1. POST 获取 token
tokenURL := bbsurl + "/api/token"
formData := url.Values{}
formData.Set("identification", username)
formData.Set("password", password)
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(formData.Encode()))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
// 解析 token
var tokenResp TokenResponse
err = json.Unmarshal(body, &tokenResp)
if err != nil {
return nil, fmt.Errorf("解析 token 失败: %w", err)
}
// 提取 CSRF Token
csrfToken := resp.Header.Get("X-CSRF-Token")
// 2. GET 获取该用户的详细信息
usersURL := bbsurl + fmt.Sprintf("/api/users/%d", tokenResp.UserID)
req2, err := http.NewRequest("GET", usersURL, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req2.Header.Set("Authentication", tokenResp.Token)
req2.Header.Set("X-CSRF-Token", csrfToken)
resp2, err := client.Do(req2)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %w", err)
}
defer resp2.Body.Close()
body2, err := io.ReadAll(resp2.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
// 解析用户信息
var userResp UserResponse
err = json.Unmarshal(body2, &userResp)
if err != nil {
return nil, fmt.Errorf("解析用户信息失败: %w", err)
}
if !strings.EqualFold(userResp.Data.Attributes.Username, username) { //说明没查找到用户
return nil, gerror.New("账户或密码不正确~")
} else {
var user *model.BaseSysUser
m := cool.DBM(s.Model).Where("username=?", username)
m.Scan(&user)
if user == nil {
s.Gen(userResp.Data.Attributes)
m := cool.DBM(s.Model).Where("username=?", username)
m.Scan(&user)
} else {
if *user.Status == 0 {
err = gerror.New("账户被封禁~")
return nil, err
}
user.HeadImg = &userResp.Data.Attributes.AvatarUrl
var ttt = *user.PasswordV + 1
user.PasswordV = &ttt
_, err := m.Save(user)
if err != nil {
log.Fatal(err)
}
cool.CacheManager.Set(context.Background(), fmt.Sprintf("admin:passwordVersion:%d", user.ID), ttt, 0)
}
return user, nil
}
return nil, nil
}
// ServiceAdd 方法 添加用户
func (s *BaseSysUserService) ServiceAdd(ctx context.Context, req *cool.AddReq) (data interface{}, err error) {
var (
m = cool.DBM(s.Model)
r = g.RequestFromCtx(ctx)
reqmap = r.GetMap()
)
// 如果reqmap["password"]不为空则对密码进行md5加密
if !r.Get("password").IsNil() {
reqmap["password"] = gmd5.MustEncryptString(r.Get("password").String())
}
if !r.Get("username").IsNil() {
reqmap["username"] = strings.ToLower(r.Get("username").String())
}
if s.UniqueKey != nil {
for k, v := range s.UniqueKey {
if reqmap[k] != nil {
count, err := cool.DBM(s.Model).Where(k, reqmap[k]).Count()
if err != nil {
return nil, err
}
if count > 0 {
err = gerror.New(v)
return nil, err
}
}
}
}
lastInsertId, err := m.Data(reqmap).InsertAndGetId()
if err != nil {
return
}
data = g.Map{"id": lastInsertId}
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
if !r.Get("roleIdList").IsNil() {
inRoleIdSet := gset.NewFrom(r.Get("roleIdList").Ints())
roleArray := garray.NewArray()
inRoleIdSet.Iterator(func(v interface{}) bool {
roleArray.PushRight(g.Map{
"userId": gconv.Uint(lastInsertId),
"roleId": gconv.Uint(v),
})
return true
})
_, err = cool.DBM(model.NewBaseSysUserRole()).Fields("userId,roleId").Insert(roleArray)
if err != nil {
return err
}
}
return
})
return
}
// ServiceInfo 方法 返回服务信息
func (s *BaseSysUserService) ServiceInfo(ctx g.Ctx, req *cool.InfoReq) (data interface{}, err error) {
result, err := s.Service.ServiceInfo(ctx, req)
if err != nil {
return nil, err
}
if result.(gdb.Record).IsEmpty() {
return nil, nil
}
// g.DumpWithType(result)
resultMap := result.(gdb.Record).Map()
// 获取角色
roleIds, err := cool.DBM(model.NewBaseSysUserRole()).Where("userId", resultMap["id"]).Fields("roleId").Array()
if err != nil {
return nil, err
}
resultMap["roleIdList"] = roleIds
data = resultMap
return
}
// ServiceUpdate 方法 更新用户信息
func (s *BaseSysUserService) ServiceUpdate(ctx context.Context, req *cool.UpdateReq) (data sql.Result, err error) {
var (
admin = cool.GetAdmin(ctx)
m = cool.DBM(s.Model)
)
r := g.RequestFromCtx(ctx)
rMap := r.GetMap()
if !r.Get("username").IsNil() {
rMap["username"] = strings.ToLower(r.Get("username").String())
}
// 如果不传入ID代表更新当前用户
userId := r.Get("id", admin.UserId).Uint()
userInfo, err := m.Where("id", userId).One()
if err != nil {
return
}
if userInfo.IsEmpty() {
err = gerror.New("用户不存在")
return
}
// 禁止禁用超级管理员
if userId == 10001 && (!r.Get("status").IsNil() && r.Get("status").Int() == 0) {
err = gerror.New("禁止禁用超级管理员")
return
}
if s.UniqueKey != nil {
for k, v := range s.UniqueKey {
if rMap[k] != nil {
count, err := cool.DBM(s.Model).Where(k, rMap[k]).WhereNot("id", userId).Count()
if err != nil {
return nil, err
}
if count > 0 {
err = gerror.New(v)
return nil, err
}
}
}
}
// 如果请求的password不为空并且密码加密后的值有变动说明要修改密码
var rPassword = r.Get("password", "").String()
if rPassword != "" && rPassword != userInfo["password"].String() {
rMap["password"], _ = gmd5.Encrypt(rPassword)
rMap["passwordV"] = userInfo["passwordV"].Int() + 1
cool.CacheManager.Set(ctx, fmt.Sprintf("admin:passwordVersion:%d", userId), rMap["passwordV"], 0)
} else {
delete(rMap, "password")
}
delete(rMap, "goldbean")
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
roleModel := cool.DBM(model.NewBaseSysUserRole()).TX(tx).Where("userId", userId)
roleIds, err := roleModel.Fields("roleId").Array()
if err != nil {
return
}
// 如果请求参数中不包含roleIdList说明不修改角色信息
if !r.Get("roleIdList").IsNil() {
inRoleIdSet := gset.NewFrom(r.Get("roleIdList").Ints())
roleIdsSet := gset.NewFrom(gconv.Ints(roleIds))
// 如果请求的角色信息未发生变化则跳过更新逻辑
if roleIdsSet.Diff(inRoleIdSet).Size() != 0 || inRoleIdSet.Diff(roleIdsSet).Size() != 0 {
roleArray := garray.NewArray()
inRoleIdSet.Iterator(func(v interface{}) bool {
roleArray.PushRight(g.Map{
"userId": gconv.Uint(userId),
"roleId": gconv.Uint(v),
})
return true
})
_, err = roleModel.Delete()
if err != nil {
return err
}
_, err = roleModel.Fields("userId,roleId").Insert(roleArray)
if err != nil {
return err
}
}
}
_, err = m.TX(tx).Update(rMap)
if err != nil {
return err
}
return
})
return
}
// Move 移动用户部门
func (s *BaseSysUserService) Move(ctx g.Ctx) (err error) {
request := g.RequestFromCtx(ctx)
departmentId := request.Get("departmentId").Int()
userIds := request.Get("userIds").Slice()
_, err = cool.DBM(s.Model).Where("id IN(?)", userIds).Data(g.Map{"departmentId": departmentId}).Update()
return
}
// NewBaseSysUserService 创建一个新的BaseSysUserService实例
func NewBaseSysUserService() *BaseSysUserService {
return &BaseSysUserService{
Service: &cool.Service{
Model: model.NewBaseSysUser(),
InfoIgnoreProperty: "password",
UniqueKey: map[string]string{
"username": "用户名不能重复",
"email": "邮箱不能重复",
},
PageQueryOp: &cool.QueryOp{
Select: `
base_sys_user.*,
STRING_AGG(role.name, ', ') AS roleName`,
Join: []*cool.JoinOp{
{
Model: model.NewBaseSysDepartment(),
Alias: "dept",
Type: "LeftJoin",
Condition: `base_sys_user."departmentId" = dept.id`,
},
{
Model: model.NewBaseSysUserRole(),
Alias: "user_role",
Type: "LeftJoin",
Condition: `base_sys_user.id = user_role."userId"`,
},
{
Model: model.NewBaseSysRole(),
Alias: "role",
Type: "LeftJoin",
Condition: `role.id = user_role."roleId"`,
},
},
Where: func(ctx context.Context) []g.Array {
r := g.RequestFromCtx(ctx).GetMap()
return []g.Array{
{"id != ?", g.Slice{"10001"}, true}, //排除管理员
{`("departmentId" IN (?))`, gconv.SliceStr(r["departmentIds"])},
}
},
Extend: func(ctx g.Ctx, m *gdb.Model) *gdb.Model {
return m.Group(`base_sys_user.id`)
},
KeyWordField: []string{"username", "email"},
FieldEQ: []string{"id"},
},
},
}
}