c
This commit is contained in:
parent
23c83b745c
commit
647575c34d
@ -15,9 +15,10 @@ func RegisterGameApi(handler IGameApi) {
|
||||
}
|
||||
|
||||
type ResourceBtnInfo struct {
|
||||
Key string `json:"key"` // 按钮关键词,用于后端判断点击按钮
|
||||
Name string `json:"name"` // 按钮中文名
|
||||
BtnType string `json:"btn_type"` // primary warn等
|
||||
Key string `json:"key"` // 按钮关键词,用于后端判断点击按钮
|
||||
Name string `json:"name"` // 按钮中文名
|
||||
BtnColorType string `json:"btn_color_type"` // primary warn等
|
||||
BtnType int `json:"btn_type"` // 0:选中数据直接发往服务器的 1:前端带弹窗组件的 2:前端带页面跳转的
|
||||
}
|
||||
|
||||
type ResourceInitInfo struct {
|
||||
|
35
admin/apps/game/domain/account.go
Normal file
35
admin/apps/game/domain/account.go
Normal file
@ -0,0 +1,35 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"admin/apps/game/domain/projects"
|
||||
"admin/apps/game/domain/repo"
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/consts"
|
||||
"admin/internal/errcode"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AccountService struct {
|
||||
projectRepo repo.IProjectRepo
|
||||
}
|
||||
|
||||
func NewAccountService(db *gorm.DB) *AccountService {
|
||||
return &AccountService{projectRepo: repo.NewProjectRepo(db)}
|
||||
}
|
||||
|
||||
func (svc *AccountService) GetAccountDetail(projectId int, account string) (*dto.GetAccountDetailRsp, error) {
|
||||
_, projectEt, find, err := svc.projectRepo.GetById(projectId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !find {
|
||||
return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
|
||||
}
|
||||
hook, ok := projects.GetProjectResourceHook(projectEt, consts.ResourcesName_Account).(projects.IGetAccountDetailHook)
|
||||
if !ok {
|
||||
return nil, errcode.New(errcode.ServerError, "not found hook for project:%v", projectId)
|
||||
}
|
||||
|
||||
result, err := hook.GetDetail(projectEt, account)
|
||||
return result, err
|
||||
}
|
@ -25,12 +25,12 @@ func initCommonResourcesRepo(db *gorm.DB) {
|
||||
{
|
||||
serverRepo := r(consts.ResourcesName_Server, "服务器管理", repo.NewCommonResourceRepo(db, &model.Server{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
|
||||
serverRepo.GlobalBtns = []*api.ResourceBtnInfo{
|
||||
{Key: consts.BtnKeyGlobal_Server_DownAll, Name: "一键停服", BtnType: "info"},
|
||||
{Key: consts.BtnKeyGlobal_Server_UpAll, Name: "一键起服", BtnType: "warning"},
|
||||
{Key: consts.BtnKeyGlobal_Server_DownAll, Name: "一键停服", BtnColorType: "info"},
|
||||
{Key: consts.BtnKeyGlobal_Server_UpAll, Name: "一键起服", BtnColorType: "warning"},
|
||||
}
|
||||
serverRepo.RowBtns = []*api.ResourceBtnInfo{
|
||||
{Key: consts.BtnKeyRow_Server_Down, Name: "停服", BtnType: "info"},
|
||||
{Key: consts.BtnKeyRow_Server_Up, Name: "起服", BtnType: "warning"},
|
||||
{Key: consts.BtnKeyRow_Server_Down, Name: "停服", BtnColorType: "info"},
|
||||
{Key: consts.BtnKeyRow_Server_Up, Name: "起服", BtnColorType: "warning"},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,10 +163,14 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue
|
||||
for _, v := range rawValueList {
|
||||
list = append(list, v)
|
||||
}
|
||||
} else {
|
||||
for _, v := range rawValue.([]interface{}) {
|
||||
} else if rawValueList1, ok := rawValue.([]interface{}); ok {
|
||||
for _, v := range rawValueList1 {
|
||||
list = append(list, v.(string))
|
||||
}
|
||||
} else if rawValue == nil {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
i := 0
|
||||
|
@ -27,6 +27,10 @@ type IPostResourceOpRowsHook interface {
|
||||
RowsSelection(projectInfo *entity.Project, resource string, btnKey string, dtoObjs []dto.CommonDtoValues) (*dto.CommonRowsSelectionRsp, error)
|
||||
}
|
||||
|
||||
type IGetAccountDetailHook interface {
|
||||
GetDetail(projectInfo *entity.Project, account string) (*dto.GetAccountDetailRsp, error)
|
||||
}
|
||||
|
||||
type IGetAllValueChoicesHook interface {
|
||||
// 获取所有道具,可以用于前端页面做下拉选择等
|
||||
GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error)
|
||||
|
@ -24,7 +24,7 @@ func (hook *AccountHook) List(projectInfo *entity.Project, resource string, para
|
||||
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
|
||||
}
|
||||
|
||||
serverList, err := internal.GetRemoteServerInfoList(alisrvAddr)
|
||||
serverList, err := getAllRunningServers(projectInfo)
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
@ -98,6 +98,58 @@ func (hook *AccountHook) List(projectInfo *entity.Project, resource string, para
|
||||
return totalCount, fields, rows, nil
|
||||
}
|
||||
|
||||
func (hook *AccountHook) GetDetail(projectInfo *entity.Project, account string) (*dto.GetAccountDetailRsp, error) {
|
||||
accountId := account
|
||||
if accountId == "" {
|
||||
return nil, errcode.New(errcode.ParamsInvalid, "not found account id:%v", accountId)
|
||||
}
|
||||
|
||||
rsp := &dto.GetAccountDetailRsp{
|
||||
AccountInfo: &dto.AccountDetailInfo{},
|
||||
}
|
||||
|
||||
params := &url.Values{}
|
||||
|
||||
allRunningServers, err := getAllRunningServers(projectInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roleList := make([]*dto.AccountDetailRoleInfo, 0)
|
||||
for _, runningServer := range allRunningServers {
|
||||
params.Set("serverid", runningServer.ServerId)
|
||||
params.Set("userId", accountId)
|
||||
params.Set("userlist", "true")
|
||||
|
||||
type RspData struct {
|
||||
State struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
} `json:"state"`
|
||||
|
||||
Data *dto.AccountDetailInfo `json:"data"`
|
||||
}
|
||||
|
||||
detailRsp := &RspData{}
|
||||
err := httpclient.Request(projectInfo.GetApiAddr()+"/rolelist", "get", params, detailRsp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if detailRsp.Data == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rsp.AccountInfo = detailRsp.Data
|
||||
roleList = append(roleList, detailRsp.Data.RoleList...)
|
||||
}
|
||||
|
||||
rsp.AccountInfo.RoleCount = len(roleList)
|
||||
rsp.AccountInfo.RoleList = roleList
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func getPaginationServerReqList(projectInfo *entity.Project, serverInfoList internal.ServerInfoList,
|
||||
params *dto.CommonListReq, queryCountField func(*internal.ServerInfo) int) (
|
||||
totalCount int, reqServerInfoList []*internal.PageServerCountInfo, err error) {
|
||||
|
@ -1,15 +1,54 @@
|
||||
package internal
|
||||
|
||||
import "admin/lib/httpclient"
|
||||
import (
|
||||
"admin/lib/httpclient"
|
||||
"admin/lib/xlog"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
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"`
|
||||
ProjectId int `json:"project_id"`
|
||||
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"`
|
||||
IsServerDown bool `json:"open_whitelist_login"`
|
||||
}
|
||||
|
||||
func (server *ServerInfo) Down(api string) error {
|
||||
addr := api + "/gm"
|
||||
params := &url.Values{}
|
||||
params.Set("server", server.ServerId)
|
||||
params.Set("cmd_data", "pauseServer")
|
||||
params.Set("reason", "down")
|
||||
|
||||
rsp := make(map[string]any)
|
||||
err := httpclient.Request(addr, "get", params, rsp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xlog.Infof("操作项目:%v服务器:%v停服返回:%v", server.ProjectId, server.ServerId, rsp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *ServerInfo) Up(api string) error {
|
||||
addr := api + "/gm"
|
||||
params := &url.Values{}
|
||||
params.Set("server", server.ServerId)
|
||||
params.Set("cmd_data", "setWhite")
|
||||
params.Set("open", "false")
|
||||
|
||||
rsp := make(map[string]any)
|
||||
err := httpclient.Request(addr, "get", params, rsp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
xlog.Infof("操作项目:%v服务器:%v起服返回:%v", server.ProjectId, server.ServerId, rsp)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServerInfoList []*ServerInfo
|
||||
@ -26,6 +65,15 @@ type PageInfo struct {
|
||||
ServerList []*PageServerCountInfo // 参与组成这一页的服务器列表
|
||||
}
|
||||
|
||||
func (list ServerInfoList) FindByID(serverId string) *ServerInfo {
|
||||
for _, info := range list {
|
||||
if info.ServerId == serverId {
|
||||
return info
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list ServerInfoList) TidyToPageList(pageLen int, queryCountField func(info *ServerInfo) int) (totalCount int, pageList []*PageInfo) {
|
||||
curPageNo := 1
|
||||
curPageNeedCount := pageLen
|
||||
|
@ -35,7 +35,7 @@ func (hook *RoleHook) List(projectInfo *entity.Project, resource string, params
|
||||
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
|
||||
}
|
||||
|
||||
serverList, err := internal.GetRemoteServerInfoList(alisrvAddr)
|
||||
serverList, err := getAllRunningServers(projectInfo)
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
@ -2,10 +2,14 @@ package smdl
|
||||
|
||||
import (
|
||||
"admin/apps/game/domain/entity"
|
||||
"admin/apps/game/domain/projects/smdl/internal"
|
||||
"admin/apps/game/domain/repo"
|
||||
"admin/apps/game/model"
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/consts"
|
||||
"admin/internal/errcode"
|
||||
"admin/lib/httpclient"
|
||||
"admin/lib/xlog"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ServerHook struct {
|
||||
@ -19,15 +23,13 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, param
|
||||
return 0, nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
|
||||
}
|
||||
|
||||
fields = append(fields, getAttachFields()...)
|
||||
|
||||
serverInfoList, err := getRemoteServerInfoList(alisrvAddr)
|
||||
serverInfoList, err := internal.GetRemoteServerInfoList(alisrvAddr)
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
var findRemoteServerInfo *ServerInfo
|
||||
var findRemoteServerInfo *internal.ServerInfo
|
||||
for _, curRunning := range serverInfoList {
|
||||
et := (&entity.CommonResource{}).FromPo(&model.Server{}).FromDto(row)
|
||||
if et.ToPo().(*model.Server).ServerConfID == curRunning.ServerId {
|
||||
@ -41,10 +43,10 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, param
|
||||
row["Ccu"] = findRemoteServerInfo.Ccu
|
||||
row["TotalRoleCount"] = findRemoteServerInfo.TotalRoleCount
|
||||
row["TotalAccountCount"] = findRemoteServerInfo.TotalAccountCount
|
||||
if findRemoteServerInfo.OpenWhitelistLogin {
|
||||
row["IsWhitelistLogin"] = "是"
|
||||
if findRemoteServerInfo.IsServerDown {
|
||||
row["IsServerDown"] = "是"
|
||||
} else {
|
||||
row["IsWhitelistLogin"] = "否"
|
||||
row["IsServerDown"] = "否"
|
||||
}
|
||||
} else {
|
||||
row["RunningStatus"] = "未知"
|
||||
@ -59,98 +61,112 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, param
|
||||
}
|
||||
|
||||
func (hook *ServerHook) RowsSelection(projectInfo *entity.Project, resource string, btnKey string, dtoObjs []dto.CommonDtoValues) (*dto.CommonRowsSelectionRsp, error) {
|
||||
msg := ""
|
||||
serverIds := make([]string, 0, len(dtoObjs))
|
||||
|
||||
apiAddr := projectInfo.GetApiAddr()
|
||||
|
||||
allRunningServers, err := getAllRunningServers(projectInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opHandler := func(serverRunningInfo *internal.ServerInfo) error {
|
||||
switch btnKey {
|
||||
case consts.BtnKeyGlobal_Server_UpAll, consts.BtnKeyRow_Server_Up:
|
||||
err := serverRunningInfo.Up(apiAddr)
|
||||
if err != nil {
|
||||
xlog.Warnf("操作项目:%v 服务器:%v 起服返回错误:%v", projectInfo.GetProjectPo().ID, serverRunningInfo.ServerId, err)
|
||||
} else {
|
||||
xlog.Warnf("操作项目:%v 服务器:%v 起服成功", projectInfo.GetProjectPo().ID, serverRunningInfo.ServerId)
|
||||
}
|
||||
return err
|
||||
case consts.BtnKeyGlobal_Server_DownAll, consts.BtnKeyRow_Server_Down:
|
||||
err := serverRunningInfo.Down(apiAddr)
|
||||
if err != nil {
|
||||
xlog.Warnf("操作项目:%v 服务器:%v 停服返回错误:%v", projectInfo.GetProjectPo().ID, serverRunningInfo.ServerId, err)
|
||||
} else {
|
||||
xlog.Warnf("操作项目:%v 服务器:%v 停服成功", projectInfo.GetProjectPo().ID, serverRunningInfo.ServerId)
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return errcode.New(errcode.ParamsInvalid, "unsupport btn key:%v", btnKey)
|
||||
}
|
||||
}
|
||||
|
||||
errList := make([]error, 0)
|
||||
if len(dtoObjs) == 0 {
|
||||
for _, runningServer := range allRunningServers {
|
||||
err := opHandler(runningServer)
|
||||
if err != nil {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
switch btnKey {
|
||||
case consts.BtnKeyGlobal_Server_UpAll:
|
||||
msg = "一键起服"
|
||||
case consts.BtnKeyGlobal_Server_DownAll:
|
||||
msg = "一键停服"
|
||||
}
|
||||
} else {
|
||||
for _, dtoObj := range dtoObjs {
|
||||
serverId := dtoObj["ServerConfID"].(string)
|
||||
serverRunningInfo := allRunningServers.FindByID(serverId)
|
||||
if serverRunningInfo == nil {
|
||||
xlog.Warnf("项目:%v 服务器:%v 处于停服中,跳过操作", projectInfo.Po.Name, serverId)
|
||||
continue
|
||||
}
|
||||
serverIds = append(serverIds, serverId)
|
||||
err := opHandler(serverRunningInfo)
|
||||
if err != nil {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
switch btnKey {
|
||||
case consts.BtnKeyGlobal_Server_UpAll:
|
||||
msg = fmt.Sprintf("服务器:%v一键起服", serverIds)
|
||||
case consts.BtnKeyGlobal_Server_DownAll:
|
||||
msg = fmt.Sprintf("服务器:%v一键停服", serverIds)
|
||||
case consts.BtnKeyRow_Server_Up:
|
||||
msg = fmt.Sprintf("服务器:%v起服", serverIds)
|
||||
case consts.BtnKeyRow_Server_Down:
|
||||
msg = fmt.Sprintf("服务器:%v停服", serverIds)
|
||||
}
|
||||
}
|
||||
|
||||
return &dto.CommonRowsSelectionRsp{
|
||||
Msg: "执行行操作成功!",
|
||||
Msg: fmt.Sprintf("执行%v操作成功!", msg),
|
||||
NeedRefresh: true,
|
||||
}, 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"`
|
||||
}
|
||||
// getAllRunningServers 获取本地配置的服务器列表和远程获取的运行中服务器列表求个交集
|
||||
func getAllRunningServers(projectEt *entity.Project) (internal.ServerInfoList, error) {
|
||||
alisrvAddr := projectEt.GetApiAddr()
|
||||
if alisrvAddr == "" {
|
||||
return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectEt.Po.Name)
|
||||
}
|
||||
|
||||
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)
|
||||
remoteRunningServers, err := internal.GetRemoteServerInfoList(alisrvAddr)
|
||||
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,
|
||||
},
|
||||
localConfServers, err := repo.ServerRepoInstance.List(projectEt.Po.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allRunningServers := make(internal.ServerInfoList, 0, len(localConfServers))
|
||||
|
||||
for _, localServer := range localConfServers {
|
||||
serverInfo := localServer.Po.(*model.Server)
|
||||
for _, remoteServer := range remoteRunningServers {
|
||||
if serverInfo.ServerConfID == remoteServer.ServerId {
|
||||
remoteServer.ProjectId = serverInfo.ProjectId
|
||||
allRunningServers = append(allRunningServers, remoteServer)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return allRunningServers, nil
|
||||
}
|
||||
|
@ -60,3 +60,45 @@ type ItemInfo struct {
|
||||
ItemType int `json:"item_type"`
|
||||
Desc string `json:"desc"`
|
||||
}
|
||||
|
||||
type AccountDetailOrderInfo struct {
|
||||
ServerId string `json:"server_id"`
|
||||
AccountId string `json:"account_id"`
|
||||
RoleId string `json:"role_id"`
|
||||
RoleName string `json:"role_name"`
|
||||
Sn string `json:"sn"`
|
||||
ProductId string `json:"product_id"`
|
||||
Price int `json:"price"`
|
||||
PurchaseType string `json:"purchase_type"`
|
||||
PurchaseAt string `json:"purchase_at"`
|
||||
}
|
||||
|
||||
type AccountDetailRoleInfo struct {
|
||||
Platform string `json:"platform"` // ios ad
|
||||
ServerId string `json:"server_id"`
|
||||
Name string `json:"name"`
|
||||
RoleId string `json:"role_id"`
|
||||
TotalPayAmount int `json:"total_pay_amount"`
|
||||
TotalPayTimes int `json:"total_pay_times"`
|
||||
Level int `json:"level"`
|
||||
CurrencyItems []*ItemInfo `json:"currency_items"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
LastLoginTime string `json:"last_login_time"`
|
||||
OrderList []*AccountDetailOrderInfo `json:"order_list"`
|
||||
}
|
||||
|
||||
type AccountDetailInfo struct {
|
||||
AccountId string `json:"account_id"`
|
||||
Platform string `json:"platform"` // ios ad
|
||||
RoleCount int `json:"role_count"`
|
||||
Channel string `json:"channel"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
CreatedIp string `json:"created_ip"`
|
||||
TotalPayAmount int `json:"total_pay_amount"`
|
||||
TotalPayTimes int `json:"total_pay_times"`
|
||||
FirstPayAt string `json:"first_pay_at"`
|
||||
LastPayAt string `json:"last_pay_at"`
|
||||
LoginDeviceCount int `json:"login_device_count"`
|
||||
LastLoginTime string `json:"last_login_time"`
|
||||
RoleList []*AccountDetailRoleInfo `json:"role_list"`
|
||||
}
|
||||
|
@ -112,3 +112,11 @@ type CDKeyUsedInfo struct {
|
||||
type CDKeyUsedHistoryRsp struct {
|
||||
List []*CDKeyUsedInfo `json:"list"`
|
||||
}
|
||||
|
||||
type GetAccountDetailReq struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
type GetAccountDetailRsp struct {
|
||||
AccountInfo *AccountDetailInfo `json:"account_info"`
|
||||
}
|
||||
|
@ -12,11 +12,15 @@ func init() {
|
||||
|
||||
// Server 逻辑服
|
||||
type Server struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"uniqueIndex:idx_server"`
|
||||
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" uneditable:"true"`
|
||||
Desc string `name:"描述"`
|
||||
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"uniqueIndex:idx_server"`
|
||||
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" uneditable:"true"`
|
||||
Desc string `name:"描述"`
|
||||
RunningStatus string `name:"进程运行状态" desc:"进程运行状态:未知、运行中、停止" readonly:"true" uneditable:"true"`
|
||||
Ccu int `name:"实时在线" desc:"ccu" readonly:"true" uneditable:"true"`
|
||||
TotalRoleCount int `name:"总角色数" desc:"" readonly:"true" uneditable:"true"`
|
||||
TotalAccountCount int `name:"总账号数" desc:"" readonly:"true" uneditable:"true"`
|
||||
IsServerDown bool `name:"停服维护中" desc:"" readonly:"true" uneditable:"true"`
|
||||
// command_list接口服务器地址,为空代表由由项目统一提供command_list.
|
||||
// 取决于每个项目改造难度:
|
||||
// 为空就代表项目要实现一个自己统一对外暴露的gm调用服务对内聚合、分发指令执行,本后台执行指令只调用一次;
|
||||
|
16
admin/apps/game/server/ctl_account.go
Normal file
16
admin/apps/game/server/ctl_account.go
Normal file
@ -0,0 +1,16 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/context"
|
||||
)
|
||||
|
||||
func (ctl *controller) GetAccountDetail(ctx *context.WebContext, params *dto.GetAccountDetailReq, rsp *dto.GetAccountDetailRsp) error {
|
||||
projectId := getCtxURIProjectId(ctx)
|
||||
result, err := ctl.svc.GetAccountDetail(projectId, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*rsp = *result
|
||||
return nil
|
||||
}
|
@ -67,10 +67,11 @@ func (ctl *controller) CommonRowsSelection(ctx *context.WebContext, params *dto.
|
||||
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserName)
|
||||
|
||||
err := ctl.svc.CommonRowsSelection(ctx1, projectId, resource, params)
|
||||
result, err := ctl.svc.CommonRowsSelection(ctx1, projectId, resource, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*rsp = *result
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func (srv *Server) Route(engine *web.Engine, sdkEngine *web.Engine) {
|
||||
resourceUnderProjectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost)
|
||||
resourceUnderProjectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut)
|
||||
resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete)
|
||||
resourceUnderProjectGroup.Post("/selection", "多选", consts.WebPathPermit_Read, srv.ctl.CommonDelete)
|
||||
resourceUnderProjectGroup.Post("/selection", "多选", consts.WebPathPermit_Read, srv.ctl.CommonRowsSelection)
|
||||
}
|
||||
|
||||
projectGroup.Get("/:projectId/items", "获取项目所有道具列表", consts.WebPathPermit_Read, srv.ctl.GetProjectAllItems)
|
||||
@ -39,6 +39,12 @@ func (srv *Server) Route(engine *web.Engine, sdkEngine *web.Engine) {
|
||||
cdkeyGroup.Get("/export", "导出礼包码文件", consts.WebPathPermit_Write, srv.ctl.CDKeyExportFile)
|
||||
cdkeyGroup.Get("/used", "查看礼包码使用情况", consts.WebPathPermit_Write, srv.ctl.CDKeyUsedHistory)
|
||||
}
|
||||
|
||||
{
|
||||
// 礼包码特殊接口
|
||||
accountGroup := projectGroup.Group("/:projectId/account/special", "")
|
||||
accountGroup.Get("/detail", "账号详情查看", consts.WebPathPermit_Write, srv.ctl.GetAccountDetail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"admin/internal/event"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -17,6 +18,7 @@ type Service struct {
|
||||
resourceSvc *domain.CommonResourceService
|
||||
projectSvc *domain.ProjectService
|
||||
cdkeySvc *domain.CDKeyService
|
||||
accountSvc *domain.AccountService
|
||||
}
|
||||
|
||||
func New(db *gorm.DB) (*Service, error) {
|
||||
@ -25,6 +27,7 @@ func New(db *gorm.DB) (*Service, error) {
|
||||
resourceSvc: domain.NewCommonResourceService(db),
|
||||
projectSvc: domain.NewProjectService(db),
|
||||
cdkeySvc: domain.NewCDKeyService(db),
|
||||
accountSvc: domain.NewAccountService(db),
|
||||
}
|
||||
api.RegisterGameApi(svc)
|
||||
//err := svc.ensureProjectsDBData()
|
||||
@ -122,10 +125,10 @@ func (svc *Service) CommonDelete(ctx context.Context, projectId int, resourceNam
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) CommonRowsSelection(ctx context.Context, projectId int, resourceName string, param *dto.CommonRowsSelectionReq) error {
|
||||
func (svc *Service) CommonRowsSelection(ctx context.Context, projectId int, resourceName string, param *dto.CommonRowsSelectionReq) (*dto.CommonRowsSelectionRsp, error) {
|
||||
rsp, err := svc.resourceSvc.RowsSelection(projectId, resourceName, param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return rsp, err
|
||||
}
|
||||
|
||||
userId := ctx.Value("user_id").(int)
|
||||
@ -136,7 +139,7 @@ func (svc *Service) CommonRowsSelection(ctx context.Context, projectId int, reso
|
||||
UserName: userName,
|
||||
ProjectId: projectId,
|
||||
Resource: resourceName,
|
||||
Method: "删除",
|
||||
Method: fmt.Sprintf("选择行:%v", param.BtnKey),
|
||||
Any: param,
|
||||
})
|
||||
|
||||
|
9
admin/apps/game/service/service_account.go
Normal file
9
admin/apps/game/service/service_account.go
Normal file
@ -0,0 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"admin/apps/game/model/dto"
|
||||
)
|
||||
|
||||
func (svc *Service) GetAccountDetail(projectId int, params *dto.GetAccountDetailReq) (*dto.GetAccountDetailRsp, error) {
|
||||
return svc.accountSvc.GetAccountDetail(projectId, params.Account)
|
||||
}
|
10
ui/src/api/account.js
Normal file
10
ui/src/api/account.js
Normal file
@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export function accountGetDetail(baseUrl, params) {
|
||||
return request({
|
||||
url: baseUrl + '/special/detail',
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
@ -40,6 +40,15 @@ export function resourceDelete(url, params) {
|
||||
})
|
||||
}
|
||||
|
||||
export function resourceRowsSelectionPost(url, params) {
|
||||
return request({
|
||||
url: url + "/selection",
|
||||
method: 'post',
|
||||
data: params
|
||||
// params: {'dto': params}
|
||||
})
|
||||
}
|
||||
|
||||
export function resourceGetAllItems(projectId) {
|
||||
return request({
|
||||
url: '/project/' + projectId.toString() + "/items",
|
||||
|
57
ui/src/components/game/userDetail.vue
Normal file
57
ui/src/components/game/userDetail.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<script setup>
|
||||
|
||||
import userDetailAccount from '@/components/game/userDetailAccount.vue';
|
||||
import {accountGetDetail} from "@/api/account.js";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
const props = defineProps({
|
||||
rowInfo: {},
|
||||
})
|
||||
|
||||
const cachedResource = LocalCache.getCache("resource");
|
||||
const resource_url = cachedResource.meta.resource_url;
|
||||
|
||||
const activeName = ref('detail')
|
||||
|
||||
console.log("进入用户详情:", props.rowInfo)
|
||||
|
||||
const account = props.rowInfo.Account
|
||||
const serverId = props.rowInfo.ServerConfId
|
||||
|
||||
accountGetDetail(resource_url, props.rowInfo).then((res) => {
|
||||
console.log("获取账户详情返回:", res.data)
|
||||
}, (err) => {
|
||||
|
||||
})
|
||||
|
||||
const handleClick = (tab, event) => {
|
||||
// console.log("tab info:", tab)
|
||||
switch (tab.props.name) {
|
||||
case 'detail':
|
||||
console.log("点击了账号详情")
|
||||
break
|
||||
case 'order':
|
||||
console.log("点击了充值订单记录")
|
||||
break
|
||||
case 'currency':
|
||||
console.log("点击了货币记录")
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="账号详情" name="detail">
|
||||
<component :is="userDetailAccount"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="充值订单记录" name="order">充值订单子页面</el-tab-pane>
|
||||
<el-tab-pane label="货币记录" name="currency">货币记录子页面</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
ui/src/components/game/userDetailAccount.vue
Normal file
13
ui/src/components/game/userDetailAccount.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
账号详情子页面
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
ui/src/components/game/userDetailOrder.vue
Normal file
13
ui/src/components/game/userDetailOrder.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
账号详情子页面
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,11 +1,23 @@
|
||||
<script setup>
|
||||
import {ElNotification, selectGroupKey} from "element-plus";
|
||||
import {resourceDelete, resourceList, resourcePost, resourcePut, resourceGetAllItems} from "@/api/resource.js";
|
||||
import {
|
||||
resourceDelete,
|
||||
resourceList,
|
||||
resourcePost,
|
||||
resourcePut,
|
||||
resourceGetAllItems,
|
||||
resourceRowsSelectionPost
|
||||
} from "@/api/resource.js";
|
||||
import {ref, toRaw} from "vue";
|
||||
import {useRoute} from 'vue-router';
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
import empty from '@/components/restful/empty.vue';
|
||||
import {getWhereConditionDesc} from "@/utils/string.js";
|
||||
import UserDetail from "@/components/game/userDetail.vue";
|
||||
|
||||
const props = defineProps({
|
||||
rowClickDialogBtns: Array,
|
||||
})
|
||||
|
||||
const cachedResource = LocalCache.getCache("resource");
|
||||
|
||||
@ -15,9 +27,13 @@ const projectId = cachedResource.meta.projectId
|
||||
const resource_raw_node = cachedResource;
|
||||
const hasListPermit = resource_raw_node.meta.methods.get !== undefined && resource_raw_node.meta.methods.get === true;
|
||||
const globalClickBtns = resource_raw_node.meta.global_click_btns ? resource_raw_node.meta.global_click_btns : [];
|
||||
const rowClickBtns = resource_raw_node.meta.row_click_btns ? resource_raw_node.meta.row_click_btns : [];
|
||||
let rowClickBtns = resource_raw_node.meta.row_click_btns ? resource_raw_node.meta.row_click_btns : []
|
||||
rowClickBtns.push(...props.rowClickDialogBtns)
|
||||
const rowClickBtnVisibleList = reactive(rowClickBtns.map(() => false))
|
||||
const rowClickBtnSelectRow = ref(null)
|
||||
|
||||
console.log("global btns:", resource_raw_node)
|
||||
console.log("global btns:", globalClickBtns)
|
||||
console.log("row btns:", rowClickBtns)
|
||||
|
||||
const resource_url = cachedResource.meta.resource_url;
|
||||
const fieldsDescInfo = ref([])
|
||||
@ -145,17 +161,50 @@ const dialogObjectForm = ref({
|
||||
|
||||
const tableSelectRows = ref([])
|
||||
|
||||
const handleTableSelectRowsSendToServer = (keyType, rows) => {
|
||||
console.log(`选择表格行,类型:${keyType},:`, rows)
|
||||
const handleTableSelectRowsSendToServer = (btnInfo, rows) => {
|
||||
console.log(`选择表格行,类型:${btnInfo},:`, rows)
|
||||
|
||||
ElMessageBox.confirm("确定要操作吗?").then(() => {
|
||||
resourceRowsSelectionPost(resource_url, {btn_key: btnInfo.key, rows: rows}).then((res) => {
|
||||
const resultMsg = res.data.msg
|
||||
const needRefresh = res.data.need_refresh
|
||||
|
||||
ElNotification({
|
||||
title: "操作行数据返回",
|
||||
message: h('i', {style: 'color: teal'}, resultMsg),
|
||||
duration: 900,
|
||||
onClose(vm) {
|
||||
if (needRefresh) {
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// console.log("返回消息:", resultMsg, needRefresh)
|
||||
}, (err) => {
|
||||
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
const handleTableSelectChange = (rows) => {
|
||||
tableSelectRows.value = rows
|
||||
}
|
||||
const tableSelectRows1 = (keyType) => {
|
||||
handleTableSelectRowsSendToServer(keyType, tableSelectRows.value)
|
||||
const tableSelectRows1 = (btnInfo) => {
|
||||
handleTableSelectRowsSendToServer(btnInfo, tableSelectRows.value)
|
||||
}
|
||||
const tableSelectRows2 = (keyType, index, row) => {
|
||||
handleTableSelectRowsSendToServer(keyType, [row])
|
||||
const tableSelectRows2 = (btnInfo, index, row) => {
|
||||
if (btnInfo.btn_type > 0) {
|
||||
if (btnInfo.btn_type == 1) {
|
||||
}
|
||||
return
|
||||
}
|
||||
handleTableSelectRowsSendToServer(btnInfo, [row])
|
||||
}
|
||||
const tableSelectRow3 = (i, row) => {
|
||||
rowClickBtnSelectRow.value = row
|
||||
rowClickBtnVisibleList[i] = true
|
||||
console.log("点击按钮:", rowClickBtnSelectRow)
|
||||
}
|
||||
|
||||
const submitAdd = async () => {
|
||||
@ -386,8 +435,8 @@ const handlePaginationCurChange = (val) => {
|
||||
添加
|
||||
</el-button>
|
||||
|
||||
<el-button v-for="btn in globalClickBtns" size="default" :type="btn.btn_type"
|
||||
@click="tableSelectRows1(btn.key)">
|
||||
<el-button v-for="btn in globalClickBtns" size="default" :type="btn.btn_color_type"
|
||||
@click="tableSelectRows1(btn)">
|
||||
{{ btn.name }}
|
||||
</el-button>
|
||||
</el-row>
|
||||
@ -423,10 +472,29 @@ const handlePaginationCurChange = (val) => {
|
||||
<!-- </el-icon>-->
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
<el-button v-for="btn in rowClickBtns" size="default" :type="btn.btn_type"
|
||||
@click="tableSelectRows2(btn.key, scope.$index, scope.row)">
|
||||
{{ btn.name }}
|
||||
</el-button>
|
||||
<template v-for="(btn, index) in rowClickBtns">
|
||||
<template v-if="btn.btn_type === 0">
|
||||
<el-button size="default" :type="btn.btn_color_type"
|
||||
@click="tableSelectRows2(btn, scope.$index, scope.row)">
|
||||
{{ btn.name }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="btn.btn_type === 1">
|
||||
<el-button size="default" :type="btn.btn_color_type"
|
||||
@click="tableSelectRow3(index, scope.row)">
|
||||
{{ btn.name }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="btn.btn_type === 2">
|
||||
<el-button size="default" :type="btn.btn_color_type"
|
||||
@click="btn.btn_callback_visible = true">
|
||||
{{ btn.name }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -445,6 +513,14 @@ const handlePaginationCurChange = (val) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-for="(btn, index) in rowClickBtns">
|
||||
<el-dialog v-model="rowClickBtnVisibleList[index]" :title="btn.name"
|
||||
@close="rowClickBtnVisibleList[index]=false"
|
||||
destroy-on-close>
|
||||
<component :is="btn.btn_callback_component" :rowInfo="rowClickBtnSelectRow"/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogAddFormRef" :model="dialogObjectForm" :rules="rules" label-position="right"
|
||||
|
@ -1,11 +1,43 @@
|
||||
<script setup>
|
||||
|
||||
import tableView from "@/components/restful/table.vue";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
import userDetail from "@/components/game/userDetail.vue";
|
||||
|
||||
const cachedResource = LocalCache.getCache("resource");
|
||||
|
||||
const rowClickDialogBtns = []
|
||||
|
||||
switch (cachedResource.meta.resource) {
|
||||
case "account":
|
||||
// 给账号页面添加按钮
|
||||
if (rowClickDialogBtns.length > 0) {
|
||||
break
|
||||
}
|
||||
rowClickDialogBtns.push({
|
||||
key: "account:detail",
|
||||
name: "用户详情",
|
||||
btn_color_type: "primary",
|
||||
btn_type: 1,
|
||||
btn_callback_component: userDetail,
|
||||
}, {
|
||||
key: "account:detail",
|
||||
name: "白名单",
|
||||
btn_color_type: "success",
|
||||
btn_type: 2,
|
||||
}, {
|
||||
key: "account:detail",
|
||||
name: "封禁",
|
||||
btn_color_type: "info",
|
||||
btn_type: 2,
|
||||
},)
|
||||
break
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="tableView"></component>
|
||||
<component :is="tableView" :rowClickDialogBtns="rowClickDialogBtns"></component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
Loading…
x
Reference in New Issue
Block a user