优化账户、角色分页
This commit is contained in:
parent
902059e11b
commit
8ae6f7ee1d
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
110
admin/apps/game/domain/projects/smdl/internal/server.go
Normal file
110
admin/apps/game/domain/projects/smdl/internal/server.go
Normal 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
|
||||
}
|
67
admin/apps/game/domain/projects/smdl/internal/server_test.go
Normal file
67
admin/apps/game/domain/projects/smdl/internal/server_test.go
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
64
admin/apps/game/domain/projects/smdl/support_account.go
Normal file
64
admin/apps/game/domain/projects/smdl/support_account.go
Normal 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
|
||||
}
|
@ -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{}
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
32
admin/apps/game/model/support_account.go
Normal file
32
admin/apps/game/model/support_account.go
Normal 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)
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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=
|
||||
|
@ -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 (
|
||||
|
@ -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返回
|
||||
|
@ -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
101
admin/lib/cache/cache_local.go
vendored
Normal 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
23
admin/lib/cache/cache_redis.go
vendored
Normal 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
|
||||
}
|
@ -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>
|
||||
|
||||
|
30
ui/todo.md
30
ui/todo.md
@ -1,12 +1,28 @@
|
||||
# todo列表
|
||||
# TODO列表
|
||||
|
||||
* 高优先级
|
||||
|
||||
- [x] 道具列表选择支持远程搜索(避免道具列表太长卡顿)
|
||||
- [ ] 表格各种项目支持分页(难点是账户、角色、订单这种来自各个游戏区服数据如何分)
|
||||
- [ ] 表格字段排序
|
||||
- [x] 表格字段搜索
|
||||
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁)
|
||||
- [ ] 表格各种项目支持分页(难点是账户、角色、订单这种来自各个游戏区服数据如何分)
|
||||
- [x] 执行历史记录
|
||||
- [ ] 执行历史查看和条件检索
|
||||
- [ ] 公告发送游戏
|
||||
- [x] 奖励码接入
|
||||
- [x] 统一错误码内容
|
||||
|
||||
* 中优先级
|
||||
|
||||
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁)
|
||||
- [x] 统一错误码内容
|
||||
- [ ] 执行历史查看和条件检索
|
||||
- [ ] 订单列表从游戏服拉取可以查看
|
||||
|
||||
* 低优先级
|
||||
|
||||
- [ ] 项目信息添加排序字段,支持菜单排序
|
||||
- [ ] 表格字段排序
|
||||
- [ ] 公告发送游戏
|
||||
- [ ] 优化header右上角昵称部分显示
|
||||
|
||||
* bug记录
|
||||
|
||||
- [ ] 执行删除记录的历史信息只记录了id
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user