diff --git a/admin/apps/game/boot.go b/admin/apps/game/boot.go index 1a92228..d5bf3e5 100644 --- a/admin/apps/game/boot.go +++ b/admin/apps/game/boot.go @@ -8,14 +8,14 @@ import ( ) func initFun(app *node.Application) error { - svc := service.NewCmdServerSvc(global.GLOB_DB) // 初始化应用服务 - srv := server.New(svc) // 初始化http服务 - srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由 + svc := service.New(global.GLOB_DB) // 初始化应用服务 + srv := server.New(svc) // 初始化http服务 + srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由 return nil } func New() *node.ApplicationDescInfo { - app := node.NewApplicationDescInfo("user", initFun) + app := node.NewApplicationDescInfo("game", initFun) return app } diff --git a/admin/apps/game/domain/entity/project.go b/admin/apps/game/domain/entity/project.go index 87d39ed..1a6e577 100644 --- a/admin/apps/game/domain/entity/project.go +++ b/admin/apps/game/domain/entity/project.go @@ -3,6 +3,7 @@ package entity import ( "admin/apps/game/model" "admin/apps/game/model/dto" + "math/rand" "reflect" ) @@ -31,13 +32,17 @@ func FromProjectDto(dto dto.CommonDtoValues) *Project { po := et.po //to := reflect.TypeOf(po) - vo := reflect.ValueOf(po) + vo := reflect.ValueOf(po).Elem() + to := reflect.TypeOf(po).Elem() for k, v := range dto { fo := vo.FieldByName(k) - fo.Set(reflect.ValueOf(v)) + ft, _ := to.FieldByName(k) + fo.Set(parseStr2FieldValue(ft, v)) } + et.Id = po.ID + return et } @@ -77,7 +82,7 @@ func (et *Project) GetDtoFieldsDescInfo() []*dto.CommonDtoFieldDesc { Type: ft.Type.Name(), HelpText: ft.Tag.Get("desc"), Editable: true, - Require: true, + Require: rand.Int()%2 == 0, Choices: make([]*dto.CommonDtoFieldChoice, 0), MultiChoice: false, } diff --git a/admin/apps/game/domain/entity/server.go b/admin/apps/game/domain/entity/server.go index 0e321a4..b158cc7 100644 --- a/admin/apps/game/domain/entity/server.go +++ b/admin/apps/game/domain/entity/server.go @@ -38,6 +38,8 @@ func FromServerDto(dto dto.CommonDtoValues) *Server { fo.Set(reflect.ValueOf(v)) } + et.Id = po.ID + return et } diff --git a/admin/apps/game/domain/entity/utils.go b/admin/apps/game/domain/entity/utils.go new file mode 100644 index 0000000..cc1c8e3 --- /dev/null +++ b/admin/apps/game/domain/entity/utils.go @@ -0,0 +1,68 @@ +package entity + +import ( + "fmt" + "gorm.io/gorm" + "reflect" + "strconv" + "time" +) + +func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue reflect.Value) { + setValue := fmt.Sprintf("%v", rawValue) + var parsedValue any + switch field.Type.Kind() { + case reflect.Int: + v, _ := strconv.Atoi(setValue) + parsedValue = int(v) + case reflect.Int32: + v, _ := strconv.Atoi(setValue) + parsedValue = int32(v) + case reflect.Int8: + v, _ := strconv.Atoi(setValue) + parsedValue = int8(v) + case reflect.Int16: + v, _ := strconv.Atoi(setValue) + parsedValue = int16(v) + case reflect.Int64: + v, _ := strconv.Atoi(setValue) + parsedValue = int64(v) + case reflect.Uint: + v, _ := strconv.Atoi(setValue) + parsedValue = uint(v) + case reflect.Uint8: + v, _ := strconv.Atoi(setValue) + parsedValue = uint(v) + case reflect.Uint16: + v, _ := strconv.Atoi(setValue) + parsedValue = uint16(v) + case reflect.Uint32: + v, _ := strconv.Atoi(setValue) + parsedValue = uint32(v) + case reflect.Uint64: + v, _ := strconv.Atoi(setValue) + parsedValue = uint64(v) + case reflect.Bool: + parsedValue = setValue == "true" + case reflect.String: + parsedValue = setValue + case reflect.Float32: + v, _ := strconv.ParseFloat(setValue, 10) + parsedValue = float32(v) + case reflect.Float64: + v, _ := strconv.ParseFloat(setValue, 10) + parsedValue = float64(v) + case reflect.Struct: + typeName := field.Type.Name() + if typeName == "Time" { + return reflect.ValueOf(time.Now()) + } + if typeName == "DeletedAt" { + return reflect.ValueOf(gorm.DeletedAt{}) + } + fallthrough + default: + panic(fmt.Errorf("暂时不支持的前后端交互字段类型:%v, 类型名:%v", field.Type.Kind(), field.Type.Name())) + } + return reflect.ValueOf(parsedValue) +} diff --git a/admin/apps/game/domain/repo/project.go b/admin/apps/game/domain/repo/project.go index 361a20c..9e789cf 100644 --- a/admin/apps/game/domain/repo/project.go +++ b/admin/apps/game/domain/repo/project.go @@ -34,12 +34,12 @@ func (repo *projectRepoImpl) List(pageNo, pageLen int) ([]*entity.Project, error } // debug - list = append(list, &model.Project{ - ID: 123, - Name: "神魔大陆", - Desc: "神魔大陆服务器", - ApiAddr: "http://192.168.1.1:8081", - }) + //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 { diff --git a/admin/apps/game/model/dto/common.go b/admin/apps/game/model/dto/common.go index 6f994f5..a296593 100644 --- a/admin/apps/game/model/dto/common.go +++ b/admin/apps/game/model/dto/common.go @@ -31,3 +31,8 @@ type CommonDtoList struct { FieldsDesc []*CommonDtoFieldDesc `json:"fields_desc"` // 数据字段描述信息 Rows []CommonDtoValues `json:"rows"` // 数据行 } + +type PathInfo struct { + Path string `json:"path"` + Method string `json:"method"` +} diff --git a/admin/apps/game/model/dto/msg.go b/admin/apps/game/model/dto/msg.go new file mode 100644 index 0000000..d4179cf --- /dev/null +++ b/admin/apps/game/model/dto/msg.go @@ -0,0 +1,45 @@ +package dto + +type NilReq struct { +} + +type NilRsp = NilReq + +type CommonListReq struct { + PageNo int `json:"page_no"` + PageLen int `json:"page_len"` +} + +type CommonPostReq struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonPutReq struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonDeleteReq struct { + Id int `json:"id"` +} + +type CommonListRsp = CommonDtoList + +type CommonPostRsp struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonPutRsp struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonDeleteRsp struct { + Id int `json:"id"` +} + +type CommandListReq struct { + Addr string `json:"addr"` +} + +type CommandListRsp struct { + List []*PathInfo `json:"list"` +} diff --git a/admin/apps/game/model/dto/response.go b/admin/apps/game/model/dto/response.go deleted file mode 100644 index 95538ee..0000000 --- a/admin/apps/game/model/dto/response.go +++ /dev/null @@ -1,3 +0,0 @@ -package dto - -type CommonListRsp = CommonDtoList diff --git a/admin/apps/game/server/ctl_common_rest.go b/admin/apps/game/server/ctl_common_rest.go index 90e8f59..1e292d3 100644 --- a/admin/apps/game/server/ctl_common_rest.go +++ b/admin/apps/game/server/ctl_common_rest.go @@ -5,38 +5,37 @@ import ( "admin/internal/context" ) -func (ctl *controller) CommonList(ctx *context.WebContext, restfulResourceName string, params *dto.CommonListReq) { +func (ctl *controller) CommonList(ctx *context.WebContext, restfulResourceName string, params *dto.CommonListReq, rsp *dto.CommonListRsp) error { list, err := ctl.svc.CommonList(ctx, restfulResourceName, params) if err != nil { - ctx.Fail(err) - return + return err } - ctx.Ok(list) + *rsp = *list + return nil } -func (ctl *controller) CommonPost(ctx *context.WebContext, restfulResourceName string, params *dto.CommonDtoValues) { - newObj, err := ctl.svc.CommonPost(ctx, restfulResourceName, *params) +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) if err != nil { - ctx.Fail(err) - return + return err } - ctx.Ok(newObj) + rsp.Dto = &newObj + return nil } -func (ctl *controller) CommonPut(ctx *context.WebContext, restfulResourceName string, params *dto.CommonDtoValues) { - newObj, err := ctl.svc.CommonPut(ctx, restfulResourceName, *params) +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) if err != nil { - ctx.Fail(err) - return + return err } - ctx.Ok(newObj) + rsp.Dto = &newObj + return nil } -func (ctl *controller) CommonDelete(ctx *context.WebContext, restfulResourceName string, id int) { - err := ctl.svc.CommonDelete(ctx, restfulResourceName, id) +func (ctl *controller) CommonDelete(ctx *context.WebContext, restfulResourceName string, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { + err := ctl.svc.CommonDelete(ctx, restfulResourceName, params.Id) if err != nil { - ctx.Fail(err) - return + return err } - ctx.Ok(nil) + return nil } diff --git a/admin/apps/game/server/ctl_game.go b/admin/apps/game/server/ctl_game.go new file mode 100644 index 0000000..bdb08aa --- /dev/null +++ b/admin/apps/game/server/ctl_game.go @@ -0,0 +1,31 @@ +package server + +import ( + "admin/apps/game/model/dto" + "admin/internal/context" + "admin/lib/httpclient" + "admin/lib/xlog" +) + +func (ctl *controller) CommandList(ctx *context.WebContext, params *dto.CommandListReq, rsp *dto.CommandListRsp) error { + + url := params.Addr + "/api/commandlist" + + xlog.Debugf("request url:%v command list", url) + + cmdListRsp := make(map[string]any) + err := httpclient.Request(url, "get", nil, &cmdListRsp) + if err != nil { + return err + } + + xlog.Debugf("command list rsp:%+v", cmdListRsp) + + ctx.Ok(cmdListRsp) + + return nil +} + +func (ctl *controller) apiRequest() { + +} diff --git a/admin/apps/game/server/route.go b/admin/apps/game/server/route.go index 46f92ee..4ba5076 100644 --- a/admin/apps/game/server/route.go +++ b/admin/apps/game/server/route.go @@ -3,55 +3,57 @@ package server import ( "admin/apps/game/model/dto" "admin/internal/context" + "admin/internal/global" "admin/lib/web" ) func (srv *Server) Route(engine *web.Engine) { - apiGroup := engine.Group("/api") + apiGroup := engine.Group("/api", "") srv.proRoute(apiGroup) srv.serverRoute(apiGroup) + + { + apiGroup.Get("/project/commandlist", "调用对应游戏服api获取命令列表", global.WebPathPermit_Read, srv.ctl.CommandList) + } } -func (srv *Server) proRoute(engine *web.Group) { +func (srv *Server) proRoute(engine *web.RoutesGroup) { resourceName := "project" - proGroup := engine.Group("/" + resourceName) - - proGroup.Get("", "获取项目列表", web.AccessMode_Read, dto.CommonListReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Post("", "新增项目", web.AccessMode_Read, dto.CommonPostReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Put("", "修改项目", web.AccessMode_Read, dto.CommonPutReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Delete("", "删除项目", web.AccessMode_Read, dto.CommonDeleteReq{}, commonHandlerList(srv.ctl, resourceName)) + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "")) } -func (srv *Server) serverRoute(engine *web.Group) { +func (srv *Server) serverRoute(engine *web.RoutesGroup) { resourceName := "server" - proGroup := engine.Group("/" + resourceName) - - proGroup.Get("", "获取服务器列表", web.AccessMode_Read, dto.CommonListReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Post("", "新增服务器", web.AccessMode_Read, dto.CommonPostReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Put("", "修改服务器", web.AccessMode_Read, dto.CommonPutReq{}, commonHandlerList(srv.ctl, resourceName)) - proGroup.Delete("", "删除服务器", web.AccessMode_Read, dto.CommonDeleteReq{}, commonHandlerList(srv.ctl, resourceName)) + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "")) } -func commonHandlerList(ctl *controller, resourceName string) func(ctx *context.WebContext, params *dto.CommonListReq) { - return func(ctx *context.WebContext, params *dto.CommonListReq) { - ctl.CommonList(ctx, resourceName, params) +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) { - return func(ctx *context.WebContext, params *dto.CommonPostReq) { - ctl.CommonPost(ctx, resourceName, params.Dto) +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) { - return func(ctx *context.WebContext, params *dto.CommonPutReq) { - ctl.CommonPut(ctx, resourceName, params.Dto) +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) { - return func(ctx *context.WebContext, params *dto.CommonDeleteReq) { - ctl.CommonDelete(ctx, resourceName, params.Id) +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) } } diff --git a/admin/apps/game/service/service.go b/admin/apps/game/service/service.go index cfa5cf6..b953b40 100644 --- a/admin/apps/game/service/service.go +++ b/admin/apps/game/service/service.go @@ -13,7 +13,7 @@ type Service struct { serverSvc *domain.ServerSvc } -func NewCmdServerSvc(db *gorm.DB) *Service { +func New(db *gorm.DB) *Service { return &Service{ db: db, projectSvc: domain.NewProjectSvc(db), diff --git a/admin/apps/mockpro/boot.go b/admin/apps/mockpro/boot.go new file mode 100644 index 0000000..7747d8b --- /dev/null +++ b/admin/apps/mockpro/boot.go @@ -0,0 +1,28 @@ +package mockpro + +import ( + internalGlobal "admin/apps/mockpro/internal/global" + "admin/apps/mockpro/server" + "admin/apps/mockpro/service" + "admin/internal/global" + "admin/lib/node" +) + +func initFun(app *node.Application) error { + internalGlobal.GLOB_DB = global.GLOB_DB + svc := service.New(global.GLOB_DB) + server.New(svc) // 初始化http服务 + return nil +} + +func New() *node.ApplicationDescInfo { + app := node.NewApplicationDescInfo("mock_project", initFun). + WithOptions(node.WithAppBootFlag(internalGlobal.GLOB_BOOT_FLAGS)) + return app +} + +func must(err error) { + if err != nil { + panic(err) + } +} diff --git a/admin/apps/mockpro/domain/ban.go b/admin/apps/mockpro/domain/ban.go new file mode 100644 index 0000000..9d5b5eb --- /dev/null +++ b/admin/apps/mockpro/domain/ban.go @@ -0,0 +1,48 @@ +package domain + +import ( + "admin/apps/mockpro/domain/entity" + "admin/apps/mockpro/domain/repo" + "admin/apps/mockpro/internal/model/dto" + "gorm.io/gorm" +) + +type BanSvc struct { + BanRepo repo.IBanRepo +} + +func NewBanSvc(db *gorm.DB) *BanSvc { + svc := &BanSvc{ + BanRepo: repo.NewBanRepo(db), + } + registerRestfulSvc("ban", svc) + return svc +} + +func (svc *BanSvc) List(pageNo, pageLen int) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error) { + entityList, err := svc.BanRepo.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.BanDtoFieldsDescInfo, iList, nil +} + +func (svc *BanSvc) Post(obj dto.CommonDtoValues) (IRestfulEntity, error) { + et := entity.FromBanDto(obj) + err := svc.BanRepo.Create(et) + return et, err +} + +func (svc *BanSvc) Put(obj dto.CommonDtoValues) (IRestfulEntity, error) { + et := entity.FromBanDto(obj) + err := svc.BanRepo.Edit(et) + return et, err +} + +func (svc *BanSvc) Delete(id int) error { + return svc.BanRepo.Delete(id) +} diff --git a/admin/apps/mockpro/domain/entity/server.go b/admin/apps/mockpro/domain/entity/server.go new file mode 100644 index 0000000..e953551 --- /dev/null +++ b/admin/apps/mockpro/domain/entity/server.go @@ -0,0 +1,89 @@ +package entity + +import ( + "admin/apps/mockpro/internal/model" + "admin/apps/mockpro/internal/model/dto" + "reflect" +) + +var BanDtoFieldsDescInfo = DefaultBan().GetDtoFieldsDescInfo() + +type Ban struct { + Id int + po *model.Ban +} + +func DefaultBan() *Ban { + return &Ban{ + po: &model.Ban{}, + } +} + +func FromBanPo(po *model.Ban) *Ban { + return &Ban{ + Id: po.ID, + po: po, + } +} + +func FromBanDto(dto dto.CommonDtoValues) *Ban { + et := DefaultBan() + 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)) + } + + return et +} + +func (et *Ban) ToPo() *model.Ban { + return et.po +} + +func (et *Ban) 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 *Ban) 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 +} diff --git a/admin/apps/mockpro/domain/entity/whitelist.go b/admin/apps/mockpro/domain/entity/whitelist.go new file mode 100644 index 0000000..1eec71c --- /dev/null +++ b/admin/apps/mockpro/domain/entity/whitelist.go @@ -0,0 +1,89 @@ +package entity + +import ( + "admin/apps/mockpro/internal/model" + "admin/apps/mockpro/internal/model/dto" + "reflect" +) + +var WhiteListDtoFieldsDescInfo = DefaultWhiteList().GetDtoFieldsDescInfo() + +type WhiteList struct { + Id int + po *model.WhiteList +} + +func DefaultWhiteList() *WhiteList { + return &WhiteList{ + po: &model.WhiteList{}, + } +} + +func FromWhiteListPo(po *model.WhiteList) *WhiteList { + return &WhiteList{ + Id: po.ID, + po: po, + } +} + +func FromWhiteListDto(dto dto.CommonDtoValues) *WhiteList { + et := DefaultWhiteList() + 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)) + } + + return et +} + +func (et *WhiteList) ToPo() *model.WhiteList { + return et.po +} + +func (et *WhiteList) 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 *WhiteList) 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 +} diff --git a/admin/apps/mockpro/domain/irestfull.go b/admin/apps/mockpro/domain/irestfull.go new file mode 100644 index 0000000..6e427ae --- /dev/null +++ b/admin/apps/mockpro/domain/irestfull.go @@ -0,0 +1,31 @@ +package domain + +import ( + "admin/apps/mockpro/internal/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 +} diff --git a/admin/apps/mockpro/domain/repo/ban.go b/admin/apps/mockpro/domain/repo/ban.go new file mode 100644 index 0000000..94ce678 --- /dev/null +++ b/admin/apps/mockpro/domain/repo/ban.go @@ -0,0 +1,69 @@ +package repo + +import ( + "admin/apps/mockpro/domain/entity" + "admin/apps/mockpro/internal/model" + "admin/internal/errcode" + "gorm.io/gorm" +) + +type IBanRepo interface { + List(pageNo, pageLen int) ([]*entity.Ban, error) + Create(et *entity.Ban) error + Edit(et *entity.Ban) error + Delete(id int) error +} + +func NewBanRepo(db *gorm.DB) IBanRepo { + return newBanRepoImpl(db) +} + +type BanRepoImpl struct { + db *gorm.DB +} + +func newBanRepoImpl(db *gorm.DB) *BanRepoImpl { + return &BanRepoImpl{db: db} +} + +func (repo *BanRepoImpl) List(pageNo, pageLen int) ([]*entity.Ban, error) { + list := make([]*model.Ban, 0) + err := repo.db.Find(&list).Error + if err != nil { + return nil, errcode.New(errcode.DBError, "find Ban error:%v", err) + } + + entityList := make([]*entity.Ban, 0, len(list)) + for _, Ban := range list { + entityList = append(entityList, entity.FromBanPo(Ban)) + } + + return entityList, nil +} + +func (repo *BanRepoImpl) Create(et *entity.Ban) 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 *BanRepoImpl) Edit(et *entity.Ban) 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 *BanRepoImpl) Delete(id int) error { + err := repo.db.Where("id=?", id).Unscoped().Delete(&model.Ban{}).Error + if err != nil { + return errcode.New(errcode.DBError, "delete obj:%+v error:%v", id, err) + } + return nil +} diff --git a/admin/apps/mockpro/domain/repo/whitelist.go b/admin/apps/mockpro/domain/repo/whitelist.go new file mode 100644 index 0000000..6d5ee62 --- /dev/null +++ b/admin/apps/mockpro/domain/repo/whitelist.go @@ -0,0 +1,69 @@ +package repo + +import ( + "admin/apps/mockpro/domain/entity" + "admin/apps/mockpro/internal/model" + "admin/internal/errcode" + "gorm.io/gorm" +) + +type IWhiteListRepo interface { + List(pageNo, pageLen int) ([]*entity.WhiteList, error) + Create(et *entity.WhiteList) error + Edit(et *entity.WhiteList) error + Delete(id int) error +} + +func NewWhiteListRepo(db *gorm.DB) IWhiteListRepo { + return newWhiteListRepoImpl(db) +} + +type WhiteListRepoImpl struct { + db *gorm.DB +} + +func newWhiteListRepoImpl(db *gorm.DB) *WhiteListRepoImpl { + return &WhiteListRepoImpl{db: db} +} + +func (repo *WhiteListRepoImpl) List(pageNo, pageLen int) ([]*entity.WhiteList, error) { + list := make([]*model.WhiteList, 0) + err := repo.db.Find(&list).Error + if err != nil { + return nil, errcode.New(errcode.DBError, "find WhiteList error:%v", err) + } + + entityList := make([]*entity.WhiteList, 0, len(list)) + for _, WhiteList := range list { + entityList = append(entityList, entity.FromWhiteListPo(WhiteList)) + } + + return entityList, nil +} + +func (repo *WhiteListRepoImpl) Create(et *entity.WhiteList) 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 *WhiteListRepoImpl) Edit(et *entity.WhiteList) 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 *WhiteListRepoImpl) Delete(id int) error { + err := repo.db.Where("id=?", id).Unscoped().Delete(&model.WhiteList{}).Error + if err != nil { + return errcode.New(errcode.DBError, "delete obj:%+v error:%v", id, err) + } + return nil +} diff --git a/admin/apps/mockpro/domain/whitelist.go b/admin/apps/mockpro/domain/whitelist.go new file mode 100644 index 0000000..baacaf7 --- /dev/null +++ b/admin/apps/mockpro/domain/whitelist.go @@ -0,0 +1,48 @@ +package domain + +import ( + "admin/apps/mockpro/domain/entity" + "admin/apps/mockpro/domain/repo" + "admin/apps/mockpro/internal/model/dto" + "gorm.io/gorm" +) + +type WhiteListSvc struct { + proRepo repo.IWhiteListRepo +} + +func NewWhiteListSvc(db *gorm.DB) *WhiteListSvc { + svc := &WhiteListSvc{ + proRepo: repo.NewWhiteListRepo(db), + } + registerRestfulSvc("whitelist", svc) + return svc +} + +func (svc *WhiteListSvc) List(pageNo, pageLen int) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error) { + entityList, err := svc.proRepo.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.WhiteListDtoFieldsDescInfo, iList, nil +} + +func (svc *WhiteListSvc) Post(obj dto.CommonDtoValues) (IRestfulEntity, error) { + et := entity.FromWhiteListDto(obj) + err := svc.proRepo.Create(et) + return et, err +} + +func (svc *WhiteListSvc) Put(obj dto.CommonDtoValues) (IRestfulEntity, error) { + et := entity.FromWhiteListDto(obj) + err := svc.proRepo.Edit(et) + return et, err +} + +func (svc *WhiteListSvc) Delete(id int) error { + return svc.proRepo.Delete(id) +} diff --git a/admin/apps/mockpro/internal/config/flags.go b/admin/apps/mockpro/internal/config/flags.go new file mode 100644 index 0000000..04a5261 --- /dev/null +++ b/admin/apps/mockpro/internal/config/flags.go @@ -0,0 +1,5 @@ +package config + +type MockBootFlags struct { + ApiPort string `env:"mock_api_port" default:"8654" desc:"api端口,客户端请求的地址端口"` +} diff --git a/admin/apps/mockpro/internal/global/var.go b/admin/apps/mockpro/internal/global/var.go new file mode 100644 index 0000000..5b4022a --- /dev/null +++ b/admin/apps/mockpro/internal/global/var.go @@ -0,0 +1,11 @@ +package global + +import ( + "admin/apps/mockpro/internal/config" + "gorm.io/gorm" +) + +var ( + GLOB_BOOT_FLAGS = &config.MockBootFlags{} + GLOB_DB *gorm.DB +) diff --git a/admin/apps/mockpro/internal/model/account.go b/admin/apps/mockpro/internal/model/account.go new file mode 100644 index 0000000..524e052 --- /dev/null +++ b/admin/apps/mockpro/internal/model/account.go @@ -0,0 +1,20 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Account{}) +} + +type Account struct { + ID int `gorm:"primarykey"` + Account string `gorm:"primarykey"` + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/ban.go b/admin/apps/mockpro/internal/model/ban.go new file mode 100644 index 0000000..0701e4a --- /dev/null +++ b/admin/apps/mockpro/internal/model/ban.go @@ -0,0 +1,22 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Ban{}) +} + +type Ban struct { + ID int `gorm:"primarykey"` + BanType string + Value string + ExpireSeconds int + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/devicepush.go b/admin/apps/mockpro/internal/model/devicepush.go new file mode 100644 index 0000000..fc53060 --- /dev/null +++ b/admin/apps/mockpro/internal/model/devicepush.go @@ -0,0 +1,22 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(DevicePush{}) +} + +type DevicePush struct { + ID int `gorm:"primarykey"` + Account string + Title string + Content string + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/dto/common.go b/admin/apps/mockpro/internal/model/dto/common.go new file mode 100644 index 0000000..6f994f5 --- /dev/null +++ b/admin/apps/mockpro/internal/model/dto/common.go @@ -0,0 +1,33 @@ +package dto + +type CommonDtoFieldChoice struct { + Desc string `json:"desc"` + Value any `json:"value"` + // 描述选项的类型,例如添加物品时,可以添加道具、翅膀、宠物等,他们可能不一定都设计为道具 + Type int `json:"type"` +} + +type CommonDtoFieldDesc struct { + Name string `json:"name"` + Key string `json:"key"` + // 字段类型,基础类型支持int float string bool []<基础类行>, + // 支持自定义类型和自定义类型的数组 + Type string `json:"type"` + HelpText string `json:"help_text"` + Editable bool `json:"editable"` // 是否可编辑,例如id就不可编辑,新增时也不需要填写 + Require bool `json:"require"` // 是否必填,不能为空 + Choices []*CommonDtoFieldChoice `json:"choices"` // 可选项,用于字段做下拉框 + MultiChoice bool `json:"multi_choice"` // 是否多选 +} + +//type CommonDtoValue struct { +// FieldName string `json:"field_name"` +// Value any `json:"value"` +//} + +type CommonDtoValues map[string]any + +type CommonDtoList struct { + FieldsDesc []*CommonDtoFieldDesc `json:"fields_desc"` // 数据字段描述信息 + Rows []CommonDtoValues `json:"rows"` // 数据行 +} diff --git a/admin/apps/game/model/dto/request.go b/admin/apps/mockpro/internal/model/dto/msg.go similarity index 51% rename from admin/apps/game/model/dto/request.go rename to admin/apps/mockpro/internal/model/dto/msg.go index 9bf54ab..8ee41bd 100644 --- a/admin/apps/game/model/dto/request.go +++ b/admin/apps/mockpro/internal/model/dto/msg.go @@ -1,5 +1,11 @@ package dto +type NilReq struct { +} + +type NilRsp struct { +} + type CommonListReq struct { PageNo int `json:"page_no"` PageLen int `json:"page_len"` @@ -16,3 +22,17 @@ type CommonPutReq struct { type CommonDeleteReq struct { Id int `json:"id"` } + +type CommonListRsp = CommonDtoList + +type CommonPostRsp struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonPutRsp struct { + Dto *CommonDtoValues `json:"dto"` +} + +type CommonDeleteRsp struct { + Id int `json:"id"` +} diff --git a/admin/apps/mockpro/internal/model/globalmail.go b/admin/apps/mockpro/internal/model/globalmail.go new file mode 100644 index 0000000..422ee08 --- /dev/null +++ b/admin/apps/mockpro/internal/model/globalmail.go @@ -0,0 +1,23 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(GlobalMail{}) +} + +type GlobalMail struct { + ID int `gorm:"primarykey"` + ServerIDs []int `gorm:"type:json;serializer:json"` + Title string + Content string + Attach []*MailAttachItem `gorm:"type:json;serializer:json"` + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/notice.go b/admin/apps/mockpro/internal/model/notice.go new file mode 100644 index 0000000..83e43bf --- /dev/null +++ b/admin/apps/mockpro/internal/model/notice.go @@ -0,0 +1,23 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Notice{}) +} + +type Notice struct { + ID int `gorm:"primarykey"` + ServerIDs []int `gorm:"type:json;serializer:json"` + Content string + StartAt int64 + EndAt int64 + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/order.go b/admin/apps/mockpro/internal/model/order.go new file mode 100644 index 0000000..9274538 --- /dev/null +++ b/admin/apps/mockpro/internal/model/order.go @@ -0,0 +1,24 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Order{}) +} + +type Order struct { + ID int `gorm:"primarykey"` + RoleID string + Account string + Price int + Currency int + GoodID int + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/rewardcode.go b/admin/apps/mockpro/internal/model/rewardcode.go new file mode 100644 index 0000000..393a8fb --- /dev/null +++ b/admin/apps/mockpro/internal/model/rewardcode.go @@ -0,0 +1,21 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(RewardCode{}) +} + +type RewardCode struct { + ID int `gorm:"primarykey"` + Group int + Code int + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/role.go b/admin/apps/mockpro/internal/model/role.go new file mode 100644 index 0000000..7af6a99 --- /dev/null +++ b/admin/apps/mockpro/internal/model/role.go @@ -0,0 +1,22 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Role{}) +} + +type Role struct { + ID int `gorm:"primarykey"` + RoleID string + Name string + Account string `gorm:"primarykey"` + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/rolemail.go b/admin/apps/mockpro/internal/model/rolemail.go new file mode 100644 index 0000000..3e946a2 --- /dev/null +++ b/admin/apps/mockpro/internal/model/rolemail.go @@ -0,0 +1,30 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(RoleMail{}) +} + +type MailAttachItem struct { + ID int32 + Num int64 + ItemType int +} + +type RoleMail struct { + ID int `gorm:"primarykey"` + RoleIDs string + ServerID int + Title string + Content string + Attach []*MailAttachItem `gorm:"type:json;serializer:json"` + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/internal/model/whitelist.go b/admin/apps/mockpro/internal/model/whitelist.go new file mode 100644 index 0000000..14bda69 --- /dev/null +++ b/admin/apps/mockpro/internal/model/whitelist.go @@ -0,0 +1,22 @@ +package model + +import ( + "admin/internal/db" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(WhiteList{}) +} + +type WhiteList struct { + ID int `gorm:"primarykey"` + Account string `gorm:"primarykey"` + AccountType int + Desc string + + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt `gorm:"index"` +} diff --git a/admin/apps/mockpro/server/ctl.go b/admin/apps/mockpro/server/ctl.go new file mode 100644 index 0000000..0fe38ab --- /dev/null +++ b/admin/apps/mockpro/server/ctl.go @@ -0,0 +1,11 @@ +package server + +import "admin/apps/mockpro/service" + +type controller struct { + svc *service.Service +} + +func newController(svc *service.Service) *controller { + return &controller{svc: svc} +} diff --git a/admin/apps/mockpro/server/ctl_mock.go b/admin/apps/mockpro/server/ctl_mock.go new file mode 100644 index 0000000..724070b --- /dev/null +++ b/admin/apps/mockpro/server/ctl_mock.go @@ -0,0 +1,40 @@ +package server + +import ( + dto2 "admin/apps/mockpro/internal/model/dto" +) + +func (ctl *controller) CommonList(ctx *WebContext, restfulResourceName string, params *dto2.CommonListReq, rsp *dto2.CommonListRsp) error { + list, err := ctl.svc.CommonList(ctx, restfulResourceName, params) + if err != nil { + return err + } + *rsp = *list + return nil +} + +func (ctl *controller) CommonPost(ctx *WebContext, restfulResourceName string, params *dto2.CommonPostReq, rsp *dto2.CommonPostRsp) error { + newObj, err := ctl.svc.CommonPost(ctx, restfulResourceName, *params.Dto) + if err != nil { + return err + } + rsp.Dto = &newObj + return nil +} + +func (ctl *controller) CommonPut(ctx *WebContext, restfulResourceName string, params *dto2.CommonPutReq, rsp *dto2.CommonPutRsp) error { + newObj, err := ctl.svc.CommonPut(ctx, restfulResourceName, *params.Dto) + if err != nil { + return err + } + rsp.Dto = &newObj + return nil +} + +func (ctl *controller) CommonDelete(ctx *WebContext, restfulResourceName string, params *dto2.CommonDeleteReq, rsp *dto2.CommonDeleteRsp) error { + err := ctl.svc.CommonDelete(ctx, restfulResourceName, params.Id) + if err != nil { + return err + } + return nil +} diff --git a/admin/apps/mockpro/server/ctx_web.go b/admin/apps/mockpro/server/ctx_web.go new file mode 100644 index 0000000..e15587b --- /dev/null +++ b/admin/apps/mockpro/server/ctx_web.go @@ -0,0 +1,43 @@ +package server + +import ( + "admin/internal/errcode" + "admin/lib/web" + "context" + "github.com/gin-gonic/gin" +) + +type WebContext struct { + context.Context + rawCtx *gin.Context +} + +func NewWebContext(rawCtx *gin.Context) web.IContext { + return &WebContext{rawCtx: rawCtx} +} + +func (ctx *WebContext) Ok(data any) { + ctx.rawCtx.JSON(200, map[string]any{ + "code": 200, + "msg": "", + "data": data, + }) +} + +func (ctx *WebContext) Fail(err error) { + code, stack, msg := errcode.ParseError(err) + ctx.rawCtx.JSON(200, map[string]any{ + "code": code, + "stack": stack, + "msg": msg, + "data": "", + }) +} + +func (ctx *WebContext) HandleError(path string, err error) { + ctx.Fail(err) +} + +func (ctx *WebContext) HandleSuccess(rspData any) { + ctx.Ok(rspData) +} diff --git a/admin/apps/mockpro/server/route.go b/admin/apps/mockpro/server/route.go new file mode 100644 index 0000000..257a79e --- /dev/null +++ b/admin/apps/mockpro/server/route.go @@ -0,0 +1,105 @@ +package server + +import ( + "admin/apps/mockpro/internal/model/dto" + "admin/internal/global" + "admin/lib/web" +) + +func (srv *Server) Route(engine *web.Engine) { + apiGroup := engine.Group("/api", "") + { + srv.whitelist(apiGroup) + srv.ban(apiGroup) + srv.rolemail(apiGroup) + srv.globalmail(apiGroup) + srv.account(apiGroup) + srv.role(apiGroup) + srv.order(apiGroup) + srv.notice(apiGroup) + srv.rewardcode(apiGroup) + srv.devicepush(apiGroup) + } + apiGroup.Get("/commandlist", "获取项目所有gm指令描述", global.WebPathPermit_Read, srv.commandlist) +} + +func (srv *Server) whitelist(engine *web.RoutesGroup) { + resourceName := "whitelist" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "白名单管理")) +} + +func (srv *Server) ban(engine *web.RoutesGroup) { + resourceName := "ban" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "封禁管理")) +} + +func (srv *Server) rolemail(engine *web.RoutesGroup) { + resourceName := "rolemail" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "个人邮件")) +} + +func (srv *Server) globalmail(engine *web.RoutesGroup) { + resourceName := "globalmail" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "全服邮件")) +} + +func (srv *Server) account(engine *web.RoutesGroup) { + resourceName := "account" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "账号管理")) +} + +func (srv *Server) role(engine *web.RoutesGroup) { + resourceName := "role" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "角色管理")) +} + +func (srv *Server) order(engine *web.RoutesGroup) { + resourceName := "order" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "订单管理")) +} + +func (srv *Server) notice(engine *web.RoutesGroup) { + resourceName := "notice" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "公告管理")) +} + +func (srv *Server) rewardcode(engine *web.RoutesGroup) { + resourceName := "rewardcode" + srv.registerResourceRouter(resourceName, engine.Group("/"+resourceName, "奖励码")) +} + +func (srv *Server) devicepush(engine *web.RoutesGroup) { + resourceName := "devicepush" + 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 *WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error { + return func(ctx *WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error { + return ctl.CommonList(ctx, resourceName, params, rsp) + } +} + +func commonHandlerPost(ctl *controller, resourceName string) func(ctx *WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error { + return func(ctx *WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error { + return ctl.CommonPost(ctx, resourceName, params, rsp) + } +} + +func commonHandlerPut(ctl *controller, resourceName string) func(ctx *WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error { + return func(ctx *WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error { + return ctl.CommonPut(ctx, resourceName, params, rsp) + } +} + +func commonHandlerDelete(ctl *controller, resourceName string) func(ctx *WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { + return func(ctx *WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { + return ctl.CommonDelete(ctx, resourceName, params, rsp) + } +} diff --git a/admin/apps/mockpro/server/route_desc.go b/admin/apps/mockpro/server/route_desc.go new file mode 100644 index 0000000..fbdcd38 --- /dev/null +++ b/admin/apps/mockpro/server/route_desc.go @@ -0,0 +1,48 @@ +package server + +import ( + "admin/apps/mockpro/internal/model/dto" + "admin/lib/web" +) + +type PathInfo struct { + Path string `json:"path"` + Method string `json:"method"` +} +type ResourceInfo struct { + Desc string `json:"desc"` + Paths []*PathInfo `json:"paths"` +} +type CmdListRsp struct { + List []*ResourceInfo `json:"list"` +} + +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) { + find := false + for _, v := range paths { + if v.Desc == parentDesc { + v.Paths = append(v.Paths, &PathInfo{ + Path: path, + Method: method, + }) + find = true + break + } + } + if !find { + paths = append(paths, &ResourceInfo{ + Desc: parentDesc, + Paths: []*PathInfo{ + &PathInfo{ + Path: path, + Method: method, + }, + }, + }) + } + }) + rsp.List = paths + return nil +} diff --git a/admin/apps/mockpro/server/server.go b/admin/apps/mockpro/server/server.go new file mode 100644 index 0000000..441fdf5 --- /dev/null +++ b/admin/apps/mockpro/server/server.go @@ -0,0 +1,29 @@ +package server + +import ( + "admin/apps/mockpro/internal/global" + "admin/apps/mockpro/service" + "admin/lib/web" +) + +type Server struct { + engine *web.Engine + svc *service.Service + ctl *controller +} + +func New(svc *service.Service) *Server { + engine := web.New(NewWebContext) + srv := &Server{ + engine: engine, + ctl: newController(svc), + } + srv.Route(engine) + go func() { + err := engine.Run(":" + global.GLOB_BOOT_FLAGS.ApiPort) + if err != nil { + panic(err) + } + }() + return srv +} diff --git a/admin/apps/mockpro/service/service.go b/admin/apps/mockpro/service/service.go new file mode 100644 index 0000000..8fc963d --- /dev/null +++ b/admin/apps/mockpro/service/service.go @@ -0,0 +1,68 @@ +package service + +import ( + "admin/apps/mockpro/domain" + dto2 "admin/apps/mockpro/internal/model/dto" + "context" + "gorm.io/gorm" +) + +type Service struct { + db *gorm.DB + whitelistSvc *domain.WhiteListSvc + banSvc *domain.BanSvc +} + +func New(db *gorm.DB) *Service { + return &Service{ + db: db, + whitelistSvc: domain.NewWhiteListSvc(db), + banSvc: domain.NewBanSvc(db), + } +} + +func (svc *Service) CommonList(ctx context.Context, resourceName string, params *dto2.CommonListReq) (*dto2.CommonDtoList, error) { + restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName) + if err != nil { + return nil, err + } + dtoFieldsDescInfo, list, err := restfulDomainSvc.List(params.PageNo, params.PageLen) + if err != nil { + return nil, err + } + retList := make([]dto2.CommonDtoValues, 0, len(list)) + for _, v := range list { + retList = append(retList, v.ToCommonDto()) + } + return &dto2.CommonDtoList{FieldsDesc: dtoFieldsDescInfo, Rows: retList}, nil +} + +func (svc *Service) CommonPost(ctx context.Context, resourceName string, params dto2.CommonDtoValues) (dto2.CommonDtoValues, error) { + restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName) + if err != nil { + return nil, err + } + et, err := restfulDomainSvc.Post(params) + if err != nil { + return nil, err + } + return et.ToCommonDto(), nil +} +func (svc *Service) CommonPut(ctx context.Context, resourceName string, params dto2.CommonDtoValues) (dto2.CommonDtoValues, error) { + restfulDomainSvc, err := domain.FindRestfulResourceSvc(resourceName) + if err != nil { + return nil, err + } + et, err := restfulDomainSvc.Put(params) + if err != nil { + return nil, err + } + return et.ToCommonDto(), nil +} +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) +} diff --git a/admin/cmd/all_in_one/main.go b/admin/cmd/all_in_one/main.go index c073490..ea3a78b 100644 --- a/admin/cmd/all_in_one/main.go +++ b/admin/cmd/all_in_one/main.go @@ -2,6 +2,7 @@ package main import ( "admin/apps/game" + "admin/apps/mockpro" "admin/internal/mynode" "admin/lib/node" ) @@ -11,6 +12,7 @@ var appList []*node.ApplicationDescInfo func init() { appList = []*node.ApplicationDescInfo{ game.New(), + mockpro.New(), } } diff --git a/admin/internal/context/ctx_web.go b/admin/internal/context/ctx_web.go index 8ae820b..1ebfa59 100644 --- a/admin/internal/context/ctx_web.go +++ b/admin/internal/context/ctx_web.go @@ -3,41 +3,55 @@ package context import ( "admin/internal/errcode" "admin/lib/web" + "admin/lib/xlog" "context" + "github.com/gin-gonic/gin" ) type WebContext struct { context.Context - rawCtx web.RawContext + rawCtx *gin.Context + alreadySetRsp bool } -func NewWebContext() web.Context { - return &WebContext{} +func NewWebContext(rawCtx *gin.Context) web.IContext { + return &WebContext{rawCtx: rawCtx} } func (ctx *WebContext) Ok(data any) { - ctx.rawCtx.Json(200, map[string]any{ + if ctx.alreadySetRsp { + return + } + + ctx.rawCtx.JSON(200, map[string]any{ "code": 200, "msg": "", "data": data, }) + + ctx.alreadySetRsp = true } func (ctx *WebContext) Fail(err error) { + if ctx.alreadySetRsp { + return + } + code, stack, msg := errcode.ParseError(err) - ctx.rawCtx.Json(200, map[string]any{ + ctx.rawCtx.JSON(200, map[string]any{ "code": code, "stack": stack, "msg": msg, "data": "", }) + + ctx.alreadySetRsp = true } -func (ctx *WebContext) SetRawContext(rawCtx web.RawContext) { - ctx.Context = context.Background() - ctx.rawCtx = rawCtx +func (ctx *WebContext) HandleError(path string, err error) { + xlog.Errorf("path:%v handle error:%v ", path, err) + ctx.Fail(err) } - -func (ctx *WebContext) GetRawContext() web.RawContext { - return ctx.rawCtx +func (ctx *WebContext) HandleSuccess(rspData any) { + ctx.Ok(rspData) } diff --git a/admin/internal/db/db.go b/admin/internal/db/db.go index 4426b9b..6a4a445 100644 --- a/admin/internal/db/db.go +++ b/admin/internal/db/db.go @@ -118,7 +118,7 @@ func tryCreateDB(dbName, dsn string) (string, error) { db.Raw("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?", dbName).Scan(&count) if count == 0 { // 数据库不存在,创建它 - sql := fmt.Sprintf(`create database if not exists %s default charset utf8mb4 collate utf8mb4_unicode_ci`, + sql := fmt.Sprintf("create database if not exists `%s` default charset utf8mb4 collate utf8mb4_unicode_ci", dbName) if e := db.Exec(sql).Error; e != nil { return "", fmt.Errorf("failed to create database:%v", e) diff --git a/admin/internal/global/consts.go b/admin/internal/global/consts.go new file mode 100644 index 0000000..3d58b45 --- /dev/null +++ b/admin/internal/global/consts.go @@ -0,0 +1,6 @@ +package global + +const ( + WebPathPermit_Read = 1 + WebPathPermit_Write = 2 +) diff --git a/admin/internal/mynode/node.go b/admin/internal/mynode/node.go index f239c3e..fc4803f 100644 --- a/admin/internal/mynode/node.go +++ b/admin/internal/mynode/node.go @@ -14,7 +14,7 @@ func New() *node.Node { nd.ApplyOptions(node.WithNodeExBootFlags(global.GLOB_BOOT_FLAGS)) nd.AddInitTask("初始化全局api监听服务", func() error { - global.GLOB_API_ENGINE = web.NewEngine("gin", context.NewWebContext) + global.GLOB_API_ENGINE = web.New(context.NewWebContext) return nil }) diff --git a/admin/lib/httpclient/client.go b/admin/lib/httpclient/client.go new file mode 100644 index 0000000..6330346 --- /dev/null +++ b/admin/lib/httpclient/client.go @@ -0,0 +1,73 @@ +package httpclient + +import ( + "admin/internal/errcode" + "admin/lib/xlog" + "encoding/json" + "io" + "net/http" + "strings" + "time" +) + +func Request(addr string, method string, body interface{}, resData interface{}) error { + removeUrl := checkUrl(addr) + + //xlog.Debugf("-->req url: %v", removeUrl) + var res *http.Response + var err error + if method != "get" { + var bodyReader io.Reader + if body != nil { + bodyBytes, err := json.Marshal(body) + if err != nil { + xlog.Warnf(err) + return errcode.New(errcode.ServerError, "数据解析失败:%v", err) + } + bodyReader = strings.NewReader(string(bodyBytes)) + } + client := &http.Client{ + Timeout: time.Second * 3, + } + req, err := http.NewRequest(method, removeUrl, bodyReader) + if err != nil { + xlog.Warnf(err) + return errcode.New(errcode.ServerError, "数据请求创建失败:%v", err) + } + + res, err = client.Do(req) + if err != nil { + xlog.Warnf(err) + return errcode.New(errcode.ServerError, "数据请求失败:%v", err) + } + } else { + res, err = http.Get(removeUrl) + } + + // 获取数据 + defer res.Body.Close() + + resBody, err := io.ReadAll(res.Body) + if err != nil { + xlog.Warnf(err) + return errcode.New(errcode.ServerError, "数据解析失败:%v", err) + } + + 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) + } + + //xlog.Debugf("%+v", resData) + + return nil +} + +func checkUrl(u string) string { + if strings.Contains(u, `http://`) || strings.Contains(u, `https://`) { + return u + } else { + return `http://` + u + } +} diff --git a/admin/lib/node/node_init.go b/admin/lib/node/node_init.go index f3bcf38..a5eae47 100644 --- a/admin/lib/node/node_init.go +++ b/admin/lib/node/node_init.go @@ -112,7 +112,7 @@ func (node *Node) initBootConfig() error { } func (node *Node) initLog() error { - logConfig := &LogBootConfig{} + logConfig := DefaultLogBootConfig() if node.bootConfigFile.globalBootConfigFileContent != nil { logConfig = node.bootConfigFile.globalBootConfigFileContent.GetLogConfig().Check() } diff --git a/admin/lib/node/node_log.go b/admin/lib/node/node_log.go index 0acbc4d..2736154 100644 --- a/admin/lib/node/node_log.go +++ b/admin/lib/node/node_log.go @@ -16,6 +16,12 @@ type LogBootConfig struct { LogLevel string `yaml:"log_level"` // 日志等级: trace/debug/info/notice/warn/error/fatal } +func DefaultLogBootConfig() *LogBootConfig { + return &LogBootConfig{ + LogLevel: "trace", + } +} + func (lc *LogBootConfig) Check() *LogBootConfig { if lc.LogDir == "" { lc.LogDir = "./logs" diff --git a/admin/lib/web/desc.go b/admin/lib/web/desc.go new file mode 100644 index 0000000..ef6cacb --- /dev/null +++ b/admin/lib/web/desc.go @@ -0,0 +1,57 @@ +package web + +type routesPath struct { + method string // get|post|put|delete + desc string + permit int + handlers []HandlerFunc +} + +type routesNode struct { + path string + methods []*routesPath + child []*routesNode +} + +func newRoutesNode(method, path string, desc string, permit int, handlers ...HandlerFunc) *routesNode { + n := &routesNode{ + path: path, + } + n.addMethod(method, desc, permit, handlers) + return n +} + +func (n *routesNode) addChildren(method, path string, desc string, permit int, handlers ...HandlerFunc) *routesNode { + for _, c := range n.child { + if c.path == path { + c.addMethod(method, desc, permit, handlers) + return c + } + } + + children := newRoutesNode(method, path, desc, permit, handlers) + n.child = append(n.child, children) + return children +} + +func (n *routesNode) addMethod(method string, desc string, permit int, handlers ...HandlerFunc) *routesNode { + n.methods = append(n.methods, &routesPath{method: method, desc: desc, permit: permit, handlers: handlers}) + return n +} + +func (n *routesNode) travel(parentPath string, parentDesc string, travelFun func(parentDesc string, path string, method string, handlers ...HandlerFunc)) { + curPath := parentPath + n.path + curNodeDesc := "" + if len(n.methods) > 0 { + for _, method := range n.methods { + if method.method == "" { + curNodeDesc = method.desc + continue + } + travelFun(curPath, parentDesc, method.method, method.handlers) + } + } + for _, c := range n.child { + c.travel(curPath, curNodeDesc, travelFun) + } +} diff --git a/admin/lib/web/engine.go b/admin/lib/web/engine.go new file mode 100644 index 0000000..44196ab --- /dev/null +++ b/admin/lib/web/engine.go @@ -0,0 +1,34 @@ +package web + +import "github.com/gin-gonic/gin" + +type Engine struct { + *RoutesGroup + newContextFun func(ctx *gin.Context) IContext + rawEngine *gin.Engine +} + +func New(newContextFun func(ctx *gin.Context) IContext) *Engine { + rawE := gin.Default() + e := &Engine{ + RoutesGroup: newRoutesGroup(&rawE.RouterGroup, newContextFun), + newContextFun: newContextFun, + rawEngine: rawE, + } + + return e +} + +func (e *Engine) Use(middlewares ...HandlerFunc) { + e.rawEngine.Use(getGinHandlerFunWithRequest(e.newContextFun, middlewares...)...) +} + +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)) { + for _, c := range e.RoutesGroup.node.child { + c.travel("", "", travelFun) + } +} diff --git a/admin/lib/web/example/example.go b/admin/lib/web/example/example.go deleted file mode 100644 index 4d27ae7..0000000 --- a/admin/lib/web/example/example.go +++ /dev/null @@ -1,212 +0,0 @@ -package main - -import ( - "admin/lib/web" - "fmt" - "html/template" - "sort" -) - -type MyContext struct { - rawCtx web.RawContext -} - -func (ctx *MyContext) SetRawContext(rawCtx web.RawContext) { - ctx.rawCtx = rawCtx -} -func (ctx *MyContext) GetRawContext() web.RawContext { - return ctx.rawCtx -} - -func main() { - coreRouter := "iris" // gin|iris - engine := web.NewEngine(":8888", coreRouter, func() web.Context { - return new(MyContext) - }) - groupV1 := engine.Group("/v1") - { - type Test1 struct { - RoleId string `json:"role_id" desc:"角色id字段" required:"true"` - F1 int `json:"f1" desc:"这是字段1" default:"324"` - F2 string `json:"f2" desc:"这是字段2" default:"abcd"` - F3 bool `json:"f3" desc:"这是字段3" default:"false"` - } - groupV1.Get("/test1", "设置玩家背包数据", web.AccessMode_Write, Test1{}, func(ctx *MyContext, params *Test1) { - fmt.Printf("receive test1 path params:%+v\n", params) - ctx.GetRawContext().Json(200, map[string]any{ - "msg": "ok", - "code": 200, - }) - }) - groupV1.Post("/test1", "获取玩家数据", web.AccessMode_Read, Test1{}, func(ctx *MyContext, params *Test1) { - fmt.Printf("receive test1 path params:%+v\n", params) - ctx.GetRawContext().Json(200, map[string]any{ - "msg": "ok", - "code": 200, - }) - }) - // URI --> :8888/v1/test1?f1=123&f2=sdfsd&f3=true - // BODY --> :8888/v1/test1 {"f1":123,"f2":"sdfds","f3":"true"} - } - engine.Get("/test2", "获取玩家比赛", web.AccessMode_Read, nil, func(ctx *MyContext) { - fmt.Printf("receive test2 request\n") - ctx.GetRawContext().Json(200, map[string]any{ - "msg": "ok", - "code": 200, - }) - }) - engine.Post("/test2", "测试gm指令名字", web.AccessMode_Read, nil, func(ctx *MyContext) { - fmt.Printf("receive test2 request\n") - ctx.GetRawContext().Json(200, map[string]any{ - "msg": "ok", - "code": 200, - }) - }) - groupV2 := engine.Group("v2") - { - type Test2 struct { - F1 int `json:"f1" desc:"这是字段1"` - F2 string `json:"f2" desc:"这是字段2"` - F3 bool `json:"f3" desc:"这是字段3"` - } - groupV2.Post("test3", "测试gm指令名字123", web.AccessMode_Write, Test2{}, func(ctx *MyContext, params *Test2) { - fmt.Printf("receive test3\n") - ctx.GetRawContext().Json(200, map[string]any{ - "msg": "ok", - "code": 200, - }) - }) - } - - engine.Get("path_tree", "测试gm指令名字3424", web.AccessMode_Write, nil, func(ctx *MyContext) { - tree := engine.TravelPathTree() - ctx.GetRawContext().Json(200, tree) - }) - - engine.Get("/grid", "获取指令描述表", web.AccessMode_Read, nil, func(c *MyContext) { - tree := &Tree{tree: engine.TravelPathTree()} - - tmpl, err := template.New("html_test").Funcs(template.FuncMap(map[string]any{ - "incr": incr, - })).Parse(tplText) - if err != nil { - panic(err) - } - err = tmpl.Execute(c.GetRawContext().Writer(), tree) - if err != nil { - panic(err) - } - }) - - err := engine.Run() - if err != nil { - panic(err) - } -} - -type Path struct { - Path string - Route *web.RouteDescInfo -} - -type Tree struct { - tree map[string]*web.RouteDescInfo -} - -func (t *Tree) Paths() [][]*Path { - splitCol := 4 - list := make([]*Path, 0) - for k, v := range t.tree { - list = append(list, &Path{Path: k, Route: v}) - } - sort.SliceStable(list, func(i, j int) bool { - return list[i].Path < list[j].Path - }) - - if len(list) <= splitCol { - return [][]*Path{list} - } - - section := len(list) / splitCol - paths := make([][]*Path, splitCol) - - for i := 0; i < splitCol; i++ { - paths[i] = make([]*Path, 0) - start := i * section - for j := 0; j < section; j++ { - start += j - paths[i] = append(paths[i], list[start]) - } - } - - if len(list)%splitCol > 0 { - idx := 0 - for i := len(list) - len(list)%splitCol; i < len(list); i++ { - paths[idx] = append(paths[idx], list[i]) - idx++ - } - } - - return paths -} - -func incr(src int) int { - return src + 1 -} - -var tplText = ` - - - - - GM MASTER -
- {{ $pathsList := .Paths }} - {{ range $pidex, $paths := $pathsList }} -
-

指令列表{{ incr $pidex }}

-
- - {{ range $idx, $path := $paths }} - - - - - - - - - - - - {{ if $path.Route.HasRequest }} - {{ range $idx1, $field := $path.Route.Fields }} - - - - - - {{ if eq $field.Default "" }} - - {{ else }} - - {{ end }} - - {{ end }} - {{ else }} - - {{ end }} - - {{ end }} -
{{ $path.Route.Desc }}
请求路径  {{ $path.Path }}
方法  {{ $path.Route.MethodDesc "或" }}
{{ printf "参数%d" $idx1 }}{{printf "%s" $field.Name }}{{printf "%s" $field.Type }}{{printf "%s" $field.Desc }}{{printf "默认:无" }}{{printf "默认:%s" $field.Default }}
无需参数
-
-
- {{ end }} -
- - - - - - -` diff --git a/admin/lib/web/example/main.go b/admin/lib/web/example/main.go new file mode 100644 index 0000000..f7ec372 --- /dev/null +++ b/admin/lib/web/example/main.go @@ -0,0 +1 @@ +package example diff --git a/admin/lib/web/group.go b/admin/lib/web/group.go deleted file mode 100644 index 62b41b7..0000000 --- a/admin/lib/web/group.go +++ /dev/null @@ -1,183 +0,0 @@ -package web - -type routeGroupInterface interface { - Use(middlewares ...HandlerFunc) - Group(path string, handlers ...HandlerFunc) routeGroupInterface - Get(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface - Post(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface - Put(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface - Delete(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface -} - -type Group struct { - coreRouter routeGroupInterface - DirectRoutes map[string]*RouteDescInfo - GroupRoutes map[string]*Group -} - -func newGroup(coreRouter routeGroupInterface) *Group { - g := &Group{ - coreRouter: coreRouter, - DirectRoutes: make(map[string]*RouteDescInfo), - GroupRoutes: make(map[string]*Group), - } - return g -} - -func (e *Group) Use(middlewares ...HandlerFunc) { - e.coreRouter.Use(middlewares...) -} - -func (e *Group) Group(path string, handlers ...HandlerFunc) *Group { - path = pathBeTheSame(path) - routeGroup := e.coreRouter.Group(path, handlers...) - group := newGroup(routeGroup) - e.GroupRoutes[path] = group - return group -} - -// Get 注册get方法路由,根据request请求体优先从body中以json格式解析参数,如果没有body,则从uri参数中解析出请求参数 -// -// path:路径 -// desc:路由的一个简短描述 -// request:请求结构体 -// 格式: -// type struct { -// F1 int `json:"f1" desc:"字段描述" default:"234" required:"true"` -// } -// tag描述: -// json:字段名 -// desc:字段简短描述,没有可以不写 -// default:默认值,没有可以不写 -// required:是否必填字段,没有要求可以不写 -// 注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -// 例如: -// type Field struct { -// A int -// B bool -// } -// type Request struct { -// F1 *Field -// } -// F1字段就是非法的,无法解析,会报错 -// handlers:路由处理函数,如果没有请求体,就是func(ctx),否则就是func(ctx, request) -func (e *Group) Get(path string, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "GET", access, request) - } else { - old.Method = append(old.Method, "GET") - e.DirectRoutes[path] = old - } - e.coreRouter.Get(path, desc, request, handler) -} - -// Post 注册post方法路由,根据request请求体优先从body中以json格式解析参数,如果没有body,则从uri参数中解析出请求参数 -// -// path:路径 -// desc:路由的一个简短描述 -// request:请求结构体 -// 格式: -// type struct { -// F1 int `json:"f1" desc:"字段描述" default:"234" required:"true"` -// } -// tag描述: -// json:字段名 -// desc:字段简短描述,没有可以不写 -// default:默认值,没有可以不写 -// required:是否必填字段,没有要求可以不写 -// 注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -// 例如: -// type Field struct { -// A int -// B bool -// } -// type Request struct { -// F1 *Field -// } -// F1字段就是非法的,无法解析,会报错 -// handlers:路由处理函数,如果没有请求体,就是func(ctx),否则就是func(ctx, request) -func (e *Group) Post(path, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "POST", access, request) - } else { - old.Method = append(old.Method, "POST") - e.DirectRoutes[path] = old - } - e.coreRouter.Post(path, desc, request, handler) -} - -func (e *Group) Put(path, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "PUT", access, request) - } else { - old.Method = append(old.Method, "PUT") - e.DirectRoutes[path] = old - } - e.coreRouter.Put(path, desc, request, handler) -} - -func (e *Group) Delete(path, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "DELETE", access, request) - } else { - old.Method = append(old.Method, "DELETE") - e.DirectRoutes[path] = old - } - e.coreRouter.Delete(path, desc, request, handler) -} - -// GetAndPost 注册get&post方法路由,根据request请求体优先从body中以json格式解析参数,如果没有body,则从uri参数中解析出请求参数 -// -// path:路径 -// desc:路由的一个简短描述 -// request:请求结构体 -// 格式: -// type struct { -// F1 int `json:"f1" desc:"字段描述" default:"234" required:"true"` -// } -// tag描述: -// json:字段名 -// desc:字段简短描述,没有可以不写 -// default:默认值,没有可以不写 -// required:是否必填字段,没有要求可以不写 -// 注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -// 例如: -// type Field struct { -// A int -// B bool -// } -// type Request struct { -// F1 *Field -// } -// F1字段就是非法的,无法解析,会报错 -// handlers:路由处理函数,如果没有请求体,就是func(ctx),否则就是func(ctx, request) -func (e *Group) GetAndPost(path, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - record := newRouteDescInfo(path, desc, "GET", access, request) - record.Method = append(record.Method, "POST") - e.DirectRoutes[path] = record - e.coreRouter.Get(path, desc, request, handler) - e.coreRouter.Post(path, desc, request, handler) -} - -func (e *Group) TravelPathTree() map[string]*RouteDescInfo { - m := make(map[string]*RouteDescInfo) - for k, route := range e.DirectRoutes { - m[k] = route - } - for k, subG := range e.GroupRoutes { - gm := subG.TravelPathTree() - for k1, v1 := range gm { - m[k+k1] = v1 - } - } - return m -} diff --git a/admin/lib/web/model.go b/admin/lib/web/model.go deleted file mode 100644 index 59f9837..0000000 --- a/admin/lib/web/model.go +++ /dev/null @@ -1,24 +0,0 @@ -package web - -import ( - "fmt" - "io" -) - -type Context interface { - SetRawContext(ctx RawContext) - GetRawContext() RawContext -} - -type HandlerFunc any - -type RawContext interface { - Json(code int, v any) - Writer() io.Writer -} - -var ParseParamsErrorHandler = func(ctx Context, path string, err error) { - ctx.GetRawContext().Json(501, map[string]interface{}{ - "MSG": fmt.Sprintf("parse params error:%v", err), - }) -} diff --git a/admin/lib/web/readme.txt b/admin/lib/web/readme.txt deleted file mode 100644 index 3d01ed6..0000000 --- a/admin/lib/web/readme.txt +++ /dev/null @@ -1,12 +0,0 @@ - - -注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -例如: -type Field struct { - A int - B bool -} -type Request struct { - F1 *Field -} -F1字段就是非法的,无法解析,会报错 \ No newline at end of file diff --git a/admin/lib/web/router.go b/admin/lib/web/router.go deleted file mode 100644 index 16180a5..0000000 --- a/admin/lib/web/router.go +++ /dev/null @@ -1,173 +0,0 @@ -package web - -import "fmt" - -type routerInterface interface { - routeGroupInterface - Run(addr string) error -} - -type Engine struct { - Addr string - newContextFun func() Context - DirectRoutes map[string]*RouteDescInfo - GroupRoutes map[string]*Group - coreRouter routerInterface -} - -// NewEngine 使用gin或者iris创建web引擎,newContextFun为调用方需要实现的context初始化函数 -// coreRouter指定不同web引擎,目前只支持gin -func NewEngine(coreRouter string, newContextFun func() Context) *Engine { - e := &Engine{ - newContextFun: newContextFun, - DirectRoutes: make(map[string]*RouteDescInfo), - GroupRoutes: make(map[string]*Group), - } - switch coreRouter { - //case "iris": - // e.coreRouter = newRouterIris(newContextFun) - case "gin": - e.coreRouter = newRouterGin(newContextFun) - default: - panic(fmt.Errorf("NewEngine only support irir or gin, invalid type:%v", coreRouter)) - } - return e -} - -func (e *Engine) Use(middlewares ...HandlerFunc) { - e.coreRouter.Use(middlewares...) -} - -func (e *Engine) Group(path string, handlers ...HandlerFunc) *Group { - path = pathBeTheSame(path) - routeGroup := e.coreRouter.Group(path, handlers...) - group := newGroup(routeGroup) - e.GroupRoutes[path] = group - return group -} - -// Get 注册get方法路由,根据request请求体优先从body中以json格式解析参数,如果没有body,则从uri参数中解析出请求参数 -// -// path:路径 -// desc:路由的一个简短描述 -// request:请求结构体 -// 格式: -// type struct { -// F1 int `json:"f1" desc:"字段描述" default:"234" required:"true"` -// } -// tag描述: -// json:字段名 -// desc:字段简短描述,没有可以不写 -// default:默认值,没有可以不写 -// required:是否必填字段,没有要求可以不写 -// 注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -// 例如: -// type Field struct { -// A int -// B bool -// } -// type Request struct { -// F1 *Field -// } -// F1字段就是非法的,无法解析,会报错 -// handlers:路由处理函数,如果没有请求体,就是func(ctx),否则就是func(ctx, request) -func (e *Engine) Get(path string, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "GET", access, request) - } else { - old.Method = append(old.Method, "GET") - e.DirectRoutes[path] = old - } - e.coreRouter.Get(path, desc, request, handler) -} - -// Post 注册post方法路由,根据request请求体优先从body中以json格式解析参数,如果没有body,则从uri参数中解析出请求参数 -// -// path:路径 -// desc:路由的一个简短描述 -// request:请求结构体 -// 格式: -// type struct { -// F1 int `json:"f1" desc:"字段描述" default:"234" required:"true"` -// } -// tag描述: -// json:字段名 -// desc:字段简短描述,没有可以不写 -// default:默认值,没有可以不写 -// required:是否必填字段,没有要求可以不写 -// 注意,get、post注册的request结构字段,如果是uri参数方式类型只支持golang基础类型以及基础类型的切片,不能是结构体类型, -// 例如: -// type Field struct { -// A int -// B bool -// } -// type Request struct { -// F1 *Field -// } -// F1字段就是非法的,无法解析,会报错 -// handlers:路由处理函数,如果没有请求体,就是func(ctx),否则就是func(ctx, request) -func (e *Engine) Post(path, desc string, access AccessMode, request any, handler HandlerFunc) { - path = pathBeTheSame(path) - old, find := e.DirectRoutes[path] - if !find { - e.DirectRoutes[path] = newRouteDescInfo(path, desc, "POST", access, request) - } else { - old.Method = append(old.Method, "POST") - e.DirectRoutes[path] = old - } - e.coreRouter.Post(path, desc, request, handler) -} - -func (e *Engine) Run(addr string) error { - e.Addr = addr - return e.coreRouter.Run(addr) -} - -// TravelPathTree 获取所有路径的描述表 -func (e *Engine) TravelPathTree() map[string]*RouteDescInfo { - m := make(map[string]*RouteDescInfo) - for k, route := range e.DirectRoutes { - m[k] = route - } - for k, subG := range e.GroupRoutes { - gm := subG.TravelPathTree() - for k1, v1 := range gm { - m[k+k1] = v1 - } - } - return m -} - -func pathBeTheSame(path string) string { - if path == "" { - return "" - } - if path == "/" { - return path - } - if path[0] != '/' { - path = "/" + path - } - if path[len(path)-1] == '/' { - path = path[:len(path)-1] - } - return path -} - -func pathBeTheSame1(path string) string { - if path == "" { - return "" - } - if path == "/" { - return path - } - if path[0] != '/' { - path = "/" + path - } - if path[len(path)-1] == '/' { - path = path[:len(path)-1] - } - return path -} diff --git a/admin/lib/web/router_desc.go b/admin/lib/web/router_desc.go deleted file mode 100644 index da509c9..0000000 --- a/admin/lib/web/router_desc.go +++ /dev/null @@ -1,85 +0,0 @@ -package web - -import ( - "reflect" - "strings" -) - -type AccessMode int - -const ( - AccessMode_Read = 1 - AccessMode_Write = 2 -) - -type RouteDescInfo struct { - Path string - Desc string - Method []string - AccessMode - rawRequest any - Fields []*FieldDescInfo -} - -func newRouteDescInfo(path, desc, method string, access AccessMode, request any) *RouteDescInfo { - info := &RouteDescInfo{ - Path: path, - Desc: desc, - Method: []string{method}, - AccessMode: access, - rawRequest: request, - } - info.RequestFieldsDesc() - return info -} - -func (info *RouteDescInfo) MethodDesc(sep string) string { - return strings.Join(info.Method, sep) -} - -func (info *RouteDescInfo) RequestFieldsDesc() []*FieldDescInfo { - if !info.HasRequest() { - return []*FieldDescInfo{} - } - - list := make([]*FieldDescInfo, 0) - to := reflect.TypeOf(info.rawRequest) - if to.Kind() == reflect.Ptr { - to = to.Elem() - } - for i := 0; i < to.NumField(); i++ { - field := to.Field(i) - list = append(list, newFieldDescInfo(field)) - } - info.Fields = list - return list -} - -func (info *RouteDescInfo) HasRequest() bool { - return info.rawRequest != nil -} - -type FieldDescInfo struct { - Name string - FieldName string - Type string - Desc string - Required bool - Default string - rawTypeOfField reflect.StructField -} - -func newFieldDescInfo(field reflect.StructField) *FieldDescInfo { - desc := &FieldDescInfo{ - Name: field.Tag.Get("json"), - FieldName: field.Name, - Type: field.Type.String(), - Desc: field.Tag.Get("desc"), - rawTypeOfField: field, - } - if field.Tag.Get("required") == "true" { - desc.Required = true - } - desc.Default = desc.rawTypeOfField.Tag.Get("default") - return desc -} diff --git a/admin/lib/web/router_gin.go b/admin/lib/web/router_gin.go deleted file mode 100644 index f0bfe1b..0000000 --- a/admin/lib/web/router_gin.go +++ /dev/null @@ -1,303 +0,0 @@ -package web - -import ( - "encoding/json" - "fmt" - "github.com/gin-gonic/gin" - "io" - "reflect" - "strconv" - "strings" -) - -type routerGroupGin struct { - group *gin.RouterGroup - newContextFun func() Context -} - -func (group *routerGroupGin) Use(middlewares ...HandlerFunc) { - group.group.Use(getGinHandlerFun(group.newContextFun, middlewares...)...) -} -func (group *routerGroupGin) Group(path string, handlers ...HandlerFunc) routeGroupInterface { - ginGroup := group.group.Group(path, getGinHandlerFun(group.newContextFun, handlers...)...) - group1 := &routerGroupGin{group: ginGroup, newContextFun: group.newContextFun} - return group1 -} -func (group *routerGroupGin) Get(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - group.group.GET(path, getGinHandlerFun(group.newContextFun, handlers...)...) - } else { - group.group.GET(path, getGinHandlerFunWithRequest(group.newContextFun, req, handlers...)...) - } - return group -} -func (group *routerGroupGin) Post(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - group.group.POST(path, getGinHandlerFun(group.newContextFun, handlers...)...) - } else { - group.group.POST(path, getGinHandlerFunWithRequest(group.newContextFun, req, handlers...)...) - } - return group -} - -func (group *routerGroupGin) Put(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - group.group.PUT(path, getGinHandlerFun(group.newContextFun, handlers...)...) - } else { - group.group.PUT(path, getGinHandlerFunWithRequest(group.newContextFun, req, handlers...)...) - } - return group -} - -func (group *routerGroupGin) Delete(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - group.group.DELETE(path, getGinHandlerFun(group.newContextFun, handlers...)...) - } else { - group.group.DELETE(path, getGinHandlerFunWithRequest(group.newContextFun, req, handlers...)...) - } - return group -} - -type routerGin struct { - engine *gin.Engine - newContextFun func() Context -} - -func newRouterGin(newContextFun func() Context) routerInterface { - engine := gin.Default() - router := &routerGin{ - engine: engine, - newContextFun: newContextFun, - } - return router -} - -func (router *routerGin) Use(middlewares ...HandlerFunc) { - router.engine.Use(getGinHandlerFun(router.newContextFun, middlewares...)...) -} -func (router *routerGin) Group(path string, handlers ...HandlerFunc) routeGroupInterface { - ginGroup := router.engine.Group(path, getGinHandlerFun(router.newContextFun, handlers...)...) - group := &routerGroupGin{ - group: ginGroup, - newContextFun: router.newContextFun, - } - return group -} -func (router *routerGin) Get(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - router.engine.GET(path, getGinHandlerFun(router.newContextFun, handlers...)...) - } else { - router.engine.GET(path, getGinHandlerFunWithRequest(router.newContextFun, req, handlers...)...) - } - return router -} -func (router *routerGin) Post(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - router.engine.POST(path, getGinHandlerFun(router.newContextFun, handlers...)...) - } else { - router.engine.POST(path, getGinHandlerFunWithRequest(router.newContextFun, req, handlers...)...) - } - return router -} - -func (router *routerGin) Put(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - router.engine.PUT(path, getGinHandlerFun(router.newContextFun, handlers...)...) - } else { - router.engine.PUT(path, getGinHandlerFunWithRequest(router.newContextFun, req, handlers...)...) - } - return router -} - -func (router *routerGin) Delete(path string, desc string, req any, handlers ...HandlerFunc) routeGroupInterface { - if req == nil { - router.engine.DELETE(path, getGinHandlerFun(router.newContextFun, handlers...)...) - } else { - router.engine.DELETE(path, getGinHandlerFunWithRequest(router.newContextFun, req, handlers...)...) - } - return router -} - -func getGinHandlerFun(newContextFun func() Context, handlers ...HandlerFunc) []gin.HandlerFunc { - list := make([]gin.HandlerFunc, 0, len(handlers)) - for _, handler := range handlers { - list = append(list, func(rawCtx *gin.Context) { - rawCtx1 := &ginCtx{ctx: rawCtx} - ctx := newContextFun() - ctx.SetRawContext(rawCtx1) - reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(ctx)}) - if rawCtx.IsAborted() { - return - } - }) - } - return list -} - -func getGinHandlerFunWithRequest(newContextFun func() Context, requestTemplate any, handlers ...HandlerFunc) []gin.HandlerFunc { - list := make([]gin.HandlerFunc, 0, len(handlers)) - for _, handler := range handlers { - list = append(list, func(rawCtx *gin.Context) { - rawCtx1 := &ginCtx{ctx: rawCtx} - ctx := newContextFun() - ctx.SetRawContext(rawCtx1) - request, err := rawCtx1.parseRequest(requestTemplate) - if err != nil { - ParseParamsErrorHandler(ctx, rawCtx.Request.RequestURI, err) - return - } - reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(request)}) - }) - } - return list -} - -func (router *routerGin) Run(addr string) error { - return router.engine.Run(addr) -} - -type ginCtx struct { - ctx *gin.Context -} - -func (ctx *ginCtx) Json(code int, v any) { - ctx.ctx.JSON(code, v) -} - -func (ctx *ginCtx) Writer() io.Writer { - return ctx.ctx.Writer -} - -func (ctx *ginCtx) body() ([]byte, error) { - buf, err := io.ReadAll(ctx.ctx.Request.Body) - if err != nil { - return nil, fmt.Errorf("read body error:%v", err) - } - - return buf, nil -} - -func (ctx *ginCtx) parseRequest(request any) (any, error) { - if request == nil { - return nil, nil - } - - requestType := reflect.TypeOf(request) - newRequest := reflect.New(requestType).Interface() - - body, err := ctx.body() - if err != nil { - return nil, err - } - - if len(body) == 0 { - newRequestValue := reflect.ValueOf(newRequest).Elem() - newRequestValueType := newRequestValue.Type() - for i := 0; i < newRequestValue.NumField(); i++ { - f := newRequestValueType.Field(i) - fieldTagName := f.Tag.Get("json") - if fieldTagName == "" { - fieldTagName = f.Name - } - - field := newRequestValue.Field(i) - if !field.CanSet() { - continue - } - fieldStr := ctx.ctx.Query(fieldTagName) - err := setValue(field, fieldStr, f.Tag.Get("default"), f.Tag.Get("required")) - if err != nil { - return nil, fmt.Errorf("parse uri params field(%v) set value(%v) error:%v", f.Name, fieldStr, err) - } - } - } else { - err = json.Unmarshal(body, newRequest) - if err != nil { - return nil, fmt.Errorf("json unmarshal body error:%v", err) - } - } - return newRequest, nil -} - -// setValue 设置结构体一个字段的值 -func setValue(field reflect.Value, value string, defaultValue string, required string) error { - if value == "" { - value = defaultValue - } - if value == "" && required == "true" { - return fmt.Errorf("field is required, please give a valid value") - } - - if field.Kind() == reflect.Ptr { - if value == "" { - return nil - } - - if field.IsNil() { - field.Set(reflect.New(field.Type().Elem())) - } - field = field.Elem() - } - switch field.Kind() { - case reflect.String: - field.SetString(value) - case reflect.Bool: - if value == "" { - field.SetBool(false) - } else { - b, err := strconv.ParseBool(value) - if err != nil { - return err - } - field.SetBool(b) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if value == "" { - field.SetInt(0) - } else { - i, err := strconv.ParseInt(value, 0, field.Type().Bits()) - if err != nil { - return err - } - field.SetInt(i) - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if value == "" { - field.SetUint(0) - break - } - ui, err := strconv.ParseUint(value, 0, field.Type().Bits()) - if err != nil { - return err - } - field.SetUint(ui) - case reflect.Float32, reflect.Float64: - if value == "" { - field.SetFloat(0) - break - } - f, err := strconv.ParseFloat(value, field.Type().Bits()) - if err != nil { - return err - } - field.SetFloat(f) - case reflect.Struct: - return fmt.Errorf("unsupport struct field:%v", field.Type()) - case reflect.Slice: - values := strings.Split(value, ",") - if len(values) == 1 && values[0] == "" { - values = []string{} - } - field.Set(reflect.MakeSlice(field.Type(), len(values), len(values))) - for i := 0; i < len(values); i++ { - err := setValue(field.Index(i), values[i], "", "") - if err != nil { - return err - } - } - - default: - return fmt.Errorf("no support type %s", field.Type()) - } - return nil -} diff --git a/admin/lib/web/routes_group.go b/admin/lib/web/routes_group.go new file mode 100644 index 0000000..bbe5ba5 --- /dev/null +++ b/admin/lib/web/routes_group.go @@ -0,0 +1,228 @@ +package web + +import ( + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" + "io" + "reflect" + "strconv" + "strings" +) + +type IContext interface { + HandleError(path string, err error) + HandleSuccess(rspData any) +} + +type HandlerFunc any + +type RoutesGroup struct { + newContextFun func(ctx *gin.Context) IContext + rawGroup *gin.RouterGroup + node *routesNode +} + +func newRoutesGroup(rawGroup *gin.RouterGroup, newContextFun func(ctx *gin.Context) IContext) *RoutesGroup { + g := &RoutesGroup{ + rawGroup: rawGroup, + newContextFun: newContextFun, + node: &routesNode{}, + } + return g +} + +func (group *RoutesGroup) Group(path string, desc string, handlers ...HandlerFunc) *RoutesGroup { + rawGroup := group.rawGroup.Group(path, getGinHandlerFunWithRequest(group.newContextFun, handlers...)...) + nextRoutesGroup := newRoutesGroup(rawGroup, group.newContextFun) + nextGroupNode := group.addChildren("", path, desc, 0, handlers...) + nextRoutesGroup.node = nextGroupNode + return nextRoutesGroup +} + +func (group *RoutesGroup) Use(middlewares ...HandlerFunc) { + group.rawGroup.Use(getGinHandlerFunWithRequest(group.newContextFun, middlewares...)...) +} + +func (group *RoutesGroup) Get(path string, desc string, permit int, handlers ...HandlerFunc) { + group.rawGroup.GET(path, getGinHandlerFunWithRequest(group.newContextFun, handlers...)...) + group.addChildren("GET", path, desc, permit, handlers...) +} + +func (group *RoutesGroup) Post(path string, desc string, permit int, handlers ...HandlerFunc) { + group.rawGroup.POST(path, getGinHandlerFunWithRequest(group.newContextFun, handlers...)...) + group.addChildren("POST", path, desc, permit, handlers...) +} + +func (group *RoutesGroup) Put(path string, desc string, permit int, handlers ...HandlerFunc) { + group.rawGroup.PUT(path, getGinHandlerFunWithRequest(group.newContextFun, handlers...)...) + group.addChildren("PUT", path, desc, permit, handlers...) +} + +func (group *RoutesGroup) Delete(path string, desc string, permit int, handlers ...HandlerFunc) { + group.rawGroup.DELETE(path, getGinHandlerFunWithRequest(group.newContextFun, handlers...)...) + group.addChildren("DELETE", path, desc, permit, handlers...) +} + +func (group *RoutesGroup) addChildren(method, path string, desc string, permit int, handlers ...HandlerFunc) *routesNode { + return group.node.addChildren(method, path, desc, permit, handlers...) +} + +func getGinHandlerFunWithRequest(newContextFun func(ctx *gin.Context) IContext, handlers ...HandlerFunc) []gin.HandlerFunc { + list := make([]gin.HandlerFunc, 0, len(handlers)) + + for _, handler := range handlers { + list = append(list, func(rawCtx *gin.Context) { + + ctx := newContextFun(rawCtx) + + handlerTo := reflect.TypeOf(handler) + numParams := handlerTo.NumIn() + if numParams != 3 { + ctx.HandleError(rawCtx.Request.RequestURI, fmt.Errorf("register callback handler params len(%v) invalid", numParams)) + return + } + + paramsTo := handlerTo.In(1) + params := reflect.New(paramsTo.Elem()).Interface() + err := parseRequest(rawCtx, params) + if err != nil { + ctx.HandleError(rawCtx.Request.RequestURI, err) + return + } + 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)}) + errInt := rets[0] + if errInt.Interface() == nil { + ctx.HandleSuccess(rsp) + } else { + err, ok := errInt.Interface().(error) + if ok { + ctx.HandleError(rawCtx.Request.RequestURI, err) + } else { + ctx.HandleError(rawCtx.Request.RequestURI, fmt.Errorf("handle request ret error, but parse return error:%+v not ok", err)) + } + } + }) + } + return list +} + +func parseRequest(rawCtx *gin.Context, params any) error { + bodyBuf, err := io.ReadAll(rawCtx.Request.Body) + if err != nil { + return fmt.Errorf("read body error:%v", err) + } + + if len(bodyBuf) == 0 { + newRequestValue := reflect.ValueOf(params).Elem() + newRequestValueType := newRequestValue.Type() + for i := 0; i < newRequestValue.NumField(); i++ { + f := newRequestValueType.Field(i) + fieldTagName := f.Tag.Get("json") + if fieldTagName == "" { + fieldTagName = f.Name + } + + field := newRequestValue.Field(i) + if !field.CanSet() { + continue + } + fieldStr := rawCtx.Query(fieldTagName) + err := setValue(field, fieldStr, f.Tag.Get("default"), f.Tag.Get("required")) + if err != nil { + return fmt.Errorf("parse uri params field(%v) set value(%v) error:%v", f.Name, fieldStr, err) + } + } + } else { + err = json.Unmarshal(bodyBuf, params) + if err != nil { + return fmt.Errorf("json unmarshal body error:%v", err) + } + } + return nil +} + +// setValue 设置结构体一个字段的值 +func setValue(field reflect.Value, value string, defaultValue string, required string) error { + if value == "" { + value = defaultValue + } + if value == "" && required == "true" { + return fmt.Errorf("field is required, please give a valid value") + } + + if field.Kind() == reflect.Ptr { + if value == "" { + return nil + } + + if field.IsNil() { + field.Set(reflect.New(field.Type().Elem())) + } + field = field.Elem() + } + switch field.Kind() { + case reflect.String: + field.SetString(value) + case reflect.Bool: + if value == "" { + field.SetBool(false) + } else { + b, err := strconv.ParseBool(value) + if err != nil { + return err + } + field.SetBool(b) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if value == "" { + field.SetInt(0) + } else { + i, err := strconv.ParseInt(value, 0, field.Type().Bits()) + if err != nil { + return err + } + field.SetInt(i) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if value == "" { + field.SetUint(0) + break + } + ui, err := strconv.ParseUint(value, 0, field.Type().Bits()) + if err != nil { + return err + } + field.SetUint(ui) + case reflect.Float32, reflect.Float64: + if value == "" { + field.SetFloat(0) + break + } + f, err := strconv.ParseFloat(value, field.Type().Bits()) + if err != nil { + return err + } + field.SetFloat(f) + case reflect.Struct: + return fmt.Errorf("unsupport struct field:%v", field.Type()) + case reflect.Slice: + values := strings.Split(value, ",") + if len(values) == 1 && values[0] == "" { + values = []string{} + } + field.Set(reflect.MakeSlice(field.Type(), len(values), len(values))) + for i := 0; i < len(values); i++ { + err := setValue(field.Index(i), values[i], "", "") + if err != nil { + return err + } + } + + default: + return fmt.Errorf("no support type %s", field.Type()) + } + return nil +} diff --git a/admin/lib/xlog/log.go b/admin/lib/xlog/log.go index 3458a2e..ae812b3 100644 --- a/admin/lib/xlog/log.go +++ b/admin/lib/xlog/log.go @@ -125,7 +125,7 @@ func SetLogLevel(level LogLevel) { func ParseLogLevelString(level string) LogLevel { lvEnum, find := LogLevelStr2Enum[level] if !find { - return LogLevelDebug + return LogLevelTrace } return lvEnum } diff --git a/ui/package-lock.json b/ui/package-lock.json index 2ade7bd..c2f03ea 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,6 +17,7 @@ "devDependencies": { "@vitejs/plugin-vue": "^5.2.3", "unplugin-auto-import": "^19.1.2", + "unplugin-icons": "^22.1.0", "unplugin-vue-components": "^28.5.0", "vite": "^6.2.4", "vite-plugin-vue-devtools": "^7.7.2" @@ -36,6 +37,20 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.0.0.tgz", + "integrity": "sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^0.2.8", + "tinyexec": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@antfu/utils": { "version": "0.7.10", "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.10.tgz", @@ -959,6 +974,53 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -3001,6 +3063,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz", @@ -3372,6 +3444,13 @@ "node": ">=16" } }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -3526,6 +3605,51 @@ } } }, + "node_modules/unplugin-icons": { + "version": "22.1.0", + "resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-22.1.0.tgz", + "integrity": "sha512-ect2ZNtk1Zgwb0NVHd0C1IDW/MV+Jk/xaq4t8o6rYdVS3+L660ZdD5kTSQZvsgdwCvquRw+/wYn75hsweRjoIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@iconify/utils": "^2.3.0", + "debug": "^4.4.0", + "local-pkg": "^1.0.0", + "unplugin": "^2.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0", + "vue-template-compiler": "^2.6.12", + "vue-template-es2015-compiler": "^1.9.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "vue-template-es2015-compiler": { + "optional": true + } + } + }, "node_modules/unplugin-utils": { "version": "0.2.4", "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.4.tgz", diff --git a/ui/package.json b/ui/package.json index 6e86fd3..c02fced 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,6 +18,7 @@ "devDependencies": { "@vitejs/plugin-vue": "^5.2.3", "unplugin-auto-import": "^19.1.2", + "unplugin-icons": "^22.1.0", "unplugin-vue-components": "^28.5.0", "vite": "^6.2.4", "vite-plugin-vue-devtools": "^7.7.2" diff --git a/ui/src/api/game_api.js b/ui/src/api/game_api.js new file mode 100644 index 0000000..cc55a09 --- /dev/null +++ b/ui/src/api/game_api.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function gameApiRequest(url, method, data) { + return request({ + url: url, + method: method, + params: data + }) +} diff --git a/ui/src/api/project/project.js b/ui/src/api/project/project.js deleted file mode 100644 index 09061f1..0000000 --- a/ui/src/api/project/project.js +++ /dev/null @@ -1,9 +0,0 @@ -import request from '@/utils/request' - -export function getProjectList(params) { - return request({ - url: '/project', - method: 'get', - params: params - }) -} \ No newline at end of file diff --git a/ui/src/api/resource.js b/ui/src/api/resource.js new file mode 100644 index 0000000..14b524a --- /dev/null +++ b/ui/src/api/resource.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +export function resourceList(url, params) { + return request({ + url: url, + method: 'get', + data: params + }) +} + +export function resourcePost(url, params) { + return request({ + url: url, + method: 'post', + data: {'dto': params} + // params: {'dto': params} + }) +} + +export function resourcePut(url, params) { + return request({ + url: url, + method: 'put', + data: {'dto': params} + }) +} + +export function resourceDelete(url, params) { + return request({ + url: url, + method: 'delete', + data: params + }) +} \ No newline at end of file diff --git a/ui/src/components/restful/table.vue b/ui/src/components/restful/table.vue index ec10b93..cad8e6e 100644 --- a/ui/src/components/restful/table.vue +++ b/ui/src/components/restful/table.vue @@ -1,38 +1,219 @@