This commit is contained in:
likun 2025-05-13 18:13:22 +08:00
parent 23c83b745c
commit 647575c34d
24 changed files with 596 additions and 137 deletions

View File

@ -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 {

View 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
}

View File

@ -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"},
}
}

View File

@ -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

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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"`
}

View File

@ -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调用服务对内聚合、分发指令执行本后台执行指令只调用一次

View 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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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,
})

View 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
View 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,
})
}

View File

@ -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",

View 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>

View File

@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
账号详情子页面
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
账号详情子页面
</div>
</template>
<style scoped>
</style>

View File

@ -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"

View File

@ -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>