This commit is contained in:
likun 2025-04-24 20:39:31 +08:00
parent f815590b7c
commit 02a0721d68
63 changed files with 1678 additions and 694 deletions

View File

@ -8,9 +8,12 @@ import (
)
func initFun(app *node.Application) error {
svc := service.New(global.GLOB_DB) // 初始化应用服务
srv := server.New(svc) // 初始化http服务
srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由
svc, err := service.New(global.GLOB_DB) // 初始化应用服务
if err != nil {
panic(err)
}
srv := server.New(svc) // 初始化http服务
srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由
return nil
}

View File

@ -0,0 +1,180 @@
package domain
import (
"admin/apps/game/domain/entity"
"admin/apps/game/domain/projects"
"admin/apps/game/domain/repo"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/errcode"
"fmt"
"gorm.io/gorm"
)
type CommonResourceService struct {
projectRepo repo.IProjectRepo
}
func initCommonResourcesRepo(db *gorm.DB) {
commResourcesRepo = []*resourceRepoInfo{
{consts.ResourcesName_Project, "项目管理", repo.NewCommonResourceRepo(db, &model.Project{})},
{consts.ResourcesName_Server, "服务器管理", repo.NewCommonResourceRepo(db, &model.Server{})},
{consts.ResourcesName_WhiteList, "白名单", repo.NewCommonResourceRepo(db, &model.WhiteList{})},
{consts.ResourcesName_Ban, "封禁管理", repo.NewCommonResourceRepo(db, &model.Ban{})},
{consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{})},
{consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{})},
{consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{})},
{consts.ResourcesName_RewardCode, "奖励码", repo.NewCommonResourceRepo(db, &model.RewardCode{})},
{consts.ResourcesName_DevicePush, "设备推送", repo.NewCommonResourceRepo(db, &model.DevicePush{})},
}
}
func NewCommonResourceService(db *gorm.DB) *CommonResourceService {
initCommonResourcesRepo(db)
svc := &CommonResourceService{
projectRepo: repo.NewProjectRepo(db),
}
return svc
}
func (svc *CommonResourceService) List(projectId string, resource string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) {
projectEt, find, err := svc.projectRepo.GetByProjectId(projectId)
if err != nil {
return nil, nil, err
}
if !find {
return nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectId, pageNo, pageLen, extraQuery, args...)
if err != nil {
return nil, nil, err
}
retList := make([]dto.CommonDtoValues, 0, len(etList))
for _, v := range etList {
retList = append(retList, v.ToCommonDto())
}
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpListHook); ok {
return hook.List(projectEt, resource, pageNo, pageLen, fieldsDescInfo, retList, extraQuery, args...)
}
return fieldsDescInfo, retList, nil
}
func (svc *CommonResourceService) GetById(projectId string, resource string, id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) {
fieldsDescInfo, et, find, err := findCommResourceRepo(resource).GetById(projectId, id)
if err != nil {
return nil, nil, nil, false, err
}
return fieldsDescInfo, et.ToCommonDto(), et, find, nil
}
func (svc *CommonResourceService) Create(projectId string, resource string, dtoObj dto.CommonDtoValues) (dto.CommonDtoValues, error) {
projectEt, find, err := svc.projectRepo.GetByProjectId(projectId)
if err != nil {
return nil, err
}
if !find {
return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
et, err := findCommResourceRepo(resource).Create(projectId, dtoObj)
if err != nil {
return nil, err
}
// 返回新的实体插入数据库之后会填入数据库id所以要返给前端刷新id数据
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpCreateHook); ok {
dtoObj := et.ToCommonDto()
err = hook.Create(projectEt, resource, dtoObj)
if err != nil {
return nil, err
}
return dtoObj, nil
}
return et.ToCommonDto(), err
}
func (svc *CommonResourceService) Edit(projectId string, resource string, dtoObj dto.CommonDtoValues) error {
projectEt, find, err := svc.projectRepo.GetByProjectId(projectId)
if err != nil {
return err
}
if !find {
return errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
err = findCommResourceRepo(resource).Edit(projectId, dtoObj)
if err != nil {
return err
}
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpEditHook); ok {
err = hook.Edit(projectEt, resource, dtoObj)
if err != nil {
return err
}
return nil
}
return nil
}
func (svc *CommonResourceService) Delete(projectId string, resource string, id int) error {
projectEt, find, err := svc.projectRepo.GetByProjectId(projectId)
if err != nil {
return err
}
if !find {
return errcode.New(errcode.ServerError, "not found project %v db data", projectId)
}
err = findCommResourceRepo(resource).Delete(projectId, id)
if err != nil {
return err
}
// 执行各个项目特定的钩子方法
if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpDeleteHook); ok {
err = hook.Delete(projectEt, resource, id)
if err != nil {
return err
}
return nil
}
return nil
}
func (svc *CommonResourceService) GetSupportResourcesList() [][2]string {
list := make([][2]string, 0, len(commResourcesRepo))
for _, v := range commResourcesRepo {
list = append(list, [2]string{v.resource, v.desc})
}
return list
}
type resourceRepoInfo struct {
resource string
desc string
repo repo.ICommonResourceRepo
}
var commResourcesRepo = make([]*resourceRepoInfo, 0)
func findCommResourceRepo(resource string) repo.ICommonResourceRepo {
for _, v := range commResourcesRepo {
if v.resource == resource {
return v.repo
}
}
panic(fmt.Errorf("not found resource:%v", resource))
return nil
}

View File

@ -0,0 +1,68 @@
package entity
import (
"admin/apps/game/model"
"admin/apps/game/model/dto"
"reflect"
)
type CommonResource struct {
ProjectEt *Project
Po model.IModel
}
func (et *CommonResource) FromPo(po model.IModel) *CommonResource {
et.Po = po
return et
}
func (et *CommonResource) FromDto(dto dto.CommonDtoValues) *CommonResource {
po := et.Po
//to := reflect.TypeOf(Po)
vo := reflect.ValueOf(po).Elem()
to := reflect.TypeOf(po).Elem()
for k, v := range dto {
fo := vo.FieldByName(k)
ft, find := to.FieldByName(k)
if !find {
continue
}
fo.Set(parseStr2FieldValue(ft, v))
}
return et
}
func (et *CommonResource) ToPo() model.IModel {
return et.Po
}
func (et *CommonResource) ToCommonDto() dto.CommonDtoValues {
return poToCommonDtoNo(et.Po)
}
func (et *CommonResource) GetDtoFieldsDescInfo(projectId string) []*dto.CommonDtoFieldDesc {
ptrVo := reflect.ValueOf(et.Po)
to := reflect.TypeOf(et.Po).Elem()
vo := ptrVo.Elem()
obj := make([]*dto.CommonDtoFieldDesc, 0, to.NumField())
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
//fo := vo.Field(i)
if ft.Name == "ProjectId" {
continue
}
f1 := getFieldTypeDtoDescInfo(projectId, ptrVo, ft)
obj = append(obj, f1)
}
return obj
}

View File

@ -1,94 +1,24 @@
package entity
import (
"admin/apps/game/model"
"admin/apps/game/model/dto"
"math/rand"
"reflect"
)
var ProjectDtoFieldsDescInfo = DefaultProject().GetDtoFieldsDescInfo()
import "admin/apps/game/model"
type Project struct {
Id int
po *model.Project
*CommonResource
ProjectPo *model.Project
}
func DefaultProject() *Project {
func (commEt *CommonResource) ToProjectEntity() *Project {
po := commEt.ToPo().(*model.Project)
return &Project{
po: &model.Project{},
CommonResource: commEt,
ProjectPo: po,
}
}
func FromProjectPo(po *model.Project) *Project {
return &Project{
Id: po.ID,
po: po,
}
func (project *Project) GetProjectPo() *model.Project {
return project.ProjectPo
}
func FromProjectDto(dto dto.CommonDtoValues) *Project {
et := DefaultProject()
po := et.po
//to := reflect.TypeOf(po)
vo := reflect.ValueOf(po).Elem()
to := reflect.TypeOf(po).Elem()
for k, v := range dto {
fo := vo.FieldByName(k)
ft, _ := to.FieldByName(k)
fo.Set(parseStr2FieldValue(ft, v))
}
et.Id = po.ID
return et
}
func (et *Project) ToPo() *model.Project {
return et.po
}
func (et *Project) ToCommonDto() dto.CommonDtoValues {
obj := make(dto.CommonDtoValues)
to := reflect.TypeOf(et.po).Elem()
vo := reflect.ValueOf(et.po).Elem()
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
fo := vo.Field(i)
obj[ft.Name] = fo.Interface()
}
return obj
}
func (et *Project) GetDtoFieldsDescInfo() []*dto.CommonDtoFieldDesc {
to := reflect.TypeOf(et.po).Elem()
vo := reflect.ValueOf(et.po).Elem()
obj := make([]*dto.CommonDtoFieldDesc, 0, to.NumField())
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
//fo := vo.Field(i)
f1 := &dto.CommonDtoFieldDesc{
Name: ft.Name,
Key: ft.Name,
Type: ft.Type.Name(),
HelpText: ft.Tag.Get("desc"),
Editable: true,
Require: rand.Int()%2 == 0,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
}
obj = append(obj, f1)
}
return obj
func (project *Project) GetApiAddr() string {
return project.ProjectPo.ApiAddr
}

View File

@ -1,91 +0,0 @@
package entity
import (
"admin/apps/game/model"
"admin/apps/game/model/dto"
"reflect"
)
var ServerDtoFieldsDescInfo = DefaultServer().GetDtoFieldsDescInfo()
type Server struct {
Id int
po *model.Server
}
func DefaultServer() *Server {
return &Server{
po: &model.Server{},
}
}
func FromServerPo(po *model.Server) *Server {
return &Server{
Id: po.ID,
po: po,
}
}
func FromServerDto(dto dto.CommonDtoValues) *Server {
et := DefaultServer()
po := et.po
//to := reflect.TypeOf(po)
vo := reflect.ValueOf(po)
for k, v := range dto {
fo := vo.FieldByName(k)
fo.Set(reflect.ValueOf(v))
}
et.Id = po.ID
return et
}
func (et *Server) ToPo() *model.Server {
return et.po
}
func (et *Server) ToCommonDto() dto.CommonDtoValues {
obj := make(dto.CommonDtoValues)
to := reflect.TypeOf(et.po).Elem()
vo := reflect.ValueOf(et.po).Elem()
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
fo := vo.Field(i)
obj[ft.Name] = fo.Interface()
}
return obj
}
func (et *Server) GetDtoFieldsDescInfo() []*dto.CommonDtoFieldDesc {
to := reflect.TypeOf(et.po).Elem()
vo := reflect.ValueOf(et.po).Elem()
obj := make([]*dto.CommonDtoFieldDesc, 0, to.NumField())
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
//fo := vo.Field(i)
f1 := &dto.CommonDtoFieldDesc{
Name: ft.Name,
Key: ft.Name,
Type: ft.Type.Name(),
HelpText: ft.Tag.Get("desc"),
Editable: true,
Require: true,
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: false,
}
obj = append(obj, f1)
}
return obj
}

View File

@ -1,6 +1,9 @@
package entity
import (
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/lib/xlog"
"fmt"
"gorm.io/gorm"
"reflect"
@ -8,6 +11,62 @@ import (
"time"
)
func poToCommonDtoNo(po any) dto.CommonDtoValues {
obj := make(dto.CommonDtoValues)
to := reflect.TypeOf(po).Elem()
vo := reflect.ValueOf(po).Elem()
for i := 0; i < vo.NumField(); i++ {
ft := to.Field(i)
fo := vo.Field(i)
if ft.Name == "ProjectId" {
if _, ok := po.(*model.Project); !ok {
continue
}
}
obj[ft.Name] = fo.Interface()
}
return obj
}
func getFieldTypeDtoDescInfo(projectId string, poValue reflect.Value, fieldType reflect.StructField) *dto.CommonDtoFieldDesc {
name := fieldType.Tag.Get("name")
if name == "" {
name = fieldType.Name
}
f1 := &dto.CommonDtoFieldDesc{
Name: name,
Key: fieldType.Name,
Type: fieldType.Type.Name(),
HelpText: fieldType.Tag.Get("desc"),
Readonly: fieldType.Tag.Get("readonly") == "true",
Required: fieldType.Tag.Get("required") == "true",
Choices: make([]*dto.CommonDtoFieldChoice, 0),
MultiChoice: fieldType.Tag.Get("multi_choice") == "true",
}
if tagType := fieldType.Tag.Get("type"); tagType != "" {
f1.Type = tagType
}
cf := fieldType.Tag.Get("choices")
if cf != "" {
method := poValue.MethodByName(cf)
if !method.IsValid() {
xlog.Warnf("po %v choices %v function not found in model", poValue.Type().Name(), cf)
} else {
rets := method.Call([]reflect.Value{reflect.ValueOf(projectId)})
f1.Choices = rets[0].Interface().([]*dto.CommonDtoFieldChoice)
}
}
return f1
}
func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue reflect.Value) {
setValue := fmt.Sprintf("%v", rawValue)
var parsedValue any
@ -61,6 +120,37 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue
return reflect.ValueOf(gorm.DeletedAt{})
}
fallthrough
case reflect.Slice:
typeName := field.Type.String()
if typeName == "[]string" {
list := make([]string, 0)
if _, ok := rawValue.(string); ok {
// 空的字符串
} else {
for _, v := range rawValue.([]interface{}) {
list = append(list, v.(string))
}
}
parsedValue = list
} else if typeName == "[]*model.MailAttachItem" {
items := make([]*model.MailAttachItem, 0)
for _, itemI := range rawValue.([]interface{}) {
item := &model.MailAttachItem{}
for k, vI := range itemI.(map[string]any) {
v := vI.(float64)
if k == "id" {
item.ID = int32(v)
} else if k == "num" {
item.Num = int64(v)
} else if k == "item_type" {
item.ItemType = int(v)
}
}
items = append(items, item)
}
parsedValue = items
}
default:
panic(fmt.Errorf("暂时不支持的前后端交互字段类型:%v, 类型名:%v", field.Type.Kind(), field.Type.Name()))
}

View File

@ -0,0 +1,19 @@
package domain
import (
"admin/apps/game/model/dto"
)
type IRestfulEntity interface {
ToCommonDto() dto.CommonDtoValues
}
type IRestfulResourceSvc interface {
List(pageNo, pageLen int, whereValues ...string) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error)
Post(obj dto.CommonDtoValues) (IRestfulEntity, error)
Put(obj dto.CommonDtoValues) (IRestfulEntity, error)
Delete(id int) error
}
type IResourceOpPostHook interface {
}

View File

@ -1,31 +0,0 @@
package domain
import (
"admin/apps/game/model/dto"
"admin/internal/errcode"
)
type IRestfulEntity interface {
ToCommonDto() dto.CommonDtoValues
}
type IRestfulResourceSvc interface {
List(pageNo, pageLen int) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error)
Post(obj dto.CommonDtoValues) (IRestfulEntity, error)
Put(obj dto.CommonDtoValues) (IRestfulEntity, error)
Delete(id int) error
}
var restfulResourceSvcMgr = make(map[string]IRestfulResourceSvc)
func registerRestfulSvc(name string, svc IRestfulResourceSvc) {
restfulResourceSvcMgr[name] = svc
}
func FindRestfulResourceSvc(name string) (IRestfulResourceSvc, error) {
svc, find := restfulResourceSvcMgr[name]
if !find {
return nil, errcode.New(errcode.ServerError, "not found %v restful svc", name)
}
return svc, nil
}

View File

@ -1,48 +1,65 @@
package domain
import (
"admin/apps/game/domain/entity"
"admin/apps/game/domain/projects"
"admin/apps/game/domain/repo"
"admin/apps/game/model/dto"
"admin/apps/game/model"
"admin/lib/xlog"
"gorm.io/gorm"
"time"
)
type ProjectSvc struct {
proRepo repo.IProjectRepo
type ProjectService struct {
repo repo.IProjectRepo
}
func NewProjectSvc(db *gorm.DB) *ProjectSvc {
svc := &ProjectSvc{
proRepo: repo.NewProjectRepo(db),
}
registerRestfulSvc("project", svc)
return svc
func NewProjectService(db *gorm.DB) *ProjectService {
return &ProjectService{repo: repo.NewProjectRepo(db)}
}
func (svc *ProjectSvc) List(pageNo, pageLen int) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error) {
entityList, err := svc.proRepo.List(pageNo, pageLen)
func (svc *ProjectService) EnsureProjectsDBData() error {
_, err := svc.repo.EnsureProjectsDBData()
go svc.startProjectBackGroundWorker()
return err
}
func (svc *ProjectService) GetProjectInvokeApiAddr(projectId string, serverIds []int) ([]string, error) {
et, _, err := svc.repo.GetByProjectId(projectId)
if err != nil {
return nil, nil, err
return nil, err
}
iList := make([]IRestfulEntity, 0, len(entityList))
for _, v := range entityList {
iList = append(iList, v)
return []string{et.ToPo().(*model.Project).ApiAddr}, nil
}
func (svc *ProjectService) startProjectBackGroundWorker() {
for {
allProjects, err := svc.repo.List()
if err != nil {
xlog.Warnf("list all projects error:%v", err)
time.Sleep(time.Second * 60)
continue
}
for _, p := range allProjects {
apiAddr := p.GetApiAddr()
if apiAddr == "" {
continue
}
po := p.ToPo().(*model.Project)
handler := projects.GetProjectValueChoicesGetHook(po.ProjectId)
if handler != nil {
itemsChoices, err := handler.GetItems(p)
if err != nil {
xlog.Warnf("get project %v items error:%v", po.ProjectId, err)
} else {
if len(itemsChoices) > 100 {
itemsChoices = itemsChoices[:100]
}
model.ProjectsAllItems[po.ProjectId] = itemsChoices
}
}
}
time.Sleep(time.Second * 60)
}
return entity.ProjectDtoFieldsDescInfo, iList, nil
}
func (svc *ProjectSvc) Post(obj dto.CommonDtoValues) (IRestfulEntity, error) {
et := entity.FromProjectDto(obj)
err := svc.proRepo.Create(et)
return et, err
}
func (svc *ProjectSvc) Put(obj dto.CommonDtoValues) (IRestfulEntity, error) {
et := entity.FromProjectDto(obj)
err := svc.proRepo.Edit(et)
return et, err
}
func (svc *ProjectSvc) Delete(id int) error {
return svc.proRepo.Delete(id)
}

View File

@ -0,0 +1,28 @@
package projects
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model/dto"
)
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)
}
type IPostResourceOpCreateHook interface {
Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error
}
type IPostResourceOpEditHook interface {
Edit(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error
}
type IPostResourceOpDeleteHook interface {
Delete(projectInfo *entity.Project, resource string, id int) error
}
type IGetAllValueChoicesHook interface {
// 获取所有道具,可以用于前端页面做下拉选择等
GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error)
}

View File

@ -0,0 +1,30 @@
package projects
import (
"admin/apps/game/domain/projects/smdl"
"admin/internal/consts"
)
// 注册各个项目所有gm资源操作后的回调例如后台添加了白名单可以在回调里加上通知项目内的服务
var projectsResourceHookMgr = map[string]map[string]any{
consts.RegisteredProjectId_shenmodalu: map[string]any{
consts.ResourcesName_Server: &smdl.ServerHook{}, // 查看了数据库所有数据之后还要连alisrv获取所有进程运行情况
},
}
// 注册各个项目所有增删改字段可选项的钩子
var projectsValueChoicesGetHook = map[string]IGetAllValueChoicesHook{
consts.RegisteredProjectId_shenmodalu: &smdl.Items{},
}
func GetProjectResourceHook(projectId, resource string) any {
project, find := projectsResourceHookMgr[projectId]
if !find {
return nil
}
return project[resource]
}
func GetProjectValueChoicesGetHook(projectId string) IGetAllValueChoicesHook {
return projectsValueChoicesGetHook[projectId]
}

View File

@ -0,0 +1,32 @@
package smdl
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/lib/httpclient"
)
type Items struct {
}
func (items *Items) GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error) {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name)
}
type RspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
List []*dto.CommonDtoFieldChoice `json:"list"`
} `json:"data"`
}
rsp := &RspData{}
err := httpclient.Request(alisrvAddr+"/items", "get", nil, rsp)
if err != nil {
return nil, err
}
return rsp.Data.List, nil
}

View File

@ -0,0 +1,69 @@
package smdl
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"admin/lib/httpclient"
)
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) {
alisrvAddr := projectInfo.GetApiAddr()
if alisrvAddr == "" {
return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name)
}
fields = append(fields, &dto.CommonDtoFieldDesc{
Name: "进程运行状态",
Key: "RunningStatus",
Type: "string",
HelpText: "进程运行状态:未知、运行中、停止",
Readonly: false,
Required: false,
Choices: nil,
MultiChoice: false,
})
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)
if err != nil {
return nil, nil, err
}
for _, row := range rows {
findRunning := false
for _, curRunning := range rsp.Data.List {
et := (&entity.CommonResource{}).FromPo(&model.Server{}).FromDto(row)
if et.ToPo().(*model.Server).ServerConfID == curRunning.ServerId {
// 运行中,给描述信息添加运行信息
findRunning = true
break
}
}
if findRunning {
row["RunningStatus"] = "运行中"
} else {
row["RunningStatus"] = "未知"
}
}
return fields, rows, nil
}

View File

@ -0,0 +1,102 @@
package repo
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/apps/game/model/dto"
"admin/internal/errcode"
"errors"
"gorm.io/gorm"
"reflect"
)
type ICommonResourceRepo interface {
List(projectId string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error)
GetById(projectId string, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error)
Create(projectId string, et dto.CommonDtoValues) (*entity.CommonResource, error)
Edit(projectId string, et dto.CommonDtoValues) error
Delete(projectId string, id int) error
}
func NewCommonResourceRepo(db *gorm.DB, poTemplate model.IModel) ICommonResourceRepo {
return newCommonResourceRepoImpl(db, poTemplate)
}
type commonResourceRepoImpl struct {
db *gorm.DB
poTemplate model.IModel
fieldsDescInfoFun func(projectId string) []*dto.CommonDtoFieldDesc
}
func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonResourceRepoImpl {
fieldsInfo := (&entity.CommonResource{}).FromPo(poTemplate).GetDtoFieldsDescInfo
return &commonResourceRepoImpl{db: db, poTemplate: poTemplate, fieldsDescInfoFun: fieldsInfo}
}
func (repo *commonResourceRepoImpl) List(projectId string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) {
listType := reflect.New(reflect.SliceOf(reflect.TypeOf(repo.poTemplate)))
var err error
if extraQuery == "" {
err = repo.db.Find(listType.Interface()).Error
} else {
err = repo.db.Where(extraQuery, args...).Find(listType.Interface()).Error
}
if err != nil {
return nil, nil, errcode.New(errcode.DBError, "list resource %v error:%v", repo.poTemplate.TableName(), err)
}
listType1 := listType.Elem()
listLen := listType1.Len()
entityList := make([]*entity.CommonResource, 0, listLen)
for i := 0; i < listType1.Len(); i++ {
po := listType1.Index(i).Interface().(model.IModel)
et := &entity.CommonResource{}
et.FromPo(po)
entityList = append(entityList, et)
}
return repo.fieldsDescInfoFun(projectId), entityList, nil
}
func (repo *commonResourceRepoImpl) GetById(projectId string, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) {
po := repo.newEmptyPo()
err := repo.db.Where("id = ?", id).Find(po).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return repo.fieldsDescInfoFun(projectId), (&entity.CommonResource{}).FromPo(repo.newEmptyPo()), false, nil
}
return nil, nil, false, errcode.New(errcode.DBError, "get resource:%v by id:%v error:%v", repo.poTemplate.TableName(), id, err)
}
return repo.fieldsDescInfoFun(projectId), (&entity.CommonResource{}).FromPo(po), true, nil
}
func (repo *commonResourceRepoImpl) Create(projectId string, dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) {
et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj)
err := repo.db.Create(et.Po).Error
if err != nil {
return et, errcode.New(errcode.DBError, "create resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), et, err)
}
return et, nil
}
func (repo *commonResourceRepoImpl) Edit(projectId string, dtoObj dto.CommonDtoValues) error {
et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj)
err := repo.db.Where("id=?", et.Po.GetId()).Updates(et.Po).Error
if err != nil {
return errcode.New(errcode.DBError, "edit resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), et, err)
}
return nil
}
func (repo *commonResourceRepoImpl) Delete(projectId string, id int) error {
err := repo.db.Where("id=?", id).Unscoped().Delete(repo.poTemplate).Error
if err != nil {
return errcode.New(errcode.DBError, "delete resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), id, err)
}
return nil
}
func (repo *commonResourceRepoImpl) newEmptyPo() model.IModel {
return reflect.New(reflect.TypeOf(repo.poTemplate).Elem()).Interface().(model.IModel)
}

View File

@ -3,75 +3,79 @@ package repo
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/internal/consts"
"admin/internal/errcode"
"errors"
"gorm.io/gorm"
)
type IProjectRepo interface {
List(pageNo, pageLen int) ([]*entity.Project, error)
Create(et *entity.Project) error
Edit(et *entity.Project) error
Delete(id int) error
EnsureProjectsDBData() ([]*model.Project, error)
List() ([]*entity.Project, error)
GetByProjectId(id string) (*entity.Project, bool, error)
}
func NewProjectRepo(db *gorm.DB) IProjectRepo {
return newProjectRepoImpl(db)
model.GetProjectServersHandler = func(projectId string) ([]*model.Server, error) {
servers := make([]*model.Server, 0)
err := db.Where("project_id=?", projectId).Find(&servers).Error
return servers, err
}
return &projectRepoImpl{db: db}
}
type projectRepoImpl struct {
db *gorm.DB
}
func newProjectRepoImpl(db *gorm.DB) *projectRepoImpl {
return &projectRepoImpl{db: db}
func (impl *projectRepoImpl) EnsureProjectsDBData() ([]*model.Project, error) {
var curProjects []*model.Project
err := impl.db.Find(&curProjects).Error
if err != nil {
return nil, errcode.New(errcode.DBError, "find all projects error:%v", err)
}
for k, v := range consts.RegisteredProjects {
find := false
for _, cur := range curProjects {
if k == cur.ProjectId {
find = true
break
}
}
if !find {
po := &model.Project{ProjectId: k, Name: v.Name}
err := impl.db.Create(po).Error
if err != nil {
return nil, errcode.New(errcode.DBError, "create project:%v error:%v", k, err)
}
curProjects = append(curProjects, po)
}
}
return curProjects, nil
}
func (repo *projectRepoImpl) List(pageNo, pageLen int) ([]*entity.Project, error) {
func (impl *projectRepoImpl) List() ([]*entity.Project, error) {
list := make([]*model.Project, 0)
err := repo.db.Find(&list).Error
err := impl.db.Find(&list).Error
if err != nil {
return nil, errcode.New(errcode.DBError, "find project error:%v", err)
return nil, errcode.New(errcode.DBError, "list project error:%v", err)
}
// debug
//list = append(list, &model.Project{
// ID: 123,
// Name: "神魔大陆",
// Desc: "神魔大陆服务器",
// ApiAddr: "http://192.168.1.1:8081",
//})
entityList := make([]*entity.Project, 0, len(list))
for _, Project := range list {
entityList = append(entityList, entity.FromProjectPo(Project))
list1 := make([]*entity.Project, 0, len(list))
for _, v := range list {
list1 = append(list1, (&entity.CommonResource{}).FromPo(v).ToProjectEntity())
}
return entityList, nil
return list1, nil
}
func (repo *projectRepoImpl) Create(et *entity.Project) error {
po := et.ToPo()
err := repo.db.Create(po).Error
func (impl *projectRepoImpl) GetByProjectId(id string) (*entity.Project, bool, error) {
po := &model.Project{}
err := impl.db.Where("project_id=?", id).Find(po).Error
if err != nil {
return errcode.New(errcode.DBError, "create obj:%+v error:%v", et, err)
if errors.Is(err, gorm.ErrRecordNotFound) {
return (&entity.CommonResource{}).FromPo(po).ToProjectEntity(), false, nil
}
}
et.Id = po.ID
return nil
}
func (repo *projectRepoImpl) Edit(et *entity.Project) error {
po := et.ToPo()
err := repo.db.Where("id=?", et.Id).Updates(po).Error
if err != nil {
return errcode.New(errcode.DBError, "edit obj:%+v error:%v", et, err)
}
return nil
}
func (repo *projectRepoImpl) Delete(id int) error {
err := repo.db.Where("id=?", id).Unscoped().Delete(&model.Project{}).Error
if err != nil {
return errcode.New(errcode.DBError, "delete obj:%+v error:%v", id, err)
}
return nil
return (&entity.CommonResource{}).FromPo(po).ToProjectEntity(), true, nil
}

View File

@ -1,69 +0,0 @@
package repo
import (
"admin/apps/game/domain/entity"
"admin/apps/game/model"
"admin/internal/errcode"
"gorm.io/gorm"
)
type IServerRepo interface {
List(pageNo, pageLen int) ([]*entity.Server, error)
Create(et *entity.Server) error
Edit(et *entity.Server) error
Delete(id int) error
}
func NewServerRepo(db *gorm.DB) IServerRepo {
return newServerRepoImpl(db)
}
type ServerRepoImpl struct {
db *gorm.DB
}
func newServerRepoImpl(db *gorm.DB) *ServerRepoImpl {
return &ServerRepoImpl{db: db}
}
func (repo *ServerRepoImpl) List(pageNo, pageLen int) ([]*entity.Server, error) {
list := make([]*model.Server, 0)
err := repo.db.Find(&list).Error
if err != nil {
return nil, errcode.New(errcode.DBError, "find Server error:%v", err)
}
entityList := make([]*entity.Server, 0, len(list))
for _, Server := range list {
entityList = append(entityList, entity.FromServerPo(Server))
}
return entityList, nil
}
func (repo *ServerRepoImpl) Create(et *entity.Server) error {
po := et.ToPo()
err := repo.db.Create(po).Error
if err != nil {
return errcode.New(errcode.DBError, "create obj:%+v error:%v", et, err)
}
et.Id = po.ID
return nil
}
func (repo *ServerRepoImpl) Edit(et *entity.Server) error {
po := et.ToPo()
err := repo.db.Where("id=?", et.Id).Updates(po).Error
if err != nil {
return errcode.New(errcode.DBError, "edit obj:%+v error:%v", et, err)
}
return nil
}
func (repo *ServerRepoImpl) Delete(id int) error {
err := repo.db.Where("id=?", id).Unscoped().Delete(&model.Server{}).Error
if err != nil {
return errcode.New(errcode.DBError, "delete obj:%+v error:%v", id, err)
}
return nil
}

View File

@ -1,48 +0,0 @@
package domain
import (
"admin/apps/game/domain/entity"
"admin/apps/game/domain/repo"
"admin/apps/game/model/dto"
"gorm.io/gorm"
)
type ServerSvc struct {
serverRepo repo.IServerRepo
}
func NewServerSvc(db *gorm.DB) *ServerSvc {
svc := &ServerSvc{
serverRepo: repo.NewServerRepo(db),
}
registerRestfulSvc("server", svc)
return svc
}
func (svc *ServerSvc) List(pageNo, pageLen int) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error) {
entityList, err := svc.serverRepo.List(pageNo, pageLen)
if err != nil {
return nil, nil, err
}
iList := make([]IRestfulEntity, 0, len(entityList))
for _, v := range entityList {
iList = append(iList, v)
}
return entity.ServerDtoFieldsDescInfo, iList, nil
}
func (svc *ServerSvc) Post(obj dto.CommonDtoValues) (IRestfulEntity, error) {
et := entity.FromServerDto(obj)
err := svc.serverRepo.Create(et)
return et, err
}
func (svc *ServerSvc) Put(obj dto.CommonDtoValues) (IRestfulEntity, error) {
et := entity.FromServerDto(obj)
err := svc.serverRepo.Edit(et)
return et, err
}
func (svc *ServerSvc) Delete(id int) error {
return svc.serverRepo.Delete(id)
}

View File

@ -0,0 +1,38 @@
package model
import (
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(Ban{})
}
type Ban struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string `gorm:"type:varchar(255);uniqueIndex:idx_ban"`
BanType string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" multi_choice:"true"`
Value string `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁账号" required:"true" desc:"填账号"`
ExpireSeconds int `name:"封禁秒数" desc:"封禁到期秒数为0表示永久封禁"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *Ban) TableName() string {
return "ban"
}
func (m *Ban) GetId() int {
return m.ID
}
func (m *Ban) GetBanTypeChoices() []string {
return []string{
"作弊",
"广告",
}
}

View File

@ -0,0 +1,31 @@
package model
import (
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(DevicePush{})
}
type DevicePush struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string
Account string `name:"账号" desc:"给账号所属的设备做推送"`
Title string `name:"通知标题" desc:""`
Content string `name:"通知内容"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *DevicePush) TableName() string {
return "device_push"
}
func (m *DevicePush) GetId() int {
return m.ID
}

View File

@ -1,5 +1,11 @@
package dto
type WebRspData struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
type CommonDtoFieldChoice struct {
Desc string `json:"desc"`
Value any `json:"value"`
@ -14,8 +20,8 @@ type CommonDtoFieldDesc struct {
// 支持自定义类型和自定义类型的数组
Type string `json:"type"`
HelpText string `json:"help_text"`
Editable bool `json:"editable"` // 是否可编辑例如id就不可编辑新增时也不需要填写
Require bool `json:"require"` // 是否必填,不能为空
Readonly bool `json:"readonly"` // 是否只读,就只展示在表格中
Required bool `json:"required"` // 是否必填,不能为空
Choices []*CommonDtoFieldChoice `json:"choices"` // 可选项,用于字段做下拉框
MultiChoice bool `json:"multi_choice"` // 是否多选
}
@ -36,3 +42,9 @@ type PathInfo struct {
Path string `json:"path"`
Method string `json:"method"`
}
type ResourceInfo struct {
Resource string `json:"resource"`
Desc string `json:"desc"`
Methods []string `json:"methods"` // get|post|put|delete // 资源支持的方法,某些功能比如白名单,只有增删
}

View File

@ -6,8 +6,11 @@ type NilReq struct {
type NilRsp = NilReq
type CommonListReq struct {
PageNo int `json:"page_no"`
PageLen int `json:"page_len"`
PageNo int `json:"page_no"`
PageLen int `json:"page_len"`
WhereValue1 string `json:"where_value1"`
WhereValue2 string `json:"where_value2"`
WhereValue3 string `json:"where_value3"`
}
type CommonPostReq struct {
@ -43,3 +46,7 @@ type CommandListReq struct {
type CommandListRsp struct {
List []*PathInfo `json:"list"`
}
type ResourceListRsp struct {
List []*ResourceInfo `json:"list"`
}

View File

@ -0,0 +1,43 @@
package model
import (
"admin/apps/game/model/dto"
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(GlobalMail{})
}
var ProjectsAllItems = make(map[string][]*dto.CommonDtoFieldChoice)
type GlobalMail struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string
ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" type:"[]string" choices:"GetChoiceServers" multi_choice:"true"`
Title string `name:"邮件标题" required:"true"`
Content string `name:"邮件内容" required:"true"`
Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *GlobalMail) TableName() string {
return "mail_global"
}
func (m *GlobalMail) GetId() int {
return m.ID
}
func (m *GlobalMail) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice {
return getChoiceServers(projectId)
}
func (m *GlobalMail) GetChoiceItems(projectId string) []*dto.CommonDtoFieldChoice {
return ProjectsAllItems[projectId]
}

View File

@ -0,0 +1,28 @@
package model
import (
"admin/apps/game/model/dto"
)
type IModel interface {
TableName() string
GetId() int
}
var GetProjectServersHandler func(projectId string) ([]*Server, error)
func getChoiceServers(args ...any) []*dto.CommonDtoFieldChoice {
servers, err := GetProjectServersHandler(args[0].(string))
if err != nil {
panic(err)
}
serverChoices := make([]*dto.CommonDtoFieldChoice, 0, len(servers))
for _, s := range servers {
serverChoices = append(serverChoices, &dto.CommonDtoFieldChoice{
Desc: s.Desc,
Value: s.ServerConfID,
})
}
return serverChoices
}

View File

@ -0,0 +1,37 @@
package model
import (
"admin/apps/game/model/dto"
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(Notice{})
}
type Notice struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string
ServerIDs []int `gorm:"type:json;serializer:json" name:"公告生效服务器" desc:"为空表示所有服" choices:"GetChoiceServers"`
Content string `name:"公告内容" required:"true"`
StartAt int64 `name:"开始时间" required:"true"`
EndAt int64 `name:"结束时间" required:"true"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *Notice) TableName() string {
return "notice"
}
func (m *Notice) GetId() int {
return m.ID
}
func (m *Notice) GetChoiceServers(args ...any) []*dto.CommonDtoFieldChoice {
return getChoiceServers(args[0].(string))
}

View File

@ -12,17 +12,26 @@ func init() {
// Project 游戏项目,例如神谕、神魔大陆
type Project struct {
ID int `gorm:"primarykey"`
Name string `gorm:"primarykey"`
Desc string
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string `gorm:"type:varchar(255);unique" name:"项目id" readonly:"true"`
Name string `gorm:"primarykey" required:"true" name:"项目名" readonly:"true"`
Desc string `name:"项目描述"`
// command_list接口服务器地址为空代表由由项目下各个逻辑服提供command_list.
// 取决于每个项目改造难度:
// 不为空就代表项目要实现一个自己统一对外暴露的gm调用服务对内聚合、分发指令执行本后台执行指令只调用一次
// 为空就代表command_list实现在各个逻辑服由本后台系统在执行指令时聚合、分发指令
// 调用各个逻辑服执行,本后台执行指令需要根据逻辑服数量调用;
ApiAddr string //
ApiAddr string `name:"游戏api地址" desc:"api服务器地址例如神魔大陆就是alisrv服务器地址用于后台调用gm"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *Project) TableName() string {
return "project"
}
func (m *Project) GetId() int {
return m.ID
}

View File

@ -0,0 +1,30 @@
package model
import (
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(RewardCode{})
}
type RewardCode struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string
Group int `name:"奖励码组"`
Code int `name:"奖励码"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *RewardCode) TableName() string {
return "reward_code"
}
func (m *RewardCode) GetId() int {
return m.ID
}

View File

@ -0,0 +1,48 @@
package model
import (
"admin/apps/game/model/dto"
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(RoleMail{})
}
type MailAttachItem struct {
ID int32 `json:"id"`
Num int64 `json:"num"`
ItemType int `json:"item_type"`
}
type RoleMail struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string
RoleIDs []string `gorm:"type:json;serializer:json" name:"生效的角色id" required:"true"`
ServerID string `name:"所属区服" choices:"GetChoiceServers" multi_choice:"false"`
Title string `name:"邮件标题" required:"true"`
Content string `name:"邮件内容" required:"true"`
Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *RoleMail) TableName() string {
return "mail_role"
}
func (m *RoleMail) GetId() int {
return m.ID
}
func (m *RoleMail) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice {
return getChoiceServers(projectId)
}
func (m *RoleMail) GetChoiceItems(projectId string) []*dto.CommonDtoFieldChoice {
return ProjectsAllItems[projectId]
}

View File

@ -12,8 +12,9 @@ func init() {
// Server 逻辑服
type Server struct {
ID int `gorm:"primarykey"`
ServerConfID string `gorm:"primarykey"`
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string `gorm:"type:varchar(200);uniqueIndex:idx_server"`
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" required:"true"`
Desc string
// command_list接口服务器地址为空代表由由项目统一提供command_list.
// 取决于每个项目改造难度:
@ -22,7 +23,15 @@ type Server struct {
// 调用各个逻辑服执行,本后台执行指令需要根据逻辑服数量调用;
ApiAddr string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *Server) TableName() string {
return "server"
}
func (m *Server) GetId() int {
return m.ID
}

View File

@ -0,0 +1,31 @@
package model
import (
"admin/internal/db"
"gorm.io/gorm"
"time"
)
func init() {
db.RegisterTableModels(WhiteList{})
}
type WhiteList struct {
ID int `gorm:"primarykey" readonly:"true"`
ProjectId string `gorm:"type:varchar(256);uniqueIndex:idx_whitelist"`
Account string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist"`
AccountType int `gorm:"uniqueIndex:idx_whitelist"`
Desc string
CreatedAt time.Time `readonly:"true"`
UpdatedAt time.Time `readonly:"true"`
DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
}
func (lm *WhiteList) TableName() string {
return "whitelist"
}
func (m *WhiteList) GetId() int {
return m.ID
}

View File

@ -5,8 +5,9 @@ import (
"admin/internal/context"
)
func (ctl *controller) CommonList(ctx *context.WebContext, restfulResourceName string, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
list, err := ctl.svc.CommonList(ctx, restfulResourceName, params)
func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
list, err := ctl.svc.CommonList(ctx, projectId, resource, params)
if err != nil {
return err
}
@ -14,8 +15,9 @@ func (ctl *controller) CommonList(ctx *context.WebContext, restfulResourceName s
return nil
}
func (ctl *controller) CommonPost(ctx *context.WebContext, restfulResourceName string, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
newObj, err := ctl.svc.CommonPost(ctx, restfulResourceName, *params.Dto)
func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
newObj, err := ctl.svc.CommonPost(ctx, projectId, resource, *params.Dto)
if err != nil {
return err
}
@ -23,17 +25,19 @@ func (ctl *controller) CommonPost(ctx *context.WebContext, restfulResourceName s
return nil
}
func (ctl *controller) CommonPut(ctx *context.WebContext, restfulResourceName string, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
newObj, err := ctl.svc.CommonPut(ctx, restfulResourceName, *params.Dto)
func (ctl *controller) CommonPut(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
err := ctl.svc.CommonPut(ctx, projectId, resource, *params.Dto)
if err != nil {
return err
}
rsp.Dto = &newObj
rsp.Dto = params.Dto
return nil
}
func (ctl *controller) CommonDelete(ctx *context.WebContext, restfulResourceName string, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
err := ctl.svc.CommonDelete(ctx, restfulResourceName, params.Id)
func (ctl *controller) CommonDelete(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
projectId, resource := getCtxURIProjectIdAndResource(ctx)
err := ctl.svc.CommonDelete(ctx, projectId, resource, params.Id)
if err != nil {
return err
}

View File

@ -2,9 +2,11 @@ package server
import (
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/context"
"admin/lib/httpclient"
"admin/lib/xlog"
"strings"
)
func (ctl *controller) CommandList(ctx *context.WebContext, params *dto.CommandListReq, rsp *dto.CommandListRsp) error {
@ -26,6 +28,67 @@ func (ctl *controller) CommandList(ctx *context.WebContext, params *dto.CommandL
return nil
}
func (ctl *controller) ProjectResourceList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
projectId := getCtxURIProjectId(ctx)
resource := getCtxURIResource(ctx)
apiAddr, err := ctl.svc.GetProjectInvokeApiAddr(projectId, nil)
if err != nil {
return err
}
url := apiAddr[0] + "/api/" + resource
listRsp := make(map[string]any)
err = httpclient.Request(url, "get", nil, &listRsp)
if err != nil {
return err
}
xlog.Debugf("receive project %v resource %v list", "projectId", resource)
return nil
}
func (ctl *controller) ProjectResourcePost(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
return nil
}
func (ctl *controller) ProjectResourcePut(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
return nil
}
func (ctl *controller) ProjectResourceDelete(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
return nil
}
func (ctl *controller) getProjectResourceCommandApiAddr(ctx *context.WebContext) ([]string, error) {
projectId := getCtxURIProjectId(ctx)
//resouce := getCtxURIResource(ctx)
return ctl.svc.GetProjectInvokeApiAddr(projectId, nil)
}
func getCtxURIProjectIdAndResource(ctx *context.WebContext) (string, string) {
return getCtxURIProjectId(ctx), getCtxURIResource(ctx)
}
func getCtxURIResource(ctx *context.WebContext) string {
resource := ctx.GinCtx().Param("resource")
if resource == "" {
if strings.HasPrefix(ctx.GinCtx().Request.RequestURI, "/api/"+consts.ResourcesName_Project) {
return consts.ResourcesName_Project
}
}
return resource
}
func getCtxURIProjectId(ctx *context.WebContext) string {
projectId := ctx.GinCtx().Param("projectId")
return projectId
}
func (ctl *controller) apiRequest() {
}

View File

@ -2,58 +2,41 @@ package server
import (
"admin/apps/game/model/dto"
"admin/internal/consts"
"admin/internal/context"
"admin/internal/global"
"admin/lib/web"
)
func (srv *Server) Route(engine *web.Engine) {
apiGroup := engine.Group("/api", "")
srv.proRoute(apiGroup)
srv.serverRoute(apiGroup)
{
apiGroup.Get("/project/commandlist", "调用对应游戏服api获取命令列表", global.WebPathPermit_Read, srv.ctl.CommandList)
// 注册项目增删改查接口
projectGroup := apiGroup.Group("/"+consts.ResourcesName_Project, "项目")
projectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList)
projectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost)
projectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut)
projectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete)
// 注册项目之下其它所有资源通用增删改查接口
{
resourceUnderProjectGroup := projectGroup.Group("/:projectId/:resource", "")
resourceUnderProjectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList)
resourceUnderProjectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost)
resourceUnderProjectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut)
resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete)
}
projectGroup.Get("/resourcelist", "获取支持的资源列表,用于客户端生成前端操作菜单", consts.WebPathPermit_Read, srv.getResourceList)
}
}
func (srv *Server) proRoute(engine *web.RoutesGroup) {
resourceName := "project"
srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, ""))
}
func (srv *Server) serverRoute(engine *web.RoutesGroup) {
resourceName := "server"
srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, ""))
}
func (srv *Server) registerResourceRouter(resourceName string, group *web.RoutesGroup) {
group.Get("", "获取列表", global.WebPathPermit_Read, commonHandlerList(srv.ctl, resourceName))
group.Post("", "新增", global.WebPathPermit_Read, commonHandlerPost(srv.ctl, resourceName))
group.Put("", "修改", global.WebPathPermit_Read, commonHandlerPut(srv.ctl, resourceName))
group.Delete("", "删除", global.WebPathPermit_Read, commonHandlerDelete(srv.ctl, resourceName))
}
func commonHandlerList(ctl *controller, resourceName string) func(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
return func(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
return ctl.CommonList(ctx, resourceName, params, rsp)
}
}
func commonHandlerPost(ctl *controller, resourceName string) func(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
return func(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error {
return ctl.CommonPost(ctx, resourceName, params, rsp)
}
}
func commonHandlerPut(ctl *controller, resourceName string) func(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
return func(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
return ctl.CommonPut(ctx, resourceName, params, rsp)
}
}
func commonHandlerDelete(ctl *controller, resourceName string) func(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
return func(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
return ctl.CommonDelete(ctx, resourceName, params, rsp)
func (srv *Server) getResourceList(ctx *context.WebContext, params *dto.NilReq, rsp *dto.ResourceListRsp) error {
for _, v := range srv.ctl.svc.GetSupportResourcesList() {
if v[0] == consts.ResourcesName_Project {
continue
}
rsp.List = append(rsp.List, &dto.ResourceInfo{Resource: v[0], Desc: v[1], Methods: []string{"get", "post", "put", "delete"}})
}
return nil
}

View File

@ -3,66 +3,63 @@ package service
import (
"admin/apps/game/domain"
"admin/apps/game/model/dto"
"admin/internal/consts"
"context"
"gorm.io/gorm"
)
type Service struct {
db *gorm.DB
projectSvc *domain.ProjectSvc
serverSvc *domain.ServerSvc
db *gorm.DB
resourceSvc *domain.CommonResourceService
projectSvc *domain.ProjectService
}
func New(db *gorm.DB) *Service {
return &Service{
db: db,
projectSvc: domain.NewProjectSvc(db),
serverSvc: domain.NewServerSvc(db),
func New(db *gorm.DB) (*Service, error) {
svc := &Service{
db: db,
resourceSvc: domain.NewCommonResourceService(db),
projectSvc: domain.NewProjectService(db),
}
err := svc.ensureProjectsDBData()
if err != nil {
return nil, err
}
return svc, nil
}
func (svc *Service) CommonList(ctx context.Context, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) {
restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName)
if err != nil {
return nil, err
func (svc *Service) CommonList(ctx context.Context, projectId string, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) {
var (
query string
args []any
)
switch resourceName {
case consts.ResourcesName_Project:
default:
query = "project_id = ?"
args = append(args, projectId)
}
dtoFieldsDescInfo, list, err := restfulDomainSvc.List(params.PageNo, params.PageLen)
if err != nil {
return nil, err
}
retList := make([]dto.CommonDtoValues, 0, len(list))
for _, v := range list {
retList = append(retList, v.ToCommonDto())
}
return &dto.CommonDtoList{FieldsDesc: dtoFieldsDescInfo, Rows: retList}, nil
fieldsDescInfo, rows, err := svc.resourceSvc.List(projectId, resourceName, params.PageNo, params.PageLen, query, args)
return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, Rows: rows}, err
}
func (svc *Service) CommonPost(ctx context.Context, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) {
restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName)
if err != nil {
return nil, err
func (svc *Service) CommonPost(ctx context.Context, projectId string, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) {
if resourceName != consts.ResourcesName_Project {
params["ProjectId"] = projectId
}
et, err := restfulDomainSvc.Post(params)
if err != nil {
return nil, err
}
return et.ToCommonDto(), nil
return svc.resourceSvc.Create(projectId, resourceName, params)
}
func (svc *Service) CommonPut(ctx context.Context, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) {
restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName)
if err != nil {
return nil, err
func (svc *Service) CommonPut(ctx context.Context, projectId string, resourceName string, params dto.CommonDtoValues) error {
if resourceName != consts.ResourcesName_Project {
params["ProjectId"] = projectId
}
et, err := restfulDomainSvc.Put(params)
if err != nil {
return nil, err
}
return et.ToCommonDto(), nil
return svc.resourceSvc.Edit(projectId, resourceName, params)
}
func (svc *Service) CommonDelete(ctx context.Context, resourceName string, id int) error {
restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName)
if err != nil {
return err
}
return restfulDomainSvc.Delete(id)
func (svc *Service) CommonDelete(ctx context.Context, projectId string, resourceName string, id int) error {
return svc.resourceSvc.Delete(projectId, resourceName, id)
}
func (svc *Service) GetSupportResourcesList() [][2]string {
return svc.resourceSvc.GetSupportResourcesList()
}

View File

@ -0,0 +1,9 @@
package service
func (svc *Service) ensureProjectsDBData() error {
return svc.projectSvc.EnsureProjectsDBData()
}
func (svc *Service) GetProjectInvokeApiAddr(projectId string, serverIds []int) ([]string, error) {
return svc.projectSvc.GetProjectInvokeApiAddr(projectId, serverIds)
}

View File

@ -18,3 +18,7 @@ type Account struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *Account) TableName() string {
return "mock_account"
}

View File

@ -20,3 +20,7 @@ type Ban struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *Ban) TableName() string {
return "mock_ban"
}

View File

@ -20,3 +20,7 @@ type DevicePush struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *DevicePush) TableName() string {
return "mock_devicepush"
}

View File

@ -21,3 +21,7 @@ type GlobalMail struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *GlobalMail) TableName() string {
return "mock_mailglobal"
}

View File

@ -21,3 +21,7 @@ type Notice struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *Notice) TableName() string {
return "mock_notice"
}

View File

@ -22,3 +22,7 @@ type Order struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *Order) TableName() string {
return "mock_order"
}

View File

@ -19,3 +19,7 @@ type RewardCode struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *RewardCode) TableName() string {
return "mock_rewardcode"
}

View File

@ -20,3 +20,7 @@ type Role struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *Role) TableName() string {
return "mock_role"
}

View File

@ -28,3 +28,7 @@ type RoleMail struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *RoleMail) TableName() string {
return "mock_mailrole"
}

View File

@ -20,3 +20,7 @@ type WhiteList struct {
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
func (lm *WhiteList) TableName() string {
return "mock_whitelist"
}

View File

@ -2,7 +2,7 @@ package server
import (
"admin/apps/mockpro/internal/model/dto"
"admin/internal/global"
"admin/internal/consts"
"admin/lib/web"
)
@ -20,7 +20,7 @@ func (srv *Server) Route(engine *web.Engine) {
srv.rewardcode(apiGroup)
srv.devicepush(apiGroup)
}
apiGroup.Get("/commandlist", "获取项目所有gm指令描述", global.WebPathPermit_Read, srv.commandlist)
apiGroup.Get("/commandlist", "获取项目所有gm指令描述", consts.WebPathPermit_Read, srv.commandlist)
}
func (srv *Server) whitelist(engine *web.RoutesGroup) {
@ -74,10 +74,10 @@ func (srv *Server) devicepush(engine *web.RoutesGroup) {
}
func (srv *Server) registerResourceRouter(resourceName string, group *web.RoutesGroup) {
group.Get("", "获取列表", global.WebPathPermit_Read, commonHandlerList(srv.ctl, resourceName))
group.Post("", "新增", global.WebPathPermit_Read, commonHandlerPost(srv.ctl, resourceName))
group.Put("", "修改", global.WebPathPermit_Read, commonHandlerPut(srv.ctl, resourceName))
group.Delete("", "删除", global.WebPathPermit_Read, commonHandlerDelete(srv.ctl, resourceName))
group.Get("", "获取列表", consts.WebPathPermit_Read, commonHandlerList(srv.ctl, resourceName))
group.Post("", "新增", consts.WebPathPermit_Read, commonHandlerPost(srv.ctl, resourceName))
group.Put("", "修改", consts.WebPathPermit_Read, commonHandlerPut(srv.ctl, resourceName))
group.Delete("", "删除", consts.WebPathPermit_Read, commonHandlerDelete(srv.ctl, resourceName))
}
func commonHandlerList(ctl *controller, resourceName string) func(ctx *WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {

View File

@ -3,6 +3,7 @@ package server
import (
"admin/apps/mockpro/internal/model/dto"
"admin/lib/web"
"path/filepath"
)
type PathInfo struct {
@ -10,6 +11,7 @@ type PathInfo struct {
Method string `json:"method"`
}
type ResourceInfo struct {
Path string `json:"path"`
Desc string `json:"desc"`
Paths []*PathInfo `json:"paths"`
}
@ -19,7 +21,7 @@ type CmdListRsp struct {
func (srv *Server) commandlist(ctx *WebContext, req *dto.NilReq, rsp *CmdListRsp) error {
paths := make([]*ResourceInfo, 0)
srv.engine.TravelPaths(func(path string, parentDesc string, method string, handlers ...web.HandlerFunc) {
srv.engine.TravelPaths(func(parentPath, parentDesc string, path string, method string, handlers ...web.HandlerFunc) {
find := false
for _, v := range paths {
if v.Desc == parentDesc {
@ -33,6 +35,7 @@ func (srv *Server) commandlist(ctx *WebContext, req *dto.NilReq, rsp *CmdListRsp
}
if !find {
paths = append(paths, &ResourceInfo{
Path: filepath.Base(parentPath),
Desc: parentDesc,
Paths: []*PathInfo{
&PathInfo{

View File

@ -0,0 +1,35 @@
package consts
const (
RegisteredProjectId_shenmodalu = "smdl"
RegisteredProjectId_shenyu = "sy"
RegisteredProjectId_mt = "mt"
)
var (
RegisteredProjects = map[string]struct {
ID string
Name string
}{
RegisteredProjectId_shenmodalu: {ID: RegisteredProjectId_shenmodalu, Name: "神魔大陆"},
RegisteredProjectId_shenyu: {ID: RegisteredProjectId_shenyu, Name: "神谕"},
RegisteredProjectId_mt: {ID: RegisteredProjectId_mt, Name: "MT"},
}
)
const (
ResourcesName_Project = "project"
ResourcesName_Server = "server"
ResourcesName_WhiteList = "whitelist"
ResourcesName_Ban = "ban"
ResourcesName_MailRole = "mail_role"
ResourcesName_MailGlobal = "mail_global"
ResourcesName_Notice = "notice"
ResourcesName_RewardCode = "reward_code"
ResourcesName_DevicePush = "device_push"
)
const (
WebPathPermit_Read = 1
WebPathPermit_Write = 2
)

View File

@ -18,6 +18,10 @@ func NewWebContext(rawCtx *gin.Context) web.IContext {
return &WebContext{rawCtx: rawCtx}
}
func (ctx *WebContext) GinCtx() *gin.Context {
return ctx.rawCtx
}
func (ctx *WebContext) Ok(data any) {
if ctx.alreadySetRsp {
return
@ -44,7 +48,7 @@ func (ctx *WebContext) Fail(err error) {
"msg": msg,
"data": "",
})
ctx.alreadySetRsp = true
}

View File

@ -136,7 +136,7 @@ func autoMigrate(db *gorm.DB, tables ...interface{}) error {
// 初始化表
if err := db.AutoMigrate(tables...); err != nil {
return errcode.New(errcode.DBError, "failed to init tables", err)
return errcode.New(errcode.DBError, "failed to init tables:%v", err)
}
return nil
}

View File

@ -1,6 +0,0 @@
package global
const (
WebPathPermit_Read = 1
WebPathPermit_Write = 2
)

View File

@ -53,7 +53,9 @@ func Request(addr string, method string, body interface{}, resData interface{})
return errcode.New(errcode.ServerError, "数据解析失败:%v", err)
}
xlog.Debugf("request url:%v, rsp:%v", removeUrl, string(resBody))
if len(resBody) < 256 {
xlog.Debugf("request url:%v, rsp:%v", removeUrl, string(resBody))
}
if err = json.Unmarshal(resBody, resData); err != nil {
return errcode.New(errcode.ServerError, "数据(%v)格式错误:%v", string(resBody), err)

View File

@ -39,7 +39,7 @@ func (n *routesNode) addMethod(method string, desc string, permit int, handlers
return n
}
func (n *routesNode) travel(parentPath string, parentDesc string, travelFun func(parentDesc string, path string, method string, handlers ...HandlerFunc)) {
func (n *routesNode) travel(parentPath string, parentDesc string, travelFun func(parentPath, parentDesc string, path string, method string, handlers ...HandlerFunc)) {
curPath := parentPath + n.path
curNodeDesc := ""
if len(n.methods) > 0 {
@ -48,7 +48,7 @@ func (n *routesNode) travel(parentPath string, parentDesc string, travelFun func
curNodeDesc = method.desc
continue
}
travelFun(curPath, parentDesc, method.method, method.handlers)
travelFun(parentPath, parentDesc, curPath, method.method, method.handlers)
}
}
for _, c := range n.child {

View File

@ -27,7 +27,7 @@ func (e *Engine) Run(addr string) error {
return e.rawEngine.Run(addr)
}
func (e *Engine) TravelPaths(travelFun func(path string, parentDesc string, method string, handlers ...HandlerFunc)) {
func (e *Engine) TravelPaths(travelFun func(parentPath, parentDesc, path string, method string, handlers ...HandlerFunc)) {
for _, c := range e.RoutesGroup.node.child {
c.travel("", "", travelFun)
}

View File

@ -93,6 +93,10 @@ func getGinHandlerFunWithRequest(newContextFun func(ctx *gin.Context) IContext,
rspTo := handlerTo.In(2)
rsp := reflect.New(rspTo.Elem()).Interface()
rets := reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(params), reflect.ValueOf(rsp)})
if len(rets) != 1 {
ctx.HandleError(rawCtx.Request.RequestURI, fmt.Errorf("register handler must return error type"))
return
}
errInt := rets[0]
if errInt.Interface() == nil {
ctx.HandleSuccess(rsp)

View File

@ -1,9 +1,16 @@
import request from '@/utils/request'
export function gameApiRequest(url, method, data) {
if (method === "get") {
return request({
url: url,
method: method,
params: data
})
}
return request({
url: url,
method: method,
params: data
data: data
})
}

View File

@ -1,10 +1,17 @@
import request from '@/utils/request'
class InternalResource {
list(url, params) {
this.name = name
}
}
export function resourceList(url, params) {
return request({
url: url,
method: 'get',
data: params
params: params
})
}

View File

@ -0,0 +1,63 @@
<script setup>
import {cachedProject} from '@/stores/project.js'
import {gameApiRequest} from '@/api/game_api.js'
import {projectOperationRoutes, setProjectOperationRoutes} from '@/router/index.js'
import {useRouter} from 'vue-router'
const router = useRouter()
const cachedProject1 = cachedProject()
const curProject = cachedProject1.get()
const resourceList = ref([])
console.log("handle project:", curProject)
console.log("command api addr:", curProject.ApiAddr)
gameApiRequest("/project/resourcelist", "get", {addr: curProject.ApiAddr}).then((res) => {
// console.log("commandlist!", res.data)
resourceList.value = res.data.list
setProjectOperationRoutes(curProject, resourceList.value)
console.log("all routes:", router.getRoutes())
}, (err) => {
})
const handleMenuSelect = (routePath) => {
let pushPath = {
path: routePath.path,
query: {
resource_url: "/project/" + curProject.ProjectId + "/" + routePath.meta.resource,
}
}
console.log("push node", pushPath)
router.push(pushPath)
}
</script>
<template>
<el-container>
<el-aside width="200px">
<el-menu
v-for="routePath in projectOperationRoutes"
router @select="handleMenuSelect(routePath)"
>
<el-menu-item :index="routePath.path">
<span>{{ routePath.name }}</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</template>
<style scoped>
</style>

View File

@ -2,23 +2,38 @@
import {ElNotification} from "element-plus";
import {resourceDelete, resourceList, resourcePost, resourcePut} from "@/api/resource.js";
import {ref, toRaw} from "vue";
import {useRoute} from 'vue-router';
const props = defineProps({
// rows: {},
resource_url: '',
resource_url: String,
row_click_handler: null,
})
const route = useRoute();
const listRsp = ref({fields_desc: [], rows: []})
const getListOk = ref(false)
const resource_url = props.resource_url
const listDataOK = ref(false)
const resource_url = props.resource_url !== undefined && props.resource_url !== "" ? props.resource_url : route.query.resource_url;
const fieldsDescInfo = ref([])
const rows = ref([])
const rules = ref({})
const item = ref({
id: 0,
number: 1,
})
console.log("table resource:", resource_url)
const listData = async () => {
try {
const rspData = await resourceList(resource_url, {page_no: 0, page_len: 100});
let listParams = {
page_no: 0,
page_len: 100,
}
console.log("list params:", listParams)
const rspData = await resourceList(resource_url, listParams);
listRsp.value = rspData;
if (listRsp.value.code !== 200) throw new Error("请求失败,错误码:", listRsp.code);
fieldsDescInfo.value = listRsp.value.data.fields_desc
@ -28,9 +43,15 @@ const listData = async () => {
for (let i = 0; i < fieldsDescInfo.value.length; i++) {
var field = fieldsDescInfo.value[i]
dialogAddForm.value[field.key] = ''
if (field.require == true) {
if (field.required == true) {
rules.value[field.key] = [{required: true, message: field.name + "不能为空", trigger: "blur"}]
}
if (field.type == "items") {
for (let j = 0; j < rows.value.length; j++) {
rows.value[j].jsonValue = JSON.stringify(rows.value[j][field.key])
}
}
}
console.log('await list rsp:', listRsp.value)
@ -38,7 +59,7 @@ const listData = async () => {
console.log("await rows", toRaw(rows.value))
console.log("await rules", toRaw(rules.value))
getListOk.value = true
listDataOK.value = true
} catch (err) {
console.log(err)
} finally {
@ -55,7 +76,9 @@ const dialogEditVisible = ref(false)
const dialogAddFormRef = ref(null)
const dialogEditFormRef = ref(null)
const dialogAddForm = ref({})
const dialogAddForm = ref({
ServerIDs: [],
})
const dialogEditForm = ref({})
@ -63,6 +86,7 @@ const submitAdd = async () => {
try {
await dialogAddFormRef.value.validate(valid => {
if (valid) {
console.log("commit add form:", dialogAddForm.value)
resourcePost(resource_url, dialogAddForm.value).then((res) => {
ElNotification({
title: "添加结果通知",
@ -71,7 +95,7 @@ const submitAdd = async () => {
duration: 4000,
"show-close": true,
})
rows.value.push(dialogAddForm.value)
rows.value.push(res.data.dto)
dialogAddVisible.value = false
}, (err) => {
console.log("添加报错:", err)
@ -109,7 +133,7 @@ const submitEdit = async () => {
}
}
const handleEdit = (oper, index, row) => {
const handleEdit = (index, row) => {
dialogEditForm.value.oldData = row
dialogEditForm.value.oldIndex = index
dialogEditForm.value = row
@ -117,7 +141,7 @@ const handleEdit = (oper, index, row) => {
dialogEditVisible.value = true
}
const handleDelete = (oper, index, row) => {
const handleDelete = (index, row) => {
resourceDelete(resource_url, {id: row.ID}).then((res) => {
ElNotification({
title: "删除结果通知",
@ -139,24 +163,63 @@ const tableRowClick = (index, row) => {
}
}
function addItem(fieldDescInfo) {
if (item.value.id == null || item.value.id == '' || item.value.id < 0) {
ElMessage('请选择道具!')
return;
}
if (item.value.num == null || item.value.num == '' || item.value.num <= 0) {
ElMessage('请输入有效道具数量!')
return;
}
let d = {id: item.value.id, num: Number(item.value.num)};
for (let i = 0; i < fieldDescInfo.choices.length; i++) {
const field = fieldDescInfo.choices[i]
if (field.value === item.value.id) {
d.item_type = field.type
break
}
}
console.log("add item:", d)
if (typeof dialogAddForm.value.Attach === typeof "") {
dialogAddForm.value.Attach = [];
}
dialogAddForm.value.Attach.push(d);
}
function deleteItem(row) {
//
let number = form.value.Attach.findIndex(item => item === row);
dialogAddForm.value.Attach.splice(number, 1);
}
const handleCloseDialog = () => {
dialogAddVisible.value = false
dialogEditVisible.value = false
dialogAddForm.value = {}
dialogAddForm.value = {
Attach: [],
}
dialogEditForm.value = {}
}
</script>
<template>
<el-container v-if="getListOk">
<el-container v-if="listDataOK">
<el-header>
<el-button @click="dialogAddVisible = true" size="large" type="primary">添加</el-button>
</el-header>
<el-main>
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"></el-table-column>
<el-table-column prop="jsonValue" :label="fieldDescInfo.name"
v-if="(fieldDescInfo.type === 'items')"></el-table-column>
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"
v-else></el-table-column>
</template>
<el-table-column prop="func" label="功 能">
<template #default="scope">
@ -166,13 +229,13 @@ const handleCloseDialog = () => {
</el-icon>
<span>进入</span>
</el-button>
<el-button size="large" type="success" @click="handleEdit('operation', scope.$index, scope.row)">
<el-button size="large" type="success" @click="handleEdit( scope.$index, scope.row)">
<el-icon style="vertical-align: middle">
<Operation/>
<Edit/>
</el-icon>
<span>编辑</span>
</el-button>
<el-button size="large" type="danger" @click="handleDelete('operation', scope.$index, scope.row)">
<el-button size="large" type="danger" @click="handleDelete( scope.$index, scope.row)">
<el-icon style="vertical-align: middle">
<Delete/>
</el-icon>
@ -186,14 +249,70 @@ const handleCloseDialog = () => {
destroy-on-close>
<el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules">
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name"></el-input>
</el-form-item>
<!--如何是items类型就是物品下拉框+道具组合-->
<!--如何是[]string类型就是下拉框或多选框-->
<template v-if="(fieldDescInfo.type === 'items')">
<el-form :inline="true" :model="item">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
<el-select placeholder="可选项" v-model="item.id" style="width: 150px">
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
:value="info.value"></el-option>
</el-select>
</el-tooltip>
</el-form-item>
<el-form-item label="数量" prop="number">
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
</el-form-item>
</el-form>
<el-form-item label="奖励列表" prop="attachmentsList">
<el-table :data="dialogAddForm.Attach" border>
<el-table-column label="道具id" prop="id"/>
<el-table-column label="数量" prop="num"/>
<el-table-column label="操作">
<template #default="scope">
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</template>
<template v-else-if="(fieldDescInfo.type === '[]string')">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
<el-select placeholder="可选项" v-model="dialogAddForm.ServerIDs" style="width: 150px"
:multiple="(fieldDescInfo.multi_choice === true)">
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
:value="info.value"></el-option>
</el-select>
</el-tooltip>
</el-form-item>
</template>
<template v-else-if="(fieldDescInfo.readonly !== true)">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name"></el-input>
</el-form-item>
</template>
<!-- <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">-->
<!-- <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start"-->
<!-- v-if="(fieldDescInfo.type === 'items')">-->
<!-- <el-select placeholder="可选项">-->
<!-- <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"-->
<!-- :value="info.value"></el-option>-->
<!-- </el-select>-->
<!-- </el-tooltip>-->
<!-- <el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name" v-else></el-input>-->
<!-- </el-form-item>-->
</template>
<el-form-item>
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
</el-form-item>
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
</el-form>
</el-dialog>

View File

@ -0,0 +1,11 @@
<script setup>
</script>
<template>
test1
</template>
<style scoped>
</style>

View File

@ -7,6 +7,31 @@ const query = () => {
}
}
export const projectOperationRoutes = ref([])
const mainRoutes = [
{
path: '/user',
name: 'user',
component: () => import('@/views/user/user.vue')
},
{
path: '/project',
name: 'project',
component: () => import('@/views/project/project.vue'),
// query: query(),
children: [
{
path: 'op',
name: "ProjectOperation",
component: () => import('@/components/project/op.vue'),
children: projectOperationRoutes.value,
},
]
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
@ -14,28 +39,34 @@ const router = createRouter({
path: '/',
name: 'home',
component: Home,
children: [
{
path: '/user',
name: 'user',
component: () => import('@/views/user/user.vue')
},
{
path: '/project',
name: 'project',
component: () => import('@/views/project/project.vue'),
query: query(),
children: [
{
path: 'operation',
name: "ProjectOperation",
component: () => import('@/views/project/operation.vue')
},
]
}
]
children: mainRoutes,
},
],
})
export default router
export function setProjectOperationRoutes(projectInfo, resourceList) {
console.log("resourceList:", resourceList)
projectOperationRoutes.value = []
for (let i = 0; i < resourceList.length; i++) {
const resource = resourceList[i]
const routerPath = {
path: '/project/' + resource.resource,
name: resource.desc,
meta: {
resource: resource.resource,
},
component: () => {
return import('@/components/restful/table.vue')
},
props: true
}
projectOperationRoutes.value.push(routerPath)
router.addRoute(routerPath)
}
}

View File

@ -1,49 +0,0 @@
<script setup>
import {cachedProject} from '@/stores/project.js'
import {gameApiRequest} from '@/api/game_api.js'
const cachedProject1 = cachedProject()
const curProject = cachedProject1.get()
const commandList = ref([])
console.log("handle project:", curProject)
console.log("command api addr:", curProject.ApiAddr)
gameApiRequest("/project/commandlist", "get", {addr: curProject.ApiAddr}).then((res) => {
// console.log("commandlist!", res)
commandList.value = res.data.list
}, (err) => {
})
</script>
<template>
<el-container>
<el-aside width="200px">
<el-menu
:default-active="activeMenu"
@select="handleMenuSelect"
>
<el-menu-item index="/user">
<span>用户管理</span>
</el-menu-item>
<el-menu-item index="/project">
<span>项目管理</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header>游戏后台管理系统</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</template>
<style scoped>
</style>

View File

@ -1,7 +1,7 @@
<script setup>
import table from '@/components/restful/table.vue'
import operation from '@/views/project/operation.vue'
import tableView from '@/components/restful/table.vue'
import op from '@/components/project/op.vue'
import {cachedProject} from '@/stores/project.js'
import {useRouter} from 'vue-router'
@ -9,7 +9,7 @@ const cachedProject1 = cachedProject()
const router = useRouter()
const page = ref('project')
const pageFlag = ref('project')
const row_click_handler = (row, column, event) => {
// console.log("project row is clicked:", row)
@ -17,17 +17,18 @@ const row_click_handler = (row, column, event) => {
const cachePro = cachedProject1.get()
console.log('cached project:', cachePro)
console.log("router:", router.getRoutes())
page.value = 'operation'
pageFlag.value = 'op'
}
</script>
<template>
<div v-if="page === 'project'">
<component :is="table" resource_url="/project" :row_click_handler="row_click_handler"></component>
<div v-if="pageFlag === 'project'">
<component :is="tableView" resource_url="/project" :row_click_handler="row_click_handler"></component>
</div>
<div v-else-if="page === 'operation'">
<component :is="operation" resource_url="/project" :row_click_handler="row_click_handler"></component>
<div v-else-if="pageFlag === 'op'">
<component :is="op" resource_url="/project" :row_click_handler="row_click_handler"></component>
</div>
</template>

View File

@ -1,6 +1,6 @@
import { fileURLToPath, URL } from 'node:url'
import {fileURLToPath, URL} from 'node:url'
import { defineConfig } from 'vite'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
@ -8,68 +8,68 @@ import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { loadEnv } from 'vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
import {loadEnv} from 'vite'
// https://vite.dev/config/
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd())
const { VITE_APP_ENV, VITE_APP_BASE_URL,VITE_APP_BASE_URL_WS} = env
return {
plugins: [
vue(),
vueDevTools(),
AutoImport({
imports: ['vue'],
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: 'Icon',
}),
export default defineConfig(({mode, command}) => {
const env = loadEnv(mode, process.cwd())
const {VITE_APP_ENV, VITE_APP_BASE_URL, VITE_APP_BASE_URL_WS} = env
return {
plugins: [
vue(),
vueDevTools(),
AutoImport({
imports: ['vue'],
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: 'Icon',
}),
],
}),
Components({
resolvers: [
IconsResolver({
enabledCollections: ['ep'],
}),
ElementPlusResolver(),
],
}),
Icons({
autoInstall: true,
}),
],
}),
Components({
resolvers: [
IconsResolver({
enabledCollections: ['ep'],
}),
ElementPlusResolver(),
],
}),
Icons({
autoInstall: true,
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
// vite 相关配置
server: {
port: 5173,
host: true,
open: false,
proxy: {
// https://cn.vitejs.dev/config/#server-proxy
'/api': {
target: VITE_APP_BASE_URL,
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
// '/dev-ws': {
// target: VITE_APP_BASE_URL_WS,
// changeOrigin: true,
// rewrite: (p) => p.replace(/^\/dev-ws/, ''),
// ws: true
// }
// vite 相关配置
server: {
port: 5174,
host: true,
open: false,
}
},
}
proxy: {
// https://cn.vitejs.dev/config/#server-proxy
'/api': {
target: VITE_APP_BASE_URL,
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
// '/dev-ws': {
// target: VITE_APP_BASE_URL_WS,
// changeOrigin: true,
// rewrite: (p) => p.replace(/^\/dev-ws/, ''),
// ws: true
// }
}
},
}
})