This commit is contained in:
likun 2025-05-12 18:43:41 +08:00
parent d562c644dd
commit 23c83b745c
25 changed files with 325 additions and 84 deletions

View File

@ -14,11 +14,19 @@ func RegisterGameApi(handler IGameApi) {
gameApiInstance = handler
}
type ResourceBtnInfo struct {
Key string `json:"key"` // 按钮关键词,用于后端判断点击按钮
Name string `json:"name"` // 按钮中文名
BtnType string `json:"btn_type"` // primary warn等
}
type ResourceInitInfo struct {
Resource string `json:"resource"`
Desc string `json:"desc"`
ShowMethods []string `json:"show_methods"` // 资源权限列表
MethodsPermissionStr []string `json:"methods_permission"` // 资源权限字符串
Resource string `json:"resource"`
Desc string `json:"desc"`
ShowMethods []string `json:"show_methods"` // 资源权限列表
MethodsPermissionStr []string `json:"methods_permission"` // 资源权限字符串
GlobalBtns []*ResourceBtnInfo `json:"global_btns"` // 页面全局按钮,放在新增后面
RowBtns []*ResourceBtnInfo `json:"row_btns"` // 页面每一行的功能按钮
}
type ProjectInitInfo struct {

View File

@ -21,7 +21,19 @@ 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)
{
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"},
}
serverRepo.RowBtns = []*api.ResourceBtnInfo{
{Key: consts.BtnKeyRow_Server_Down, Name: "停服", BtnType: "info"},
{Key: consts.BtnKeyRow_Server_Up, Name: "起服", BtnType: "warning"},
}
}
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拉
@ -29,8 +41,8 @@ func initCommonResourcesRepo(db *gorm.DB) {
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) // 个人邮件发放就没法撤回?
r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post) // 直接删除,别修改了,玩家收到的更乱
r(consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_CDKey, "礼包码", repo.NewCommonResourceRepo(db, &model.CDKey{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_Notice, "公告(暂无)", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
r(consts.ResourcesName_DevicePush, "设备推送(暂无)", repo.NewCommonResourceRepo(db, &model.DevicePush{}), ShowMethod_Get)
}
@ -101,7 +113,7 @@ func (svc *CommonResourceService) Create(projectId int, resource string, dtoObj
}
createOne := func(obj dto.CommonDtoValues) (dto.CommonDtoValues, error) {
et, err := findCommResourceRepo(resource).Create(projectEt, obj)
et, err := findCommResourceRepo(resource).Create(projectEt, resource, obj)
if err != nil {
return nil, err
}
@ -200,6 +212,20 @@ func (svc *CommonResourceService) Delete(projectId int, resource string, id int)
return oldEt, nil
}
func (svc *CommonResourceService) RowsSelection(projectId int, resourceName string, params *dto.CommonRowsSelectionReq) (*dto.CommonRowsSelectionRsp, error) {
_, projectEt, find, err := svc.projectRepo.GetById(projectId)
if err != nil {
return nil, err
}
if resourceName != consts.ResourcesName_Project && !find {
return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
if hook, ok := projects.GetProjectResourceHook(projectEt, resourceName).(projects.IPostResourceOpRowsHook); ok {
return hook.RowsSelection(projectEt, resourceName, params.BtnKey, params.Rows)
}
return nil, nil
}
func (svc *CommonResourceService) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo {
list := make([]*api.ResourceInitInfo, 0, len(commResourcesRepo))
for _, v := range commResourcesRepo {
@ -207,6 +233,8 @@ func (svc *CommonResourceService) GetSupportResourcesList(permissions []string)
Resource: v.Resource,
Desc: v.Desc,
ShowMethods: v.ShowMethods,
GlobalBtns: v.GlobalBtns,
RowBtns: v.RowBtns,
})
}
return list
@ -225,11 +253,13 @@ type resourceRepoInfo struct {
Repo repo.ICommonResourceRepo
ShowMethods []string
showMethods int
GlobalBtns []*api.ResourceBtnInfo
RowBtns []*api.ResourceBtnInfo
}
var commResourcesRepo = make([]*resourceRepoInfo, 0)
func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) {
func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) *resourceRepoInfo {
curRepo := &resourceRepoInfo{
Resource: resource,
Desc: desc,
@ -251,6 +281,8 @@ func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) {
}
commResourcesRepo = append(commResourcesRepo, curRepo)
return curRepo
}
func findCommResourceRepo(resource string) repo.ICommonResourceRepo {

View File

@ -23,6 +23,10 @@ type IPostResourceOpDeleteHook interface {
Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error
}
type IPostResourceOpRowsHook interface {
RowsSelection(projectInfo *entity.Project, resource string, btnKey string, dtoObjs []dto.CommonDtoValues) (*dto.CommonRowsSelectionRsp, error)
}
type IGetAllValueChoicesHook interface {
// 获取所有道具,可以用于前端页面做下拉选择等
GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error)

View File

@ -42,20 +42,20 @@ func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj
params.Add("server", banInfo.ServerConfID)
params.Add("roleid", roleId)
expireAt := banInfo.ExpireAt.Unix()
expireAt := banInfo.ExpireAt
if expireAt <= 0 {
if !expireAt.Valid {
// 解封
params.Add("forbidtime", "0")
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
} else {
dura := (expireAt - time.Now().Unix()) / 60 // 神魔大陆封禁是分钟
dura := int64(expireAt.Time.Sub(time.Now()).Minutes()) // 神魔大陆封禁是分钟
if dura <= 0 {
// 解封
params.Add("forbidtime", "0")
} else {
params.Add("forbidtime", strconv.FormatInt(dura, 10))
params.Add("forbidtime", strconv.FormatInt(int64(dura), 10))
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
}
@ -70,6 +70,59 @@ func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj
return nil
}
func (hook *BanHook) Edit(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.Ban{}).FromDto(dtoObj)
banInfo := et.ToPo().(*model.Ban)
banApi := ""
switch banInfo.BanType {
case consts.BanType_Role:
banApi = "banrole"
case consts.BanType_RoleChat:
banApi = "banroletalk"
default:
xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType)
return nil
}
roleId := banInfo.Value
params := &url.Values{}
params.Add("server", banInfo.ServerConfID)
params.Add("roleid", roleId)
expireAt := banInfo.ExpireAt
if !expireAt.Valid {
// 解封
params.Add("forbidtime", "0")
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
} else {
dura := int64(expireAt.Time.Sub(time.Now()).Minutes()) // 神魔大陆封禁是分钟
if dura <= 0 {
// 解封
params.Add("forbidtime", "0")
} else {
params.Add("forbidtime", strconv.FormatInt(int64(dura), 10))
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
}
}
rsp := make(map[string]any)
err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp)
if err != nil {
return err
}
return nil
}
func (hook *BanHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {

View File

@ -9,8 +9,6 @@ import (
"admin/lib/httpclient"
"admin/lib/xlog"
"net/url"
"strconv"
"time"
)
type NoticeHook struct {
@ -22,55 +20,57 @@ func (hook *NoticeHook) Create(projectInfo *entity.Project, resource string, dto
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
}
et := (&entity.CommonResource{}).FromPo(&model.Ban{}).FromDto(dtoObj)
banInfo := et.ToPo().(*model.Ban)
banApi := ""
switch banInfo.BanType {
case consts.BanType_Role:
banApi = "banrole"
case consts.BanType_RoleChat:
banApi = "banroletalk"
default:
xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType)
return nil
}
roleId := banInfo.Value
params := &url.Values{}
params.Add("server", banInfo.ServerConfID)
params.Add("roleid", roleId)
expireAt := banInfo.ExpireAt.Unix()
if expireAt <= 0 {
// 解封
params.Add("forbidtime", "0")
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
} else {
dura := (expireAt - time.Now().Unix()) / 60 // 神魔大陆封禁是分钟
if dura <= 0 {
// 解封
params.Add("forbidtime", "0")
} else {
params.Add("forbidtime", strconv.FormatInt(dura, 10))
params.Add("desc", banInfo.BanReason)
params.Add("notifytouser", banInfo.BanNotifyReason)
}
}
rsp := make(map[string]any)
err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp)
if err != nil {
return err
}
//et := (&entity.CommonResource{}).FromPo(&model.Notice{}).FromDto(dtoObj)
//banInfo := et.ToPo().(*model.Notice)
//
//banApi := ""
//switch banInfo.BanType {
//case consts.BanType_Role:
// banApi = "banrole"
//case consts.BanType_RoleChat:
// banApi = "banroletalk"
//default:
// xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType)
// return nil
//}
//
//roleId := banInfo.Value
//
//params := &url.Values{}
//params.Add("server", banInfo.ServerConfID)
//params.Add("roleid", roleId)
//
//expireAt := banInfo.ExpireAt.Unix()
//
//if expireAt <= 0 {
// // 解封
// params.Add("forbidtime", "0")
// params.Add("desc", banInfo.BanReason)
// params.Add("notifytouser", banInfo.BanNotifyReason)
//} else {
// dura := (expireAt - time.Now().Unix()) / 60 // 神魔大陆封禁是分钟
// if dura <= 0 {
// // 解封
// params.Add("forbidtime", "0")
// } else {
// params.Add("forbidtime", strconv.FormatInt(dura, 10))
// params.Add("desc", banInfo.BanReason)
// params.Add("notifytouser", banInfo.BanNotifyReason)
// }
//}
//
//rsp := make(map[string]any)
//err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp)
//if err != nil {
// return err
//}
return nil
}
func (hook *NoticeHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error {
return nil
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)

View File

@ -58,6 +58,13 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, param
return totalCount, fields, rows, nil
}
func (hook *ServerHook) RowsSelection(projectInfo *entity.Project, resource string, btnKey string, dtoObjs []dto.CommonDtoValues) (*dto.CommonRowsSelectionRsp, error) {
return &dto.CommonRowsSelectionRsp{
Msg: "执行行操作成功!",
NeedRefresh: true,
}, nil
}
type ServerInfo struct {
ServerId string `json:"serverId"`
ServerName string `json:"serverName"`
@ -135,7 +142,7 @@ func getAttachFields() []*dto.CommonDtoFieldDesc {
Uneditable: true,
},
{
Name: "是否只允许白名单登录",
Name: "停服维护中",
Key: "IsWhitelistLogin",
Type: "bool",
HelpText: "打开就是相当于停服维护中了",

View File

@ -5,6 +5,7 @@ import (
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/internal/global"
"errors"
"gorm.io/gorm"
"time"
@ -23,6 +24,25 @@ func NewCDKeyRepo(db *gorm.DB) ICDKeyRepo {
return &cdKeyRepoImpl{db: db}
}
func cdKeyPreCreateHook(projectEt *entity.Project, dtoObj dto.CommonDtoValues) error {
et := (&entity.CommonResource{}).FromPo(&model.CDKey{}).FromDto(dtoObj)
do := entity.NewCDKey(et.Po.(*model.CDKey))
if do.IsGlobalType() {
if do.Po.Code == "" {
return errcode.New(errcode.CDKeyInvalid, "cdkey empty:%v", dtoObj)
}
dbEt, find, err := NewCDKeyRepo(global.GLOB_DB).GetByKey(projectEt.GetProjectPo().ID, do.Po.Code)
if err != nil {
return errcode.New(errcode.DBError, "get cdkey by key:%v db error:%v", do.Po.Code, err)
}
if find {
return errcode.New(errcode.DBInsertDuplicate, "create cdkey:%v key duplicate with:%v", dtoObj, et, dbEt.Po)
}
}
return nil
}
type cdKeyRepoImpl struct {
db *gorm.DB
}

View File

@ -4,6 +4,7 @@ import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/errcode"
"admin/lib/xlog"
"errors"
@ -15,10 +16,14 @@ import (
"time"
)
var createHooks = map[string]func(projectEt *entity.Project, et dto.CommonDtoValues) error{
consts.ResourcesName_CDKey: cdKeyPreCreateHook,
}
type ICommonResourceRepo interface {
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)
Create(projectEt *entity.Project, resource string, et dto.CommonDtoValues) (*entity.CommonResource, error)
Edit(projectEt *entity.Project, et dto.CommonDtoValues) error
Delete(projectEt *entity.Project, id int) (*entity.CommonResource, bool, error)
}
@ -102,8 +107,14 @@ func (repo *commonResourceRepoImpl) GetById(projectEt *entity.Project, id int) (
return repo.fieldsDescInfoFun(projectEt), (&entity.CommonResource{}).FromPo(po), true, nil
}
func (repo *commonResourceRepoImpl) Create(projectEt *entity.Project, dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) {
func (repo *commonResourceRepoImpl) Create(projectEt *entity.Project, resource string, dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) {
et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj)
if handler, find := createHooks[resource]; find {
if err := handler(projectEt, dtoObj); err != nil {
return et, err
}
}
err := repo.db.Create(et.Po).Error
if err != nil {
if strings.Contains(err.Error(), "Duplicate entry") || strings.Contains(err.Error(), "UNIQUE constraint") {

View File

@ -4,6 +4,7 @@ import (
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/db"
"database/sql"
"time"
)
@ -12,14 +13,14 @@ func init() {
}
type Ban struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId int `gorm:"uniqueIndex:idx_ban"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetServerConfIDChoices" uneditable:"true"`
BanType string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" uneditable:"true"`
Value string `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁值" required:"true" desc:"根据封禁类型填对应值例如ip就填ip地址" uneditable:"true"`
BanReason string `name:"封禁描述" desc:"封禁描述,入库记录的描述信息" required:"true"`
BanNotifyReason string `name:"封禁弹窗" desc:"封禁弹窗内容,会推送给客户端弹窗" required:"true"`
ExpireAt time.Time `name:"封禁到期时间" desc:"封禁到期时间0表示永久封禁"`
ID int `gorm:"primarykey" readonly:"true"`
ProjectId int `gorm:"index:project_id"`
ServerConfID string `gorm:"type:varchar(200);index:idx_server" name:"区服id" required:"true" choices:"GetServerConfIDChoices" uneditable:"true" where:"eq"`
BanType string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" uneditable:"true" where:"eq"`
Value string `gorm:"type:varchar(128);index:value" name:"封禁值" required:"true" desc:"根据封禁类型填对应值例如ip就填ip地址" uneditable:"true" where:"eq"`
BanReason string `name:"封禁描述" desc:"封禁描述,入库记录的描述信息" required:"true"`
BanNotifyReason string `name:"封禁弹窗" desc:"封禁弹窗内容,会推送给客户端弹窗" required:"true"`
ExpireAt sql.NullTime `name:"封禁到期时间" desc:"封禁到期时间0表示永久封禁"`
CreatedAt time.Time `readonly:"true"`
}

View File

@ -18,7 +18,7 @@ type CDKey struct {
Name string `gorm:"type:varchar(100);unique" name:"礼包说明" required:"true" uneditable:"true"`
ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" desc:"不选就是全服通用" type:"[]string" choices:"GetChoiceServers" multi_choice:"true" uneditable:"true""`
CodeType int `name:"礼包类型" required:"true" choices:"GetCodeTypeChoices" uneditable:"true"`
Code string `gorm:"type:VARCHAR(50);index" name:"礼包码" desc:"一码通用才配置"`
Code string `gorm:"type:VARCHAR(50);index" name:"礼包码" desc:"一码通用才配置" uneditable:"true"`
CodeNum int `name:"礼包数量" desc:"一码一用才配置"`
ValidStartTime sql.NullTime `name:"生效起始时间"`
ValidEndTime sql.NullTime `name:"生效结束时间"`

View File

@ -43,6 +43,16 @@ type CommonDeleteRsp struct {
Id int `json:"id"`
}
type CommonRowsSelectionReq struct {
BtnKey string `json:"btn_key"`
Rows []CommonDtoValues `json:"rows"`
}
type CommonRowsSelectionRsp struct {
Msg string `json:"msg"`
NeedRefresh bool `json:"need_refresh"`
}
type CommandListReq struct {
Addr string `json:"addr"`
}

View File

@ -16,6 +16,7 @@ type Server struct {
ProjectId int `gorm:"uniqueIndex:idx_server"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" uneditable:"true"`
Desc string `name:"描述"`
// command_list接口服务器地址为空代表由由项目统一提供command_list.
// 取决于每个项目改造难度:
// 为空就代表项目要实现一个自己统一对外暴露的gm调用服务对内聚合、分发指令执行本后台执行指令只调用一次

View File

@ -13,9 +13,9 @@ func init() {
type WhiteList struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId int `gorm:"uniqueIndex:idx_whitelist"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_whitelist;index:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_whitelist;index:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" multi_choices:"true" where:"eq"`
WType string `name:"白名单类型" desc:"账号或者ip" required:"true" choices:"GetWhitelistTypeChoices"`
Value string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账" required:"true"`
Value string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账号或者ip等值" required:"true"`
CreatedAt time.Time `readonly:"true" where:"range"`
}

View File

@ -61,6 +61,19 @@ func (ctl *controller) CommonDelete(ctx *context.WebContext, params *dto.CommonD
return nil
}
func (ctl *controller) CommonRowsSelection(ctx *context.WebContext, params *dto.CommonRowsSelectionReq, rsp *dto.CommonRowsSelectionRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
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)
if err != nil {
return err
}
return nil
}
func (ctl *controller) OnClickCustomButton(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
return nil
}

View File

@ -27,6 +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)
}
projectGroup.Get("/:projectId/items", "获取项目所有道具列表", consts.WebPathPermit_Read, srv.ctl.GetProjectAllItems)

View File

@ -106,7 +106,7 @@ func (svc *Service) CommonDelete(ctx context.Context, projectId int, resourceNam
if err != nil {
return err
}
userId := ctx.Value("user_id").(int)
userName := ctx.Value("user_name").(string)
@ -122,6 +122,27 @@ 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 {
rsp, err := svc.resourceSvc.RowsSelection(projectId, resourceName, param)
if err != nil {
return nil, err
}
userId := ctx.Value("user_id").(int)
userName := ctx.Value("user_name").(string)
event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{
UserId: userId,
UserName: userName,
ProjectId: projectId,
Resource: resourceName,
Method: "删除",
Any: param,
})
return rsp, err
}
func (svc *Service) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo {
return svc.resourceSvc.GetSupportResourcesList(permissions)
}

View File

@ -31,6 +31,8 @@ func (svc *Service) GetRoutes(req *api.GetRoutesReq) (*api.GetRoutesRsp, error)
Desc: v.Desc,
ShowMethods: make([]string, 0),
MethodsPermissionStr: make([]string, 0),
GlobalBtns: v.GlobalBtns,
RowBtns: v.RowBtns,
}
for _, resourceMethod := range v.ShowMethods {

Binary file not shown.

View File

@ -55,3 +55,14 @@ const (
CDKeyType_Global = 0 // 一码通用
CDKeyType_Private = 1 // 一码一用
)
const (
BtnKeyGlobal_Server_DownAll = "server:down:all" // 一键维护
BtnKeyGlobal_Server_UpAll = "server:up:all" // 一键维护结束
)
const (
BtnKeyRow_Server_Down = "server:down"
BtnKeyRow_Server_Up = "server:up"
)

View File

@ -12,4 +12,5 @@ type EventPayload_UserExecute struct {
Method string `json:"method"`
OldData any `json:"old_data"`
NewData any `json:"new_data"`
Any any `json:"any"`
}

View File

@ -0,0 +1,9 @@
package dtimer
/*
分布式定时器用于解决比如多个进程启动后只有一个进程触发定时器事件执行
目标不影响架构复杂性做个能用的出来思路是用mysql做分布式锁多个进程抢占单例执行
*/
type DTimer struct {
}

View File

@ -22,7 +22,7 @@ func ParseUnixTimestamp2LocalTime(ts int64) (local time.Time) {
sec = ts
nsec = 0
default:
panic(fmt.Errorf("ts:%v invalid parse to local time"))
panic(fmt.Errorf("ts:%v invalid parse to local time", ts))
}
t := time.Unix(sec, nsec)

View File

@ -1,5 +1,5 @@
<script setup>
import {ElNotification} from "element-plus";
import {ElNotification, selectGroupKey} from "element-plus";
import {resourceDelete, resourceList, resourcePost, resourcePut, resourceGetAllItems} from "@/api/resource.js";
import {ref, toRaw} from "vue";
import {useRoute} from 'vue-router';
@ -14,6 +14,10 @@ const listDataOK = ref(false)
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 : [];
console.log("global btns:", resource_raw_node)
const resource_url = cachedResource.meta.resource_url;
const fieldsDescInfo = ref([])
@ -139,6 +143,20 @@ const dialogObjectForm = ref({
Attach: [],
})
const tableSelectRows = ref([])
const handleTableSelectRowsSendToServer = (keyType, rows) => {
console.log(`选择表格行,类型:${keyType},`, rows)
}
const handleTableSelectChange = (rows) => {
tableSelectRows.value = rows
}
const tableSelectRows1 = (keyType) => {
handleTableSelectRowsSendToServer(keyType, tableSelectRows.value)
}
const tableSelectRows2 = (keyType, index, row) => {
handleTableSelectRowsSendToServer(keyType, [row])
}
const submitAdd = async () => {
try {
@ -363,14 +381,25 @@ const handlePaginationCurChange = (val) => {
</el-row>
<el-row style="margin-top: 10px">
<el-button @click="dialogAddVisible = true" size="large" type="primary"
<el-button @click="dialogAddVisible = true" size="default" type="primary"
v-if="(resource_raw_node.meta.methods.post === true)">
添加
</el-button>
<el-button v-for="btn in globalClickBtns" size="default" :type="btn.btn_type"
@click="tableSelectRows1(btn.key)">
{{ btn.name }}
</el-button>
</el-row>
</el-header>
<el-main>
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe
@selection-change="handleTableSelectChange">
<el-table-column type="selection" v-if="globalClickBtns.length > 0 || rowClickBtns.length > 0">
</el-table-column>
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-table-column prop="jsonValue" :label="fieldDescInfo.name" show-overflow-tooltip
v-if="(fieldDescInfo.type === 'items')"></el-table-column>
@ -382,18 +411,22 @@ const handlePaginationCurChange = (val) => {
<el-button size="default" type="success" @click="handleEdit( scope.$index, scope.row)"
v-if="(resource_raw_node.meta.methods.put === true)">
<el-icon style="vertical-align: middle">
<Edit/>
</el-icon>
<!-- <el-icon style="vertical-align: middle">-->
<!-- <Edit/>-->
<!-- </el-icon>-->
<span>编辑</span>
</el-button>
<el-button size="default" type="danger" @click="handleDelete( scope.$index, scope.row)"
v-if="(resource_raw_node.meta.methods.delete === true)">
<el-icon style="vertical-align: middle">
<Delete/>
</el-icon>
<!-- <el-icon style="vertical-align: middle">-->
<!-- <Delete/>-->
<!-- </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>
</el-table-column>
</el-table>

View File

@ -85,7 +85,7 @@ const router = createRouter({
export default router
export function setProjectOperationRoutes(projectList) {
// console.log("resourceList:", projectList)
console.log("resourceList:", projectList)
isGetUserInfo.value = true
projectOpTreeRoutes.value = []
@ -119,6 +119,8 @@ export function setProjectOperationRoutes(projectList) {
resource: resource.resource,
resource_url: routePath,
methods: {},
global_click_btns: resource.global_btns,
row_click_btns: resource.row_btns,
},
component: () => import('@/views/project/project_op.vue'),
props: true

View File

@ -25,6 +25,7 @@
- [ ] 公告发送游戏
- [ ] 订单列表从游戏服拉取可以查看
- [ ] 优化header右上角昵称部分显示
- [ ] 角色在线状态
* bug记录