优化账户、角色分页
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