diff --git a/admin/apps/game/domain/comm_resource.go b/admin/apps/game/domain/comm_resource.go index 6b94418..d3a584b 100644 --- a/admin/apps/game/domain/comm_resource.go +++ b/admin/apps/game/domain/comm_resource.go @@ -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 { diff --git a/admin/apps/game/domain/projects/ihook.go b/admin/apps/game/domain/projects/ihook.go index 1a7c290..4e00ada 100644 --- a/admin/apps/game/domain/projects/ihook.go +++ b/admin/apps/game/domain/projects/ihook.go @@ -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 { diff --git a/admin/apps/game/domain/projects/projects.go b/admin/apps/game/domain/projects/projects.go index c261377..ea40118 100644 --- a/admin/apps/game/domain/projects/projects.go +++ b/admin/apps/game/domain/projects/projects.go @@ -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 diff --git a/admin/apps/game/domain/projects/smdl/account.go b/admin/apps/game/domain/projects/smdl/account.go index bfb9207..e67d4e3 100644 --- a/admin/apps/game/domain/projects/smdl/account.go +++ b/admin/apps/game/domain/projects/smdl/account.go @@ -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 } diff --git a/admin/apps/game/domain/projects/smdl/internal/server.go b/admin/apps/game/domain/projects/smdl/internal/server.go new file mode 100644 index 0000000..b6ab916 --- /dev/null +++ b/admin/apps/game/domain/projects/smdl/internal/server.go @@ -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 +} diff --git a/admin/apps/game/domain/projects/smdl/internal/server_test.go b/admin/apps/game/domain/projects/smdl/internal/server_test.go new file mode 100644 index 0000000..5fd9a70 --- /dev/null +++ b/admin/apps/game/domain/projects/smdl/internal/server_test.go @@ -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) +} diff --git a/admin/apps/game/domain/projects/smdl/role.go b/admin/apps/game/domain/projects/smdl/role.go index 7c2e6e9..48b7743 100644 --- a/admin/apps/game/domain/projects/smdl/role.go +++ b/admin/apps/game/domain/projects/smdl/role.go @@ -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 } diff --git a/admin/apps/game/domain/projects/smdl/server.go b/admin/apps/game/domain/projects/smdl/server.go index 2a2b710..eb52257 100644 --- a/admin/apps/game/domain/projects/smdl/server.go +++ b/admin/apps/game/domain/projects/smdl/server.go @@ -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, + }, + } } diff --git a/admin/apps/game/domain/projects/smdl/support_account.go b/admin/apps/game/domain/projects/smdl/support_account.go new file mode 100644 index 0000000..786e3e0 --- /dev/null +++ b/admin/apps/game/domain/projects/smdl/support_account.go @@ -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 +} diff --git a/admin/apps/game/domain/projects/smdl/whitelist.go b/admin/apps/game/domain/projects/smdl/whitelist.go index 833414d..fbbb880 100644 --- a/admin/apps/game/domain/projects/smdl/whitelist.go +++ b/admin/apps/game/domain/projects/smdl/whitelist.go @@ -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{} diff --git a/admin/apps/game/domain/repo/comm_resource.go b/admin/apps/game/domain/repo/comm_resource.go index ed47684..c119293 100644 --- a/admin/apps/game/domain/repo/comm_resource.go +++ b/admin/apps/game/domain/repo/comm_resource.go @@ -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) { diff --git a/admin/apps/game/model/dto/common.go b/admin/apps/game/model/dto/common.go index 1a01937..0bcbdc1 100644 --- a/admin/apps/game/model/dto/common.go +++ b/admin/apps/game/model/dto/common.go @@ -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 { diff --git a/admin/apps/game/model/support_account.go b/admin/apps/game/model/support_account.go new file mode 100644 index 0000000..e690de1 --- /dev/null +++ b/admin/apps/game/model/support_account.go @@ -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) +} diff --git a/admin/apps/game/service/service.go b/admin/apps/game/service/service.go index 3c52de1..5829c1e 100644 --- a/admin/apps/game/service/service.go +++ b/admin/apps/game/service/service.go @@ -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) { diff --git a/admin/go.mod b/admin/go.mod index a0d98af..5c9f4b2 100644 --- a/admin/go.mod +++ b/admin/go.mod @@ -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 ) diff --git a/admin/go.sum b/admin/go.sum index 4259e43..ab00b62 100644 --- a/admin/go.sum +++ b/admin/go.sum @@ -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= diff --git a/admin/internal/consts/consts.go b/admin/internal/consts/consts.go index 1359c74..bcdb7eb 100644 --- a/admin/internal/consts/consts.go +++ b/admin/internal/consts/consts.go @@ -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 ( diff --git a/admin/internal/errcode/code.go b/admin/internal/errcode/code.go index 77ad2e2..e08cc30 100644 --- a/admin/internal/errcode/code.go +++ b/admin/internal/errcode/code.go @@ -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返回 diff --git a/admin/internal/global/var.go b/admin/internal/global/var.go index 98f4790..3ab3d19 100644 --- a/admin/internal/global/var.go +++ b/admin/internal/global/var.go @@ -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() // 全局本地内存缓存器 ) diff --git a/admin/lib/cache/cache_local.go b/admin/lib/cache/cache_local.go new file mode 100644 index 0000000..452a7fc --- /dev/null +++ b/admin/lib/cache/cache_local.go @@ -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)) +} diff --git a/admin/lib/cache/cache_redis.go b/admin/lib/cache/cache_redis.go new file mode 100644 index 0000000..5d55882 --- /dev/null +++ b/admin/lib/cache/cache_redis.go @@ -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 +} diff --git a/admin/todo.md b/admin/todo.md deleted file mode 100644 index e69de29..0000000 diff --git a/ui/src/components/restful/table.vue b/ui/src/components/restful/table.vue index 7f29608..81e3465 100644 --- a/ui/src/components/restful/table.vue +++ b/ui/src/components/restful/table.vue @@ -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() +} +