This commit is contained in:
likun 2025-04-22 15:46:48 +08:00
parent dafa58a5d0
commit f815590b7c
75 changed files with 2283 additions and 1154 deletions

View File

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

View File

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

View File

@ -38,6 +38,8 @@ func FromServerDto(dto dto.CommonDtoValues) *Server {
fo.Set(reflect.ValueOf(v))
}
et.Id = po.ID
return et
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +0,0 @@
package dto
type CommonListRsp = CommonDtoList

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
package config
type MockBootFlags struct {
ApiPort string `env:"mock_api_port" default:"8654" desc:"api端口客户端请求的地址端口"`
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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"` // 数据行
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

57
admin/lib/web/desc.go Normal file
View File

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

34
admin/lib/web/engine.go Normal file
View File

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

View File

@ -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 = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GM MASTER</title>
<div style="padding-left: 50px;">
{{ $pathsList := .Paths }}
{{ range $pidex, $paths := $pathsList }}
<div style="float:left">
<h1 style="text-align: center">指令列表{{ incr $pidex }}</h1>
<div style="padding-left: 20px;padding-right: 20px">
<table border = "1" align="center">
{{ range $idx, $path := $paths }}
<tr>
<th colspan="5" style="background-color: green; text-align: left">{{ $path.Route.Desc }}</th>
</tr>
<tr>
<td><b>请求路径</b></td>
<th colspan="4" style="text-align: left">&nbsp;&nbsp;{{ $path.Path }}</th>
</tr>
<tr>
<td><b>方法</b></td>
<th colspan="4" style="text-align: left">&nbsp;&nbsp;{{ $path.Route.MethodDesc "或" }}</th>
</tr>
{{ if $path.Route.HasRequest }}
{{ range $idx1, $field := $path.Route.Fields }}
<tr>
<td><b>{{ printf "参数%d" $idx1 }}</b></td>
<td style="text-align: center">{{printf "%s" $field.Name }}</td>
<td style="text-align: center">{{printf "%s" $field.Type }}</td>
<td>{{printf "%s" $field.Desc }}</td>
{{ if eq $field.Default "" }}
<td>{{printf "默认:无" }}</td>
{{ else }}
<td>{{printf "默认:%s" $field.Default }}</td>
{{ end }}
</tr>
{{ end }}
{{ else }}
<tr><th colspan="5">无需参数</th>
{{ end }}
<!--<tr><th colspan="5"><hr></th></tr>-->
{{ end }}
</table>
</div>
</div>
{{ end }}
</div>
</head>
<body>
</body>
</html>
`

View File

@ -0,0 +1 @@
package example

View File

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

View File

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

View File

@ -1,12 +0,0 @@
注意get、post注册的request结构字段如果是uri参数方式类型只支持golang基础类型以及基础类型的切片不能是结构体类型
例如:
type Field struct {
A int
B bool
}
type Request struct {
F1 *Field
}
F1字段就是非法的无法解析会报错

View File

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

View File

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

View File

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

View File

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

View File

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

124
ui/package-lock.json generated
View File

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

View File

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

9
ui/src/api/game_api.js Normal file
View File

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

View File

@ -1,9 +0,0 @@
import request from '@/utils/request'
export function getProjectList(params) {
return request({
url: '/project',
method: 'get',
params: params
})
}

34
ui/src/api/resource.js Normal file
View File

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

View File

@ -1,38 +1,219 @@
<script setup>
import {ElNotification} from "element-plus";
import {resourceDelete, resourceList, resourcePost, resourcePut} from "@/api/resource.js";
import {ref, toRaw} from "vue";
const props = defineProps({
rows: {}
// rows: {},
resource_url: '',
row_click_handler: null,
})
const fieldsDescInfo = props.rows.fields_desc
const rows = props.rows.rows
const listRsp = ref({fields_desc: [], rows: []})
const getListOk = ref(false)
const resource_url = props.resource_url
const fieldsDescInfo = ref([])
const rows = ref([])
const rules = ref({})
console.log("fields desc:", fieldsDescInfo)
console.log("rows:", props.rows)
const listData = async () => {
try {
const rspData = await resourceList(resource_url, {page_no: 0, page_len: 100});
listRsp.value = rspData;
if (listRsp.value.code !== 200) throw new Error("请求失败,错误码:", listRsp.code);
fieldsDescInfo.value = listRsp.value.data.fields_desc
rows.value = listRsp.value.data.rows
for (let i = 0; i < fieldsDescInfo.value.length; i++) {
var field = fieldsDescInfo.value[i]
dialogAddForm.value[field.key] = ''
if (field.require == true) {
rules.value[field.key] = [{required: true, message: field.name + "不能为空", trigger: "blur"}]
}
}
console.log('await list rsp:', listRsp.value)
console.log("await fields", fieldsDescInfo.value)
console.log("await rows", toRaw(rows.value))
console.log("await rules", toRaw(rules.value))
getListOk.value = true
} catch (err) {
console.log(err)
} finally {
}
}
onMounted(() => {
listData();
})
const dialogAddVisible = ref(false)
const dialogEditVisible = ref(false)
const dialogAddFormRef = ref(null)
const dialogEditFormRef = ref(null)
const dialogAddForm = ref({})
const dialogEditForm = ref({})
const submitAdd = async () => {
try {
await dialogAddFormRef.value.validate(valid => {
if (valid) {
resourcePost(resource_url, dialogAddForm.value).then((res) => {
ElNotification({
title: "添加结果通知",
message: "添加成功!",
type: 'success',
duration: 4000,
"show-close": true,
})
rows.value.push(dialogAddForm.value)
dialogAddVisible.value = false
}, (err) => {
console.log("添加报错:", err)
})
console.log("提交数据:", dialogAddForm.value)
}
})
} catch (error) {
console.log("校验失败:", error)
}
}
const submitEdit = async () => {
try {
await dialogEditFormRef.value.validate(valid => {
if (valid) {
resourcePut(resource_url, dialogEditForm.value).then((res) => {
ElNotification({
title: "编辑结果通知",
message: "编辑成功!",
type: 'success',
duration: 4000,
"show-close": true,
})
dialogEditVisible.value = false
rows.value[dialogEditForm.value.oldIndex] = res.data.dto
}, (err) => {
console.log("添加报错:", err)
})
console.log("提交数据:", dialogEditForm.value)
}
})
} catch (error) {
console.log("校验失败:", error)
}
}
const handleEdit = (oper, index, row) => {
dialogEditForm.value.oldData = row
dialogEditForm.value.oldIndex = index
dialogEditForm.value = row
console.log("edit data:", row)
dialogEditVisible.value = true
}
const handleDelete = (oper, index, row) => {
resourceDelete(resource_url, {id: row.ID}).then((res) => {
ElNotification({
title: "删除结果通知",
message: "删除指令服务器[" + row.name + "]成功!",
type: 'success',
duration: 4000,
"show-close": true,
})
rows.value.splice(index, 1)
}, (err) => {
console.log("delet error:", err)
})
}
const tableRowClick = (index, row) => {
console.log("row is clicked:", row)
if (props.row_click_handler != null) {
props.row_click_handler(row)
}
}
const handleCloseDialog = () => {
dialogAddVisible.value = false
dialogEditVisible.value = false
dialogAddForm.value = {}
dialogEditForm.value = {}
}
</script>
<template>
<div>
<el-container>
<el-header>
<el-button size="large" type="primary">添加</el-button>
</el-header>
<el-main>
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"></el-table-column>/
<el-container v-if="getListOk">
<el-header>
<el-button @click="dialogAddVisible = true" size="large" type="primary">添加</el-button>
</el-header>
<el-main>
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"></el-table-column>
</template>
<el-table-column prop="func" label="功 能">
<template #default="scope">
<el-button size="large" type="primary" @click="tableRowClick(scope.$index, scope.row)">
<el-icon style="vertical-align: middle">
<ArrowRightBold/>
</el-icon>
<span>进入</span>
</el-button>
<el-button size="large" type="success" @click="handleEdit('operation', scope.$index, scope.row)">
<el-icon style="vertical-align: middle">
<Operation/>
</el-icon>
<span>编辑</span>
</el-button>
<el-button size="large" type="danger" @click="handleDelete('operation', scope.$index, scope.row)">
<el-icon style="vertical-align: middle">
<Delete/>
</el-icon>
<span>删除</span>
</el-button>
</template>
</el-table>
</el-table-column>
</el-table>
<el-dialog v-model="addvisible" title="添加" modal="true" :before-close="handleCloseAdd" destroy-on-close>
</el-dialog>
<el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
destroy-on-close>
<el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules">
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name"></el-input>
</el-form-item>
</template>
</el-main>
</el-container>
</div>
<el-form-item>
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
</el-form-item>
</el-form>
</el-dialog>
<el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
destroy-on-close>
<el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules">
<template v-for="fieldDescInfo in fieldsDescInfo">
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
<el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>
</el-form-item>
</template>
<el-form-item>
<el-button @click="submitEdit(dialogEditFormRef)" size="large" type="primary">提交</el-button>
</el-form-item>
</el-form>
</el-dialog>
</el-main>
</el-container>
</template>
<style scoped>

View File

@ -6,9 +6,14 @@ import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus)
app.use(createPinia())
app.use(router)

View File

@ -1,27 +1,41 @@
import { createRouter, createWebHistory } from 'vue-router'
import {createRouter, createWebHistory} from 'vue-router'
import Home from '../views/Home.vue'
const query = () => {
return {
t: Date.now(),
}
}
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: Home,
children: [
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/user',
name: 'user',
component: import('@/views/user/user.vue')
path: '/',
name: 'home',
component: Home,
children: [
{
path: '/user',
name: 'user',
component: () => import('@/views/user/user.vue')
},
{
path: '/project',
name: 'project',
component: () => import('@/views/project/project.vue'),
query: query(),
children: [
{
path: 'operation',
name: "ProjectOperation",
component: () => import('@/views/project/operation.vue')
},
]
}
]
},
{
path: '/project',
name: 'project',
component: import('@/views/project/project.vue')
}
]
},
],
],
})
export default router

View File

@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

12
ui/src/stores/project.js Normal file
View File

@ -0,0 +1,12 @@
import {ref} from 'vue'
import {defineStore} from 'pinia'
export const cachedProject = defineStore('clickedProject', () => {
const project = ref({})
const set = (setProject) => project.value = setProject
const get = () => project.value
return {
set,
get,
}
})

View File

@ -1,6 +1,6 @@
<script setup>
import { useRouter, useRoute } from 'vue-router'
import { computed } from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {computed} from 'vue'
import avatarUrl from '@/assets/icon/header.png'
const router = useRouter()
@ -34,8 +34,10 @@ const handleMenuSelect = (index) => {
</el-menu>
</el-aside>
<el-container>
<el-header>Header</el-header>
<el-header><router-view/></el-header>
<el-header>游戏后台管理系统</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>

View File

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

View File

@ -1,27 +1,33 @@
<script setup>
import {ref} from "vue";
import {getProjectList} from '@/api/project/project.js'
import tableComp from "@/components/restful/table.vue"
const rows = ref([])
const getProjectListOk = ref(false)
import table from '@/components/restful/table.vue'
import operation from '@/views/project/operation.vue'
import {cachedProject} from '@/stores/project.js'
import {useRouter} from 'vue-router'
getProjectList({page_no: 0, page_len: 100}).then((response) => {
console.log(response)
rows.value = response.data
getProjectListOk.value = true
}, (err) => {
console.log(err)
})
const cachedProject1 = cachedProject()
const router = useRouter()
const page = ref('project')
const row_click_handler = (row, column, event) => {
// console.log("project row is clicked:", row)
cachedProject1.set(row)
const cachePro = cachedProject1.get()
console.log('cached project:', cachePro)
console.log("router:", router.getRoutes())
page.value = 'operation'
}
</script>
<template>
项目
<div>
<template v-if="getProjectListOk">
<component :is="tableComp" :rows="rows"></component>
</template>
<div v-if="page === 'project'">
<component :is="table" resource_url="/project" :row_click_handler="row_click_handler"></component>
</div>
<div v-else-if="page === 'operation'">
<component :is="operation" resource_url="/project" :row_click_handler="row_click_handler"></component>
</div>
</template>

View File

@ -2,6 +2,8 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
@ -18,10 +20,25 @@ export default defineConfig(({ mode, command }) => {
vue(),
vueDevTools(),
AutoImport({
resolvers: [ElementPlusResolver()],
imports: ['vue'],
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: 'Icon',
}),
],
}),
Components({
resolvers: [ElementPlusResolver()],
resolvers: [
IconsResolver({
enabledCollections: ['ep'],
}),
ElementPlusResolver(),
],
}),
Icons({
autoInstall: true,
}),
],
resolve: {