优化账户、角色分页

This commit is contained in:
likun 2025-05-09 18:28:15 +08:00
parent 902059e11b
commit 8ae6f7ee1d
24 changed files with 850 additions and 180 deletions

View File

@ -9,8 +9,10 @@ import (
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/errcode"
"admin/lib/xlog"
"fmt"
"gorm.io/gorm"
"strings"
)
type CommonResourceService struct {
@ -20,8 +22,9 @@ type CommonResourceService struct {
func initCommonResourcesRepo(db *gorm.DB) {
r(consts.ResourcesName_Project, "项目管理", repo.NewCommonResourceRepo(db, &model.Project{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_Server, "服务器管理", repo.NewCommonResourceRepo(db, &model.Server{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_Account, "账号管理", repo.NewCommonResourceRepo(db, &model.Account{}), ShowMethod_Get) // 账号管理不需要在后台读写数据都是通过项目api拉
r(consts.ResourcesName_Role, "角色管理", repo.NewCommonResourceRepo(db, &model.Role{}), ShowMethod_Get) // 角色管理不需要在后台读写数据都是通过项目api拉
r(consts.ResourcesName_Account, "账号管理", repo.NewCommonResourceRepo(db, &model.Account{}), ShowMethod_Get) // 账号管理不需要在后台读写数据都是通过项目api拉
r(consts.ResourcesName_SupportAccount, "扶持账号", repo.NewCommonResourceRepo(db, &model.SupportAccount{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete) // 扶持账号,只是标记哪些账号是扶持好
r(consts.ResourcesName_Role, "角色管理", repo.NewCommonResourceRepo(db, &model.Role{}), ShowMethod_Get) // 角色管理不需要在后台读写数据都是通过项目api拉
r(consts.ResourcesName_WhiteList, "白名单", repo.NewCommonResourceRepo(db, &model.WhiteList{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete)
r(consts.ResourcesName_Ban, "封禁管理", repo.NewCommonResourceRepo(db, &model.Ban{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{}), ShowMethod_Get|ShowMethod_Post) // 个人邮件发放就没法撤回?
@ -39,19 +42,19 @@ func NewCommonResourceService(db *gorm.DB) *CommonResourceService {
return svc
}
func (svc *CommonResourceService) List(projectId int, resource string, listParams *dto.CommonListReq) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
func (svc *CommonResourceService) List(projectId int, resource string, listParams *dto.CommonListReq) (int, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
_, projectEt, find, err := svc.projectRepo.GetById(projectId)
if err != nil {
return nil, nil, err
return 0, nil, nil, err
}
if resource != consts.ResourcesName_Project && !find {
return nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
return 0, nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectEt,
listParams.PageNo, listParams.PageLen, listParams.ParsedWhereConditions.Conditions)
rRepo := findCommResourceRepo(resource)
totalCount, fieldsDescInfo, etList, err := rRepo.List(projectEt, listParams)
if err != nil {
return nil, nil, err
return 0, nil, nil, err
}
retList := make([]dto.CommonDtoValues, 0, len(etList))
for _, v := range etList {
@ -60,10 +63,10 @@ func (svc *CommonResourceService) List(projectId int, resource string, listParam
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpListHook); ok {
return hook.List(projectEt, resource, listParams.PageNo, listParams.PageLen, fieldsDescInfo, retList, "")
return hook.List(projectEt, resource, listParams, fieldsDescInfo, totalCount, retList)
}
return fieldsDescInfo, retList, nil
return totalCount, fieldsDescInfo, retList, nil
}
func (svc *CommonResourceService) GetById(projectId int, resource string, id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) {
@ -92,24 +95,50 @@ func (svc *CommonResourceService) Create(projectId int, resource string, dtoObj
return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
et, err := findCommResourceRepo(resource).Create(projectEt, dtoObj)
if err != nil {
return nil, err
}
// 返回新的实体插入数据库之后会填入数据库id所以要返给前端刷新id数据
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpCreateHook); ok {
dtoObj := et.ToCommonDto()
err = hook.Create(projectEt, resource, dtoObj)
createOne := func(obj dto.CommonDtoValues) (dto.CommonDtoValues, error) {
et, err := findCommResourceRepo(resource).Create(projectEt, obj)
if err != nil {
return nil, err
}
return dtoObj, nil
// 返回新的实体插入数据库之后会填入数据库id所以要返给前端刷新id数据
// 这里转换一次新的数据传输对象因为上一步走了创建会给dto分配id
newObj := et.ToCommonDto()
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpCreateHook); ok {
err = hook.Create(projectEt, resource, newObj)
if err != nil {
return nil, err
}
return newObj, nil
}
return newObj, nil
}
return et.ToCommonDto(), err
var newDtoObj dto.CommonDtoValues
if resource == consts.ResourcesName_SupportAccount {
account, findAccount := dtoObj["Account"]
if findAccount {
for _, ac := range strings.Split(account.(string), ",") {
dtoObj["Account"] = ac
tmpObj, err := createOne(dtoObj)
if err != nil {
xlog.Errorf("create support account %v db error:%v", ac, err)
err = nil // 忽略多个插入的报错
} else {
newDtoObj = tmpObj
}
}
} else {
return nil, errcode.New(errcode.ParamsInvalid, "account empty:%+v", dtoObj)
}
} else {
newDtoObj, err = createOne(dtoObj)
}
return newDtoObj, err
}
func (svc *CommonResourceService) Edit(projectId int, resource string, dtoObj dto.CommonDtoValues) error {

View File

@ -6,8 +6,9 @@ import (
)
type IPostResourceOpListHook interface {
List(projectInfo *entity.Project, resource string, pageNo, pageLen int, fields []*dto.CommonDtoFieldDesc, rows []dto.CommonDtoValues, extraQuery string, args ...any) (
[]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error)
List(projectInfo *entity.Project, resource string, params *dto.CommonListReq,
fields []*dto.CommonDtoFieldDesc, totalCount int, rows []dto.CommonDtoValues) (
int, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error)
}
type IPostResourceOpCreateHook interface {

View File

@ -26,7 +26,6 @@ var projectsValueChoicesGetHook = map[string]IGetAllValueChoicesHook{
}
func GetProjectResourceHook(project *entity.Project, resource string) any {
return nil
projectResourceHooks, find := projectsResourceHookMgr[project.Po.ProjectType]
if !find {
return nil

View File

@ -2,65 +2,138 @@ package smdl
import (
"admin/apps/game/domain/entity"
"admin/apps/game/domain/projects/smdl/internal"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/lib/httpclient"
"admin/lib/utils"
"math"
"net/url"
"strconv"
)
type AccountHook struct {
}
func (hook *AccountHook) List(projectInfo *entity.Project, resource string, pageNo, pageLen int, fields []*dto.CommonDtoFieldDesc, rows []dto.CommonDtoValues, extraQuery string, args ...any) (
[]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
func (hook *AccountHook) List(projectInfo *entity.Project, resource string, params *dto.CommonListReq,
fields []*dto.CommonDtoFieldDesc, totalCount int, rows []dto.CommonDtoValues) (
int, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
serverList, err := internal.GetRemoteServerInfoList(alisrvAddr)
if err != nil {
return 0, nil, nil, err
}
totalCount, reqPageServerList, err := getPaginationServerReqList(projectInfo, serverList, params, func(info *internal.ServerInfo) int { return info.TotalAccountCount })
if err != nil {
return 0, nil, nil, err
}
// {"data":[{"account":"20250425cs01","roleList":[{"account":"20250425cs01","createAt":1745567667525,"latestLoginAt":1745567714234,"roleId":"20001000001","serverId":"20001"}],"serverId":"20001"}],
// "state":{"code":2000000,"msg":"OK"}}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Data []struct {
Account string `json:"account"`
ServerId int `json:"serverId"`
RoleList []*Role `json:"roleList"`
} `json:"data"`
State struct {
Code int `json:"code"`
Msg string `json:"msg"`
} `json:"state"`
Data []struct {
Account string `json:"account"`
ServerId string `json:"serverId"`
RoleList []*Role `json:"roleList"`
} `json:"data"`
}
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/userlist", "get", nil, rsp)
if err != nil {
return nil, nil, err
for _, reqServer := range reqPageServerList {
body := &url.Values{}
body.Add("serverid", reqServer.RawInfo.ServerId)
body.Add("offset", strconv.Itoa(reqServer.Offset))
body.Add("count", strconv.Itoa(reqServer.Count))
body.Add("userlist", "true")
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/rolelist", "get", body, rsp)
if err != nil {
return 0, nil, nil, err
}
for _, user := range rsp.Data {
userPo := &model.Account{
Account: user.Account,
ServerConfId: user.ServerId,
}
latestLoginTime := int64(0)
earliestCreateTime := int64(math.MaxInt64)
for _, v := range user.RoleList {
userPo.RoleIds = append(userPo.RoleIds, v.RoleId)
if latestLoginTime < v.LatestLoginTime {
latestLoginTime = v.LatestLoginTime
}
if earliestCreateTime > v.CreateTime {
earliestCreateTime = v.CreateTime
}
}
userPo.LatestLoginTime = utils.ParseUnixTimestamp2LocalTime(latestLoginTime)
userPo.CreateTime = utils.ParseUnixTimestamp2LocalTime(earliestCreateTime)
et := (&entity.CommonResource{}).FromPo(userPo)
rows = append(rows, et.ToCommonDto())
}
}
for _, user := range rsp.Data.Data {
userPo := &model.Account{
Account: user.Account,
ServerConfId: strconv.Itoa(user.ServerId),
}
latestLoginTime := int64(0)
earliestCreateTime := int64(math.MaxInt64)
for _, v := range user.RoleList {
userPo.RoleIds = append(userPo.RoleIds, v.RoleId)
if latestLoginTime < v.LatestLoginTime {
latestLoginTime = v.LatestLoginTime
}
if earliestCreateTime > v.CreateTime {
earliestCreateTime = v.CreateTime
}
}
userPo.LatestLoginTime = utils.ParseUnixTimestamp2LocalTime(latestLoginTime)
userPo.CreateTime = utils.ParseUnixTimestamp2LocalTime(earliestCreateTime)
et := (&entity.CommonResource{}).FromPo(userPo)
rows = append(rows, et.ToCommonDto())
}
return fields, rows, nil
return totalCount, fields, rows, nil
}
func getPaginationServerReqList(projectInfo *entity.Project, serverInfoList internal.ServerInfoList,
params *dto.CommonListReq, queryCountField func(*internal.ServerInfo) int) (
totalCount int, reqServerInfoList []*internal.PageServerCountInfo, err error) {
pageNo := params.PageNo
pageLen := params.PageLen
whereConditions := params.ParsedWhereConditions
reqServerList := make([]*internal.PageServerCountInfo, 0)
if len(whereConditions.Conditions) > 1 {
// 第一个是ProjectId条件
if whereConditions.Conditions[1].Key != "ServerConfId" { // 账号、角色的条件检索只支持区服
return 0, nil, errcode.New(errcode.ParamsInvalid, "where condition invalid:%+v", whereConditions.Conditions[0])
}
// 前端指定了查询某个区服的数据
specServerId := whereConditions.Conditions[1].Value1.(string)
for _, v := range serverInfoList {
if v.ServerId == specServerId {
offset := (pageNo - 1) * pageLen
limit := pageLen
totalCount = queryCountField(v)
reqServerList = append(reqServerList, &internal.PageServerCountInfo{
RawInfo: v,
Offset: offset,
Count: limit,
})
break
}
}
} else {
// 根据分页参数计算需要几个区服数据参与查询
totalCountTmp, tidyPageInfoList := serverInfoList.TidyToPageList(pageLen, queryCountField)
if len(tidyPageInfoList) == 0 {
// 没有找到分页信息,返回空表
return 0, nil, nil
}
totalCount = totalCountTmp
for i, tidyPageInfo := range tidyPageInfoList {
if i+1 == pageNo {
reqServerList = append(reqServerList, tidyPageInfo.ServerList...)
break
}
}
}
return totalCount, reqServerList, nil
}

View File

@ -0,0 +1,110 @@
package internal
import "admin/lib/httpclient"
type ServerInfo struct {
ServerId string `json:"serverId"`
ServerName string `json:"serverName"`
Ccu int `json:"ccu"`
TotalRoleCount int `json:"total_role_count"`
TotalAccountCount int `json:"total_account_count"`
Addr string `json:"addr"`
OpenWhitelistLogin bool `json:"open_whitelist_login"`
}
type ServerInfoList []*ServerInfo
type PageServerCountInfo struct {
Offset int
Count int
RawInfo *ServerInfo
}
type PageInfo struct {
PageNo int // 页数从1开始
Count int // 当前页的数量一般是pageLen但如果是最后一页可能不足
ServerList []*PageServerCountInfo // 参与组成这一页的服务器列表
}
func (list ServerInfoList) TidyToPageList(pageLen int, queryCountField func(info *ServerInfo) int) (totalCount int, pageList []*PageInfo) {
curPageNo := 1
curPageNeedCount := pageLen
var curPageInfo *PageInfo
for _, serverInfo := range list {
serverCount := queryCountField(serverInfo)
totalCount += serverCount
serverRestCount := serverCount
serverAccOffset := 0
for {
if serverRestCount >= curPageNeedCount {
// 当前页数量足够,切下一页
if curPageInfo == nil {
curPageInfo = &PageInfo{
PageNo: curPageNo,
}
}
curPageInfo.Count += curPageNeedCount
curPageInfo.ServerList = append(curPageInfo.ServerList, &PageServerCountInfo{
RawInfo: serverInfo,
Offset: serverAccOffset,
Count: curPageNeedCount,
})
// 切下一个页
// 置空,等待下一次循环判断来创建
pageList = append(pageList, curPageInfo)
curPageInfo = nil
serverRestCount -= curPageNeedCount
serverAccOffset += curPageNeedCount
curPageNeedCount = pageLen
curPageNo += 1
if serverRestCount <= 0 {
// 当前服务器没有余额了,也要切下一个服务器
break
}
} else {
// 当前页还不够,切下一个服务器信息
if curPageInfo == nil {
curPageInfo = &PageInfo{
PageNo: curPageNo,
}
}
curPageInfo.Count += serverRestCount
curPageInfo.ServerList = append(curPageInfo.ServerList, &PageServerCountInfo{
RawInfo: serverInfo,
Offset: serverAccOffset,
Count: serverRestCount,
})
// 切下一个服务器,页面不切
serverAccOffset += serverRestCount
curPageNeedCount -= serverRestCount
break
}
}
}
if curPageInfo != nil { // 最后一页还没达到数量的
pageList = append(pageList, curPageInfo)
}
return
}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
List []*ServerInfo `json:"list"`
} `json:"data"`
}
func GetRemoteServerInfoList(alisrvAddr string) (ServerInfoList, error) {
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/server", "get", nil, rsp)
if err != nil {
return nil, err
}
return rsp.Data.List, nil
}

View File

@ -0,0 +1,67 @@
package internal
import (
"encoding/json"
"fmt"
"github.com/go-playground/assert/v2"
"strconv"
"testing"
)
var count = []int{123, 1, 10, 31, 1} // 120,3+1+6,4+6,10,10,5+1
var serverInfoList1 = make(ServerInfoList, 0)
func init() {
for i, c := range count {
serverInfoList1 = append(serverInfoList1, &ServerInfo{
ServerId: strconv.Itoa(i),
TotalAccountCount: c,
})
}
}
func TestServer(t *testing.T) {
totalCount, pageList := serverInfoList1.TidyToPageList(10, func(info *ServerInfo) int { return info.TotalAccountCount })
assert.Equal(t, totalCount, 166)
data, _ := json.MarshalIndent(pageList, "", " ")
fmt.Printf("%v", string(data))
// 头尾
assert.Equal(t, len(pageList), 17)
page1 := pageList[0]
assert.Equal(t, page1.PageNo, 1)
assert.Equal(t, page1.Count, 10)
page17 := pageList[16]
assert.Equal(t, page17.PageNo, 17)
assert.Equal(t, page17.Count, 6)
// 第13个有3+1+6三个服务器数量组成
page13 := pageList[12]
assert.Equal(t, len(page13.ServerList), 3)
assert.Equal(t, page13.ServerList[0].Offset, 120)
assert.Equal(t, page13.ServerList[0].Count, 3)
assert.Equal(t, page13.ServerList[1].Offset, 0)
assert.Equal(t, page13.ServerList[1].Count, 1)
assert.Equal(t, page13.ServerList[2].Offset, 0)
assert.Equal(t, page13.ServerList[2].Count, 6)
// 第14个有6+4
page14 := pageList[13]
assert.Equal(t, len(page14.ServerList), 2)
assert.Equal(t, page14.ServerList[0].Offset, 6)
assert.Equal(t, page14.ServerList[0].Count, 4)
assert.Equal(t, page14.ServerList[1].Offset, 0)
assert.Equal(t, page14.ServerList[1].Count, 6)
page15 := pageList[14]
assert.Equal(t, len(page15.ServerList), 1)
assert.Equal(t, page15.ServerList[0].Offset, 6)
assert.Equal(t, page15.ServerList[0].Count, 10)
page16 := pageList[15]
assert.Equal(t, len(page16.ServerList), 1)
assert.Equal(t, page16.ServerList[0].Offset, 16)
assert.Equal(t, page16.ServerList[0].Count, 10)
}

View File

@ -2,11 +2,14 @@ package smdl
import (
"admin/apps/game/domain/entity"
"admin/apps/game/domain/projects/smdl/internal"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/lib/httpclient"
"admin/lib/utils"
"net/url"
"strconv"
)
type Role struct {
@ -24,42 +27,62 @@ type Role struct {
type RoleHook struct {
}
func (hook *RoleHook) List(projectInfo *entity.Project, resource string, pageNo, pageLen int, fields []*dto.CommonDtoFieldDesc, rows []dto.CommonDtoValues, extraQuery string, args ...any) (
[]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
func (hook *RoleHook) List(projectInfo *entity.Project, resource string, params *dto.CommonListReq,
fields []*dto.CommonDtoFieldDesc, totalCount int, rows []dto.CommonDtoValues) (
int, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
serverList, err := internal.GetRemoteServerInfoList(alisrvAddr)
if err != nil {
return 0, nil, nil, err
}
totalCount, reqPageServerList, err := getPaginationServerReqList(projectInfo, serverList, params, func(info *internal.ServerInfo) int { return info.TotalRoleCount })
if err != nil {
return 0, nil, nil, err
}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
State struct {
Code int `json:"code"`
Msg string `json:"msg"`
} `json:"state"`
Data struct {
Data []*Role `json:"data"`
} `json:"data"`
}
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/rolelist", "get", nil, rsp)
if err != nil {
return nil, nil, err
}
for _, reqServer := range reqPageServerList {
body := make(url.Values)
body.Add("serverid", reqServer.RawInfo.ServerId)
body.Add("offset", strconv.Itoa(reqServer.Offset))
body.Add("count", strconv.Itoa(reqServer.Count))
for _, role := range rsp.Data.Data {
rolePo := &model.Role{
RoleId: role.RoleId,
Account: role.Account,
ServerConfId: role.ServerConfId,
Name: role.Name,
Status: role.Status,
Level: role.Level,
Profession: role.Profession,
LatestLoginTime: utils.ParseUnixTimestamp2LocalTime(role.LatestLoginTime),
CreateTime: utils.ParseUnixTimestamp2LocalTime(role.CreateTime),
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/rolelist", "get", body, rsp)
if err != nil {
return 0, nil, nil, err
}
for _, role := range rsp.Data.Data {
rolePo := &model.Role{
RoleId: role.RoleId,
Account: role.Account,
ServerConfId: role.ServerConfId,
Name: role.Name,
Status: role.Status,
Level: role.Level,
Profession: role.Profession,
LatestLoginTime: utils.ParseUnixTimestamp2LocalTime(role.LatestLoginTime),
CreateTime: utils.ParseUnixTimestamp2LocalTime(role.CreateTime),
}
et := (&entity.CommonResource{}).FromPo(rolePo)
rows = append(rows, et.ToCommonDto())
}
et := (&entity.CommonResource{}).FromPo(rolePo)
rows = append(rows, et.ToCommonDto())
}
return fields, rows, nil
return 0, fields, rows, nil
}

View File

@ -11,60 +11,139 @@ import (
type ServerHook struct {
}
func (hook *ServerHook) List(projectInfo *entity.Project, resource string, pageNo, pageLen int, fields []*dto.CommonDtoFieldDesc, rows []dto.CommonDtoValues, extraQuery string, args ...any) (
[]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
func (hook *ServerHook) List(projectInfo *entity.Project, resource string, params *dto.CommonListReq,
fields []*dto.CommonDtoFieldDesc, totalCount int, rows []dto.CommonDtoValues) (
int, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
fields = append(fields, &dto.CommonDtoFieldDesc{
Name: "进程运行状态",
Key: "RunningStatus",
Type: "string",
HelpText: "进程运行状态:未知、运行中、停止",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
})
fields = append(fields, getAttachFields()...)
type ServerInfo struct {
ServerId string `json:"serverId"`
ServerName string `json:"serverName"`
}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
List []*ServerInfo `json:"list"`
} `json:"data"`
}
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/"+resource, "get", nil, rsp)
serverInfoList, err := getRemoteServerInfoList(alisrvAddr)
if err != nil {
return nil, nil, err
return 0, nil, nil, err
}
for _, row := range rows {
findRunning := false
for _, curRunning := range rsp.Data.List {
var findRemoteServerInfo *ServerInfo
for _, curRunning := range serverInfoList {
et := (&entity.CommonResource{}).FromPo(&model.Server{}).FromDto(row)
if et.ToPo().(*model.Server).ServerConfID == curRunning.ServerId {
// 运行中,给描述信息添加运行信息
findRunning = true
findRemoteServerInfo = curRunning
break
}
}
if findRunning {
if findRemoteServerInfo != nil {
row["RunningStatus"] = "运行中"
row["Ccu"] = findRemoteServerInfo.Ccu
row["TotalRoleCount"] = findRemoteServerInfo.TotalRoleCount
row["TotalAccountCount"] = findRemoteServerInfo.TotalAccountCount
if findRemoteServerInfo.OpenWhitelistLogin {
row["IsWhitelistLogin"] = "是"
} else {
row["IsWhitelistLogin"] = "否"
}
} else {
row["RunningStatus"] = "未知"
row["Ccu"] = 0
row["TotalRoleCount"] = 0
row["TotalAccountCount"] = 0
row["IsWhitelistLogin"] = "否"
}
}
return fields, rows, nil
return totalCount, fields, rows, nil
}
type ServerInfo struct {
ServerId string `json:"serverId"`
ServerName string `json:"serverName"`
Ccu int `json:"ccu"`
TotalRoleCount int `json:"total_role_count"`
TotalAccountCount int `json:"total_account_count"`
Addr string `json:"addr"`
OpenWhitelistLogin bool `json:"open_whitelist_login"`
}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
List []*ServerInfo `json:"list"`
} `json:"data"`
}
func getRemoteServerInfoList(alisrvAddr string) ([]*ServerInfo, error) {
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/server", "get", nil, rsp)
if err != nil {
return nil, err
}
return rsp.Data.List, nil
}
func getAttachFields() []*dto.CommonDtoFieldDesc {
return []*dto.CommonDtoFieldDesc{
{
Name: "进程运行状态",
Key: "RunningStatus",
Type: "string",
HelpText: "进程运行状态:未知、运行中、停止",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
},
{
Name: "实时在线",
Key: "Ccu",
Type: "int",
HelpText: "ccu",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
},
{
Name: "总角色数",
Key: "TotalRoleCount",
Type: "int",
HelpText: "",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
},
{
Name: "总账号数",
Key: "TotalAccountCount",
Type: "int",
HelpText: "进程运行状态:未知、运行中、停止",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
},
{
Name: "是否只允许白名单登录",
Key: "IsWhitelistLogin",
Type: "bool",
HelpText: "打开就是相当于停服维护中了",
Readonly: true,
Required: false,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
Uneditable: true,
},
}
}

View File

@ -0,0 +1,64 @@
package smdl
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/lib/httpclient"
"net/url"
)
type SupportAccountHook struct {
}
func (hook *SupportAccountHook) Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
et := (&entity.CommonResource{}).FromPo(&model.SupportAccount{}).FromDto(dtoObj)
info := et.ToPo().(*model.SupportAccount)
// 扶持账号借用白名单表存储,懒得建表了
params := &url.Values{}
params.Add("cmd_data", "OpWhitelist")
params.Add("type", "support")
params.Add("value", info.Account)
params.Add("op", "add")
params.Add("server", info.ServerConfID)
rsp := make(map[string]any)
err := httpclient.Request(alisrvAddr+"/gm", "get", params, &rsp)
if err != nil {
return errcode.New(errcode.ServerError, "发送新增白名单请求:%+v错误:%v", info, err)
}
return nil
}
func (hook *SupportAccountHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
et := (&entity.CommonResource{}).FromPo(&model.SupportAccount{}).FromDto(dtoObj)
info := et.ToPo().(*model.SupportAccount)
params := &url.Values{}
params.Add("cmd_data", "OpWhitelist")
params.Add("type", "support")
params.Add("value", info.Account)
params.Add("op", "remove")
params.Add("server", info.ServerConfID)
rsp := make(map[string]any)
err := httpclient.Request(alisrvAddr+"/gm", "get", params, &rsp)
if err != nil {
return errcode.New(errcode.ServerError, "发送删除白名单请求:%+v错误:%v", info, err)
}
return nil
}

View File

@ -18,7 +18,7 @@ func (hook *WhitelistHook) Create(projectInfo *entity.Project, resource string,
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
et := (&entity.CommonResource{}).FromPo(&model.RoleMail{}).FromDto(dtoObj)
et := (&entity.CommonResource{}).FromPo(&model.WhiteList{}).FromDto(dtoObj)
whiteInfo := et.ToPo().(*model.WhiteList)
params := &url.Values{}
@ -43,7 +43,7 @@ func (hook *WhitelistHook) Delete(projectInfo *entity.Project, resource string,
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
et := (&entity.CommonResource{}).FromPo(&model.RoleMail{}).FromDto(dtoObj)
et := (&entity.CommonResource{}).FromPo(&model.WhiteList{}).FromDto(dtoObj)
whiteInfo := et.ToPo().(*model.WhiteList)
params := &url.Values{}

View File

@ -16,7 +16,7 @@ import (
)
type ICommonResourceRepo interface {
List(project *entity.Project, pageNo, pageLen int, whereConditions []*dto.GetWhereCondition) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error)
List(project *entity.Project, params *dto.CommonListReq) (int, []*dto.CommonDtoFieldDesc, []*entity.CommonResource, error)
GetById(projectEt *entity.Project, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error)
Create(projectEt *entity.Project, et dto.CommonDtoValues) (*entity.CommonResource, error)
Edit(projectEt *entity.Project, et dto.CommonDtoValues) error
@ -38,31 +38,42 @@ func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonReso
return &commonResourceRepoImpl{db: db, poTemplate: poTemplate, fieldsDescInfoFun: fieldsInfo}
}
func (repo *commonResourceRepoImpl) List(projectEt *entity.Project, pageNo, pageLen int,
whereConditions []*dto.GetWhereCondition) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) {
func (repo *commonResourceRepoImpl) List(projectEt *entity.Project, params *dto.CommonListReq) (int, []*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) {
if pageNo < 0 || pageLen <= 0 {
return nil, nil, errcode.New(errcode.ParamsInvalid, "page no or page len invalid:%v,%v", pageNo, pageLen)
pageNo := params.PageNo
pageLen := params.PageLen
whereConditions := params.ParsedWhereConditions.Conditions
if pageNo <= 0 || pageLen <= 0 {
return 0, nil, nil, errcode.New(errcode.ParamsInvalid, "page no or page len invalid:%v,%v", pageNo, pageLen)
}
limitStart := pageNo * pageLen
limitStart := (pageNo - 1) * pageLen
limitLen := pageLen
listType := reflect.New(reflect.SliceOf(reflect.TypeOf(repo.poTemplate)))
var tx *gorm.DB
var totalCount int64
var txCount, txFind *gorm.DB
var err error
if len(whereConditions) <= 0 {
tx = repo.db.Offset(limitStart).Limit(limitLen)
txCount = repo.db.Model(repo.poTemplate)
txFind = repo.db.Offset(limitStart).Limit(limitLen)
} else {
whereSql, whereArgs := repo.parseWhereConditions2Sql(whereConditions)
xlog.Debugf("list resource %v where sql:%v, args:%+v",
repo.poTemplate.TableName(), whereSql, whereArgs)
tx = repo.db.Where(whereSql, whereArgs...).Offset(limitStart).Limit(limitLen)
txCount = repo.db.Model(repo.poTemplate).Where(whereSql, whereArgs...)
txFind = repo.db.Where(whereSql, whereArgs...).Offset(limitStart).Limit(limitLen)
}
err = tx.Find(listType.Interface()).Error
err = txCount.Count(&totalCount).Error
if err != nil {
return nil, nil, errcode.New(errcode.DBError, "list resource %v error:%v", repo.poTemplate.TableName(), err)
return 0, nil, nil, errcode.New(errcode.DBError, "count resource %v error:%v", repo.poTemplate.TableName(), err)
}
err = txFind.Find(listType.Interface()).Error
if err != nil {
return 0, nil, nil, errcode.New(errcode.DBError, "list resource %v error:%v", repo.poTemplate.TableName(), err)
}
listType1 := listType.Elem()
@ -76,7 +87,7 @@ func (repo *commonResourceRepoImpl) List(projectEt *entity.Project, pageNo, page
entityList = append(entityList, et)
}
return repo.fieldsDescInfoFun(projectEt), entityList, nil
return int(totalCount), repo.fieldsDescInfoFun(projectEt), entityList, nil
}
func (repo *commonResourceRepoImpl) GetById(projectEt *entity.Project, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) {

View File

@ -38,7 +38,8 @@ type CommonDtoValues map[string]any
type CommonDtoList struct {
FieldsDesc []*CommonDtoFieldDesc `json:"fields_desc"` // 数据字段描述信息
Rows []CommonDtoValues `json:"rows"` // 数据行
TotalCount int `json:"total_count"`
Rows []CommonDtoValues `json:"rows"` // 数据行
}
type PathInfo struct {

View File

@ -0,0 +1,32 @@
package model
import (
"admin/apps/game/model/dto"
"admin/internal/db"
"time"
)
func init() {
db.RegisterTableModels(SupportAccount{})
}
type SupportAccount struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId int `gorm:"uniqueIndex:idx_account"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_account;index:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"`
Account string `gorm:"type:varchar(200);uniqueIndex:idx_account;" name:"账号" desc:"用逗号标记多个" required:"true"`
CreatedAt time.Time `readonly:"true" where:"range"`
}
func (lm *SupportAccount) TableName() string {
return "support_account"
}
func (m *SupportAccount) GetId() int {
return m.ID
}
func (m *SupportAccount) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice {
return getChoiceServers(project)
}

View File

@ -54,8 +54,8 @@ func (svc *Service) CommonList(ctx context.Context, projectId int, resourceName
Value1: projectId,
}}, params.ParsedWhereConditions.Conditions...)
}
fieldsDescInfo, rows, err := svc.resourceSvc.List(projectId, resourceName, params)
return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, Rows: rows}, err
totalCount, fieldsDescInfo, rows, err := svc.resourceSvc.List(projectId, resourceName, params)
return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, TotalCount: totalCount, Rows: rows}, err
}
func (svc *Service) CommonPost(ctx context.Context, projectId int, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) {

View File

@ -5,11 +5,15 @@ go 1.24.2
require (
github.com/gin-contrib/pprof v1.5.3
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/assert/v2 v2.2.0
github.com/go-sql-driver/mysql v1.7.0
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/prometheus/client_golang v1.22.0
github.com/redis/go-redis/v9 v9.8.0
github.com/rs/zerolog v1.34.0
golang.org/x/crypto v0.38.0
golang.org/x/sync v0.14.0
golang.org/x/telemetry v0.0.0-20250507143331-155ddd5254aa
golang.org/x/tools v0.33.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7
@ -23,6 +27,7 @@ require (
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
@ -50,9 +55,7 @@ require (
golang.org/x/arch v0.16.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/telemetry v0.0.0-20250507143331-155ddd5254aa // indirect
golang.org/x/text v0.25.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
)

View File

@ -1,5 +1,9 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@ -15,6 +19,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM=
@ -89,6 +95,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=

View File

@ -23,17 +23,18 @@ const (
ResourcesName_SysCharacter = "character"
// game
ResourcesName_Project = "project"
ResourcesName_Server = "server"
ResourcesName_Account = "account"
ResourcesName_Role = "role"
ResourcesName_WhiteList = "whitelist"
ResourcesName_Ban = "ban"
ResourcesName_MailRole = "mail_role"
ResourcesName_MailGlobal = "mail_global"
ResourcesName_Notice = "notice"
ResourcesName_CDKey = "cdkey"
ResourcesName_DevicePush = "device_push"
ResourcesName_Project = "project"
ResourcesName_Server = "server"
ResourcesName_Account = "account"
ResourcesName_SupportAccount = "support_account"
ResourcesName_Role = "role"
ResourcesName_WhiteList = "whitelist"
ResourcesName_Ban = "ban"
ResourcesName_MailRole = "mail_role"
ResourcesName_MailGlobal = "mail_global"
ResourcesName_Notice = "notice"
ResourcesName_CDKey = "cdkey"
ResourcesName_DevicePush = "device_push"
)
const (

View File

@ -3,16 +3,17 @@ package errcode
//go:generate go run ../../cmd/tools/generator/errcode.go
const (
Ok = 0 // ok
ServerError = 1 // 服务器错误
DBError = 2 // 数据库错误
NotLogin = 3 // 没有登录,重定向到登录页面
HeaderParamsInvalid = 4 // http请求头参数不合法
TokenInvalid = 5 // http请求token不合法
UserOrPassInValid = 7 // 用户名或密码错误
NoPermission = 8 // 没有权限
ParamsInvalid = 9 // 参数不合法
DBInsertDuplicate = 10 // 数据重复
Ok = 0 // ok
ServerError = 1 // 服务器错误
DBError = 2 // 数据库错误
NotLogin = 3 // 没有登录,重定向到登录页面
HeaderParamsInvalid = 4 // http请求头参数不合法
TokenInvalid = 5 // http请求token不合法
UserOrPassInValid = 7 // 用户名或密码错误
NoPermission = 8 // 没有权限
ParamsInvalid = 9 // 参数不合法
DBInsertDuplicate = 10 // 数据重复
GameServerNotRunning = 11 // 游戏服务器区服没有运行
)
// sdk系统错误码给内部别的游戏调用api返回

View File

@ -2,12 +2,14 @@ package global
import (
"admin/internal/config"
"admin/lib/cache"
"admin/lib/web"
"gorm.io/gorm"
)
var (
GLOB_BOOT_FLAGS = &config.CommonBootFlags{} // 启动命令行参数
GLOB_DB *gorm.DB // 数据库
GLOB_API_ENGINE *web.Engine // 全局api服务器
GLOB_BOOT_FLAGS = &config.CommonBootFlags{} // 启动命令行参数
GLOB_DB *gorm.DB // 数据库
GLOB_API_ENGINE *web.Engine // 全局api服务器
GLOB_LOCAL_CACHER = cache.NewLocalCache() // 全局本地内存缓存器
)

101
admin/lib/cache/cache_local.go vendored Normal file
View File

@ -0,0 +1,101 @@
package cache
import (
"crypto/sha256"
"encoding/binary"
"sync"
"time"
)
type cacheData struct {
data any
expireAt time.Time
}
type localCache struct {
locker *sync.RWMutex
repo map[string]*cacheData
}
func newLocalCache() *localCache {
c := &localCache{
locker: new(sync.RWMutex),
repo: make(map[string]*cacheData),
}
return c
}
func (c *localCache) startBgCheckWorker() {
ticker := time.NewTicker(time.Second * 10)
for {
select {
case <-ticker.C:
timeNow := time.Now()
c.locker.Lock()
for k, v := range c.repo {
if v.expireAt.Before(timeNow) {
delete(c.repo, k)
}
}
c.locker.Unlock()
}
}
}
func (c *localCache) get(key string) (any, bool) {
c.locker.RLocker()
defer c.locker.RUnlock()
v, find := c.repo[key]
if find {
if v.expireAt.Before(time.Now()) {
return nil, false
}
return v.data, true
}
return nil, false
}
func (c *localCache) set(key string, value any, dura time.Duration) {
c.locker.Lock()
defer c.locker.Unlock()
c.repo[key] = &cacheData{
data: value,
expireAt: time.Now().Add(dura),
}
}
type LocalCache struct {
repoList [10]*localCache
}
func NewLocalCache() *LocalCache {
c := &LocalCache{}
for i := 0; i < len(c.repoList); i++ {
c.repoList[i] = newLocalCache()
}
return c
}
func (c *LocalCache) Get(key string) (any, bool) {
return c.getHashRepo(key).get(key)
}
func (c *LocalCache) Set(key string, value any, dura time.Duration) {
c.getHashRepo(key).set(key, value, dura)
}
func (c *LocalCache) getHashRepo(key string) *localCache {
return c.repoList[fastHashMod(key, len(c.repoList))]
}
func fastHashMod(key string, mod int) int {
// 使用前64位哈希值
hasher := sha256.New()
hasher.Write([]byte(key))
hashBytes := hasher.Sum(nil)
// 取前8字节转换为uint64
hashUint64 := binary.BigEndian.Uint64(hashBytes[:8])
return int(hashUint64 % uint64(mod))
}

23
admin/lib/cache/cache_redis.go vendored Normal file
View File

@ -0,0 +1,23 @@
package cache
import (
"github.com/redis/go-redis/v9"
)
type RedisCache struct {
rawClient *redis.Client
}
func NewRedisClient(addr string, dbNo int, user, pass string) (*RedisCache, error) {
opt := &redis.Options{
Addr: addr,
DB: dbNo,
Username: user,
Password: pass,
}
client := redis.NewClient(opt)
c := &RedisCache{
rawClient: client,
}
return c, nil
}

View File

View File

@ -22,8 +22,10 @@ const calcElColSpan = ref(0)
const rows = ref([])
const rules = ref({})
const current_page = ref(0)
const page_size = ref(0)
const current_page = ref(1)
const page_size = ref(2)
const pageSizes = [2, 10, 20, 50, 100]
const totalRowCount = ref(0)
const item = ref({
id: '',
@ -35,10 +37,13 @@ const item = ref({
const listData = async () => {
try {
let listParams = {
page_no: 0,
page_len: 100,
page_no: current_page.value,
page_len: page_size.value,
where_conditions: "",
}
console.log(`查询页:${listParams.page_no},查询页大小:${listParams.page_len}`)
let whereReqConditions = {
conditions: []
}
@ -58,6 +63,7 @@ const listData = async () => {
listRsp.value = rspData;
if (listRsp.value.code !== 200) throw new Error("请求失败,错误码:", listRsp.code);
fieldsDescInfo.value = listRsp.value.data.fields_desc
totalRowCount.value = listRsp.value.data.total_count
rows.value = listRsp.value.data.rows
for (let i = 0; i < fieldsDescInfo.value.length; i++) {
@ -286,6 +292,24 @@ const resetConditionSearch = () => {
}
}
const handlePaginationSizeChange = (val) => {
// console.log(`${val} `)
if (totalRowCount.value <= 0) {
return
}
if (page_size.value * current_page.value > totalRowCount.value) {
//
return
}
// console.log(`${page_size.value} `)
listData()
}
const handlePaginationCurChange = (val) => {
// console.log(`${val} `)
listData()
}
</script>
<template>
@ -374,9 +398,11 @@ const resetConditionSearch = () => {
<el-pagination
v-model:current-page="current_page"
v-model:page-size="page_size"
:page-sizes="[20, 50, 100]"
:page-sizes="pageSizes"
layout="total, sizes, prev, pager, next, jumper"
:total="400"
:total="totalRowCount"
@size-change="handlePaginationSizeChange"
@current-change="handlePaginationCurChange"
/>
</div>

View File

@ -1,12 +1,28 @@
# todo列表
# TODO列表
* 高优先级
- [x] 道具列表选择支持远程搜索(避免道具列表太长卡顿)
- [ ] 表格各种项目支持分页(难点是账户、角色、订单这种来自各个游戏区服数据如何分)
- [ ] 表格字段排序
- [x] 表格字段搜索
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁
- [ ] 表格各种项目支持分页(难点是账户、角色、订单这种来自各个游戏区服数据如何分)
- [x] 执行历史记录
- [ ] 执行历史查看和条件检索
- [ ] 公告发送游戏
- [x] 奖励码接入
- [x] 统一错误码内容
* 中优先级
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁)
- [x] 统一错误码内容
- [ ] 执行历史查看和条件检索
- [ ] 订单列表从游戏服拉取可以查看
* 低优先级
- [ ] 项目信息添加排序字段,支持菜单排序
- [ ] 表格字段排序
- [ ] 公告发送游戏
- [ ] 优化header右上角昵称部分显示
* bug记录
- [ ] 执行删除记录的历史信息只记录了id