From 29cb1e0bb88fb3b809d3387ef982609493edf21d Mon Sep 17 00:00:00 2001 From: likun <906102152@qq.com> Date: Wed, 30 Apr 2025 15:46:14 +0800 Subject: [PATCH] save --- admin/apps/game/api/api.go | 38 ++ admin/apps/game/domain/comm_resource.go | 72 ++- admin/apps/game/domain/entity/comm_resouce.go | 12 +- admin/apps/game/domain/entity/project.go | 30 +- admin/apps/game/domain/entity/utils.go | 6 +- admin/apps/game/domain/project.go | 85 ++- admin/apps/game/domain/projects/projects.go | 7 +- .../apps/game/domain/projects/smdl/account.go | 2 +- admin/apps/game/domain/projects/smdl/ban.go | 48 +- admin/apps/game/domain/projects/smdl/items.go | 2 +- .../game/domain/projects/smdl/mail_global.go | 10 +- .../game/domain/projects/smdl/mail_role.go | 4 +- admin/apps/game/domain/projects/smdl/role.go | 2 +- .../apps/game/domain/projects/smdl/server.go | 4 +- admin/apps/game/domain/repo/comm_resource.go | 53 +- admin/apps/game/domain/repo/project.go | 95 +-- admin/apps/game/domain/repo/server.go | 4 +- admin/apps/game/model/account.go | 1 + admin/apps/game/model/ban.go | 47 +- admin/apps/game/model/devicepush.go | 2 +- admin/apps/game/model/dto/common.go | 12 - admin/apps/game/model/dto/msg.go | 8 - admin/apps/game/model/globalmail.go | 10 +- admin/apps/game/model/imodel.go | 6 +- admin/apps/game/model/notice.go | 6 +- admin/apps/game/model/project.go | 30 +- admin/apps/game/model/rewardcode.go | 2 +- admin/apps/game/model/role.go | 1 + admin/apps/game/model/rolemail.go | 10 +- admin/apps/game/model/server.go | 9 +- admin/apps/game/model/whitelist.go | 6 +- admin/apps/game/server/ctl_game.go | 31 +- admin/apps/game/server/middleware.go | 72 +++ admin/apps/game/server/route.go | 2 - admin/apps/game/service/service.go | 22 +- admin/apps/game/service/service_project.go | 67 ++- admin/apps/user/api/api_user.go | 40 ++ admin/apps/user/boot.go | 13 +- admin/apps/user/domain/comm_resource.go | 146 +++++ admin/apps/user/domain/entity/comm_resouce.go | 67 +++ admin/apps/user/domain/entity/user.go | 36 ++ admin/apps/user/domain/entity/utils.go | 186 ++++++ admin/apps/user/domain/iresource.go | 19 + admin/apps/user/domain/repo/comm_resource.go | 126 ++++ admin/apps/user/domain/repo/token.go | 71 +++ admin/apps/user/domain/repo/user.go | 95 +++ admin/apps/user/domain/user.go | 64 ++ admin/apps/user/model/character.go | 43 ++ admin/apps/user/model/dto/common.go | 64 ++ admin/apps/user/model/dto/msg.go | 67 +++ admin/apps/user/model/imodel.go | 6 + admin/apps/user/model/token.go | 43 ++ admin/apps/user/model/user.go | 52 ++ admin/apps/user/server/ctl.go | 11 + admin/apps/user/server/ctl_common_rest.go | 57 ++ admin/apps/user/server/ctl_user.go | 24 + admin/apps/user/server/middleware.go | 26 + admin/apps/user/server/route.go | 28 + admin/apps/user/server/server.go | 15 + admin/apps/user/service/service.go | 57 ++ admin/apps/user/service/service_user.go | 107 ++++ admin/cmd/all_in_one/main.go | 5 +- admin/go.mod | 1 + admin/go.sum | 2 + admin/internal/consts/consts.go | 14 + admin/internal/context/ctx_web.go | 21 + admin/internal/errcode/code.go | 11 +- admin/internal/permission/strings.go | 18 + admin/lib/passlib/password.go | 12 + admin/lib/tokenlib/token.go | 56 ++ admin/lib/tokenlib/token_test.go | 20 + admin/lib/web/routes_group.go | 15 +- ui/src/api/game_api.js | 23 - ui/src/api/sys.js | 24 + ui/src/assets/img/login-background.svg | 67 +++ ui/src/components/restful/empty.vue | 11 + ui/src/components/restful/table.vue | 373 ++++++------ ui/src/components/restful/tableUser.vue | 551 ++++++++++++++++++ ui/src/permission.js | 30 +- ui/src/router/index.js | 50 +- ui/src/stores/expireCache.js | 43 ++ ui/src/stores/localCache.js | 27 + ui/src/stores/project.js | 30 +- ui/src/utils/permission.js | 73 +++ ui/src/utils/request.js | 97 +-- ui/src/views/Home.vue | 94 ++- ui/src/views/Login.vue | 262 +++++++++ ui/src/views/project/project.vue | 9 +- ui/src/views/user/character.vue | 34 ++ ui/src/views/user/user.vue | 25 +- ui/src/views/welcome.vue | 18 + 91 files changed, 3688 insertions(+), 609 deletions(-) create mode 100644 admin/apps/game/api/api.go create mode 100644 admin/apps/game/server/middleware.go create mode 100644 admin/apps/user/api/api_user.go create mode 100644 admin/apps/user/domain/comm_resource.go create mode 100644 admin/apps/user/domain/entity/comm_resouce.go create mode 100644 admin/apps/user/domain/entity/user.go create mode 100644 admin/apps/user/domain/entity/utils.go create mode 100644 admin/apps/user/domain/iresource.go create mode 100644 admin/apps/user/domain/repo/comm_resource.go create mode 100644 admin/apps/user/domain/repo/token.go create mode 100644 admin/apps/user/domain/repo/user.go create mode 100644 admin/apps/user/domain/user.go create mode 100644 admin/apps/user/model/character.go create mode 100644 admin/apps/user/model/dto/common.go create mode 100644 admin/apps/user/model/dto/msg.go create mode 100644 admin/apps/user/model/imodel.go create mode 100644 admin/apps/user/model/token.go create mode 100644 admin/apps/user/model/user.go create mode 100644 admin/apps/user/server/ctl.go create mode 100644 admin/apps/user/server/ctl_common_rest.go create mode 100644 admin/apps/user/server/ctl_user.go create mode 100644 admin/apps/user/server/middleware.go create mode 100644 admin/apps/user/server/route.go create mode 100644 admin/apps/user/server/server.go create mode 100644 admin/apps/user/service/service.go create mode 100644 admin/apps/user/service/service_user.go create mode 100644 admin/internal/permission/strings.go create mode 100644 admin/lib/passlib/password.go create mode 100644 admin/lib/tokenlib/token.go create mode 100644 admin/lib/tokenlib/token_test.go delete mode 100644 ui/src/api/game_api.js create mode 100644 ui/src/api/sys.js create mode 100644 ui/src/assets/img/login-background.svg create mode 100644 ui/src/components/restful/empty.vue create mode 100644 ui/src/components/restful/tableUser.vue create mode 100644 ui/src/stores/expireCache.js create mode 100644 ui/src/stores/localCache.js create mode 100644 ui/src/utils/permission.js create mode 100644 ui/src/views/Login.vue create mode 100644 ui/src/views/user/character.vue create mode 100644 ui/src/views/welcome.vue diff --git a/admin/apps/game/api/api.go b/admin/apps/game/api/api.go new file mode 100644 index 0000000..996ee5c --- /dev/null +++ b/admin/apps/game/api/api.go @@ -0,0 +1,38 @@ +package api + +func GetGameApiInstance() IGameApi { + return gameApiInstance +} + +var gameApiInstance IGameApi + +type IGameApi interface { + GetRoutes(req *GetRoutesReq) (*GetRoutesRsp, error) // 用户服务通过用户权限拉取游戏项目路由权限,生成动态菜单 +} + +func RegisterGameApi(handler IGameApi) { + gameApiInstance = handler +} + +type ResourceInitInfo struct { + Resource string `json:"resource"` + Desc string `json:"desc"` + ShowMethods []string `json:"show_methods"` // 资源权限列表 + MethodsPermissionStr []string `json:"methods_permission"` // 资源权限字符串 +} + +type ProjectInitInfo struct { + ProjectId int `json:"project_id"` + ProjectName string `json:"project_name"` + ResourceList []*ResourceInitInfo `json:"resource_list"` // 权限过滤后客户端可以显示的资源操作 + ResourceTotalList []*ResourceInitInfo `json:"resource_total_list"` // 所有资源操作权限,可以用来做权限分配树初始化 +} + +type GetRoutesReq struct { + IsAdmin bool + Permissions []string +} + +type GetRoutesRsp struct { + Projects []*ProjectInitInfo +} diff --git a/admin/apps/game/domain/comm_resource.go b/admin/apps/game/domain/comm_resource.go index a81b0a0..3a333e6 100644 --- a/admin/apps/game/domain/comm_resource.go +++ b/admin/apps/game/domain/comm_resource.go @@ -1,6 +1,7 @@ package domain import ( + "admin/apps/game/api" "admin/apps/game/domain/entity" "admin/apps/game/domain/projects" "admin/apps/game/domain/repo" @@ -17,14 +18,14 @@ type CommonResourceService struct { } func initCommonResourcesRepo(db *gorm.DB) { - r(consts.ResourcesName_Project, "项目管理", repo.NewCommonResourceRepo(db, &model.Project{}), ShowMethod_Get|ShowMethod_Put) + r(consts.ResourcesName_Project, "项目管理", repo.NewCommonResourceRepo(db, &model.Project{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) r(consts.ResourcesName_Server, "服务器管理", repo.NewCommonResourceRepo(db, &model.Server{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) r(consts.ResourcesName_Account, "账号管理", repo.NewCommonResourceRepo(db, &model.Account{}), ShowMethod_Get) // 账号管理不需要在后台读写数据,都是通过项目api拉 r(consts.ResourcesName_Role, "角色管理", repo.NewCommonResourceRepo(db, &model.Role{}), ShowMethod_Get) // 角色管理不需要在后台读写数据,都是通过项目api拉 r(consts.ResourcesName_WhiteList, "白名单", repo.NewCommonResourceRepo(db, &model.WhiteList{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete) r(consts.ResourcesName_Ban, "封禁管理", repo.NewCommonResourceRepo(db, &model.Ban{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) - r(consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{}), ShowMethod_Get|ShowMethod_Post) // 个人邮件发放就没法撤回? - r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete) // 直接删除,别修改了,玩家收到的更乱 + r(consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{}), ShowMethod_Get|ShowMethod_Post) // 个人邮件发放就没法撤回? + r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post) // 直接删除,别修改了,玩家收到的更乱 r(consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) r(consts.ResourcesName_RewardCode, "奖励码", repo.NewCommonResourceRepo(db, &model.RewardCode{}), ShowMethod_Get) r(consts.ResourcesName_DevicePush, "设备推送", repo.NewCommonResourceRepo(db, &model.DevicePush{}), ShowMethod_Get) @@ -38,16 +39,16 @@ func NewCommonResourceService(db *gorm.DB) *CommonResourceService { return svc } -func (svc *CommonResourceService) List(projectId string, resource string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { - projectEt, find, err := svc.projectRepo.GetByProjectId(projectId) +func (svc *CommonResourceService) List(projectId int, resource string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { + _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return nil, nil, err } - if !find { + if resource != consts.ResourcesName_Project && !find { return nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } - fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectId, pageNo, pageLen, extraQuery, args...) + fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectEt, pageNo, pageLen, extraQuery, args...) if err != nil { return nil, nil, err } @@ -57,15 +58,23 @@ func (svc *CommonResourceService) List(projectId string, resource string, pageNo } // 执行各个项目特定的钩子方法 - if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpListHook); ok { + if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpListHook); ok { return hook.List(projectEt, resource, pageNo, pageLen, fieldsDescInfo, retList, extraQuery, args...) } return fieldsDescInfo, retList, nil } -func (svc *CommonResourceService) GetById(projectId string, resource string, id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) { - fieldsDescInfo, et, find, err := findCommResourceRepo(resource).GetById(projectId, id) +func (svc *CommonResourceService) GetById(projectId int, resource string, id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) { + _, projectEt, find, err := svc.projectRepo.GetById(projectId) + if err != nil { + return nil, nil, nil, false, err + } + if resource != consts.ResourcesName_Project && !find { + return nil, nil, nil, false, errcode.New(errcode.ServerError, "not found project %v db data", projectId) + } + + fieldsDescInfo, et, find, err := findCommResourceRepo(resource).GetById(projectEt, id) if err != nil { return nil, nil, nil, false, err } @@ -73,16 +82,16 @@ func (svc *CommonResourceService) GetById(projectId string, resource string, id return fieldsDescInfo, et.ToCommonDto(), et, find, nil } -func (svc *CommonResourceService) Create(projectId string, resource string, dtoObj dto.CommonDtoValues) (dto.CommonDtoValues, error) { - projectEt, find, err := svc.projectRepo.GetByProjectId(projectId) +func (svc *CommonResourceService) Create(projectId int, resource string, dtoObj dto.CommonDtoValues) (dto.CommonDtoValues, error) { + _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return nil, err } - if !find { + if resource != consts.ResourcesName_Project && !find { return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } - et, err := findCommResourceRepo(resource).Create(projectId, dtoObj) + et, err := findCommResourceRepo(resource).Create(projectEt, dtoObj) if err != nil { return nil, err } @@ -90,7 +99,7 @@ func (svc *CommonResourceService) Create(projectId string, resource string, dtoO // 返回新的实体,插入数据库之后会填入数据库id,所以要返给前端刷新id数据 // 执行各个项目特定的钩子方法 - if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpCreateHook); ok { + if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpCreateHook); ok { dtoObj := et.ToCommonDto() err = hook.Create(projectEt, resource, dtoObj) if err != nil { @@ -102,22 +111,22 @@ func (svc *CommonResourceService) Create(projectId string, resource string, dtoO return et.ToCommonDto(), err } -func (svc *CommonResourceService) Edit(projectId string, resource string, dtoObj dto.CommonDtoValues) error { - projectEt, find, err := svc.projectRepo.GetByProjectId(projectId) +func (svc *CommonResourceService) Edit(projectId int, resource string, dtoObj dto.CommonDtoValues) error { + _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return err } - if !find { + if resource != consts.ResourcesName_Project && !find { return errcode.New(errcode.ServerError, "not found project %v db data", projectId) } - err = findCommResourceRepo(resource).Edit(projectId, dtoObj) + err = findCommResourceRepo(resource).Edit(projectEt, dtoObj) if err != nil { return err } // 执行各个项目特定的钩子方法 - if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpEditHook); ok { + if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpEditHook); ok { err = hook.Edit(projectEt, resource, dtoObj) if err != nil { return err @@ -128,23 +137,23 @@ func (svc *CommonResourceService) Edit(projectId string, resource string, dtoObj return nil } -func (svc *CommonResourceService) Delete(projectId string, resource string, id int) error { - projectEt, find, err := svc.projectRepo.GetByProjectId(projectId) +func (svc *CommonResourceService) Delete(projectId int, resource string, id int) error { + _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return err } - if !find { + if resource != consts.ResourcesName_Project && !find { return errcode.New(errcode.ServerError, "not found project %v db data", projectId) } - oldEt, find, err := findCommResourceRepo(resource).Delete(projectId, id) + oldEt, find, err := findCommResourceRepo(resource).Delete(projectEt, id) if err != nil { return err } // 执行各个项目特定的钩子方法 if find { - if hook, ok := projects.GetProjectResourceHook(projectId, resource).(projects.IPostResourceOpDeleteHook); ok { + if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpDeleteHook); ok { err = hook.Delete(projectEt, resource, oldEt.ToCommonDto()) if err != nil { return err @@ -156,10 +165,10 @@ func (svc *CommonResourceService) Delete(projectId string, resource string, id i return nil } -func (svc *CommonResourceService) GetSupportResourcesList() []*dto.ResourceInitInfo { - list := make([]*dto.ResourceInitInfo, 0, len(commResourcesRepo)) +func (svc *CommonResourceService) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo { + list := make([]*api.ResourceInitInfo, 0, len(commResourcesRepo)) for _, v := range commResourcesRepo { - list = append(list, &dto.ResourceInitInfo{ + list = append(list, &api.ResourceInitInfo{ Resource: v.Resource, Desc: v.Desc, ShowMethods: v.ShowMethods, @@ -187,9 +196,10 @@ var commResourcesRepo = make([]*resourceRepoInfo, 0) func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) { curRepo := &resourceRepoInfo{ - Resource: resource, - Desc: desc, - Repo: repo, + Resource: resource, + Desc: desc, + Repo: repo, + ShowMethods: make([]string, 0), } if showMethods&ShowMethod_Get == ShowMethod_Get { diff --git a/admin/apps/game/domain/entity/comm_resouce.go b/admin/apps/game/domain/entity/comm_resouce.go index cc1d325..17eed85 100644 --- a/admin/apps/game/domain/entity/comm_resouce.go +++ b/admin/apps/game/domain/entity/comm_resouce.go @@ -7,8 +7,8 @@ import ( ) type CommonResource struct { - ProjectEt *Project - Po model.IModel + Project *Project // 所属项目 + Po model.IModel } func (et *CommonResource) FromPo(po model.IModel) *CommonResource { @@ -43,7 +43,7 @@ func (et *CommonResource) ToCommonDto() dto.CommonDtoValues { return poToCommonDtoNo(et.Po) } -func (et *CommonResource) GetDtoFieldsDescInfo(projectId string) []*dto.CommonDtoFieldDesc { +func (et *CommonResource) GetDtoFieldsDescInfo(project *Project) []*dto.CommonDtoFieldDesc { ptrVo := reflect.ValueOf(et.Po) @@ -57,10 +57,12 @@ func (et *CommonResource) GetDtoFieldsDescInfo(projectId string) []*dto.CommonDt //fo := vo.Field(i) if ft.Name == "ProjectId" { - continue + if _, ok := et.Po.(*model.Project); !ok { + continue + } } - f1 := getFieldTypeDtoDescInfo(projectId, ptrVo, ft) + f1 := getFieldTypeDtoDescInfo(project, ptrVo, ft) obj = append(obj, f1) } diff --git a/admin/apps/game/domain/entity/project.go b/admin/apps/game/domain/entity/project.go index 5113430..0870ac4 100644 --- a/admin/apps/game/domain/entity/project.go +++ b/admin/apps/game/domain/entity/project.go @@ -1,24 +1,34 @@ package entity -import "admin/apps/game/model" +import ( + "admin/apps/game/model" + "admin/apps/game/model/dto" +) type Project struct { - *CommonResource - ProjectPo *model.Project + Po *model.Project } -func (commEt *CommonResource) ToProjectEntity() *Project { - po := commEt.ToPo().(*model.Project) - return &Project{ - CommonResource: commEt, - ProjectPo: po, +func FromProjectPo(po *model.Project) *Project { + return &Project{Po: po} +} + +func (project *Project) FromDto(obj dto.CommonDtoValues) *Project { + project.ToCommonResource().FromDto(obj) + return project +} + +func (project *Project) ToCommonResource() *CommonResource { + er := &CommonResource{ + Project: project, } + return er.FromPo(project.Po) } func (project *Project) GetProjectPo() *model.Project { - return project.ProjectPo + return project.Po } func (project *Project) GetApiAddr() string { - return project.ProjectPo.ApiAddr + return project.Po.ApiAddr } diff --git a/admin/apps/game/domain/entity/utils.go b/admin/apps/game/domain/entity/utils.go index 3585a1f..004f5c0 100644 --- a/admin/apps/game/domain/entity/utils.go +++ b/admin/apps/game/domain/entity/utils.go @@ -30,14 +30,14 @@ func poToCommonDtoNo(po any) dto.CommonDtoValues { obj[ft.Name] = fo.Interface() if ft.Type.Name() == "Time" && ft.Type.PkgPath() == "time" { - obj[ft.Name] = fo.Interface().(time.Time).Format(time.DateTime) + obj[ft.Name] = fo.Interface().(time.Time).Format("2006/01/02 15:04:05") } } return obj } -func getFieldTypeDtoDescInfo(projectId string, poValue reflect.Value, fieldType reflect.StructField) *dto.CommonDtoFieldDesc { +func getFieldTypeDtoDescInfo(project *Project, poValue reflect.Value, fieldType reflect.StructField) *dto.CommonDtoFieldDesc { name := fieldType.Tag.Get("name") if name == "" { name = fieldType.Name @@ -68,7 +68,7 @@ func getFieldTypeDtoDescInfo(projectId string, poValue reflect.Value, fieldType if !method.IsValid() { xlog.Warnf("po %v choices %v function not found in model", poValue.Type().Name(), cf) } else { - rets := method.Call([]reflect.Value{reflect.ValueOf(projectId)}) + rets := method.Call([]reflect.Value{reflect.ValueOf(project.Po)}) f1.Choices = rets[0].Interface().([]*dto.CommonDtoFieldChoice) } diff --git a/admin/apps/game/domain/project.go b/admin/apps/game/domain/project.go index 01dee8d..d5e2e8f 100644 --- a/admin/apps/game/domain/project.go +++ b/admin/apps/game/domain/project.go @@ -2,12 +2,9 @@ package domain import ( "admin/apps/game/domain/entity" - "admin/apps/game/domain/projects" "admin/apps/game/domain/repo" - "admin/apps/game/model" - "admin/lib/xlog" + "admin/apps/game/model/dto" "gorm.io/gorm" - "time" ) type ProjectService struct { @@ -19,53 +16,55 @@ func NewProjectService(db *gorm.DB) *ProjectService { return &ProjectService{repo: repo.NewProjectRepo(db)} } -func (svc *ProjectService) List() ([]*entity.Project, error) { - return svc.repo.List() +func (svc *ProjectService) List() ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, []*entity.Project, error) { + fields, rows, err := svc.repo.List() + if err != nil { + return nil, nil, nil, err + } + commonList := make([]dto.CommonDtoValues, 0, len(rows)) + for _, v := range rows { + commonList = append(commonList, v.ToCommonResource().ToCommonDto()) + } + return fields, commonList, rows, nil } -func (svc *ProjectService) EnsureProjectsDBData() error { - _, err := svc.repo.EnsureProjectsDBData() - go svc.startProjectBackGroundWorker() - return err +func (svc *ProjectService) GetById(id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) { + fields, et, find, err := svc.repo.GetById(id) + if err != nil { + return nil, nil, nil, false, err + } + cr := et.ToCommonResource() + return fields, cr.ToCommonDto(), cr, find, nil } -func (svc *ProjectService) GetProjectInvokeApiAddr(projectId string, serverIds []int) ([]string, error) { - et, _, err := svc.repo.GetByProjectId(projectId) +func (svc *ProjectService) Create(dtoObj dto.CommonDtoValues) (dto.CommonDtoValues, error) { + et, err := svc.repo.Create(dtoObj) if err != nil { return nil, err } - return []string{et.ToPo().(*model.Project).ApiAddr}, nil + return et.ToCommonResource().ToCommonDto(), nil } -func (svc *ProjectService) startProjectBackGroundWorker() { - for { - allProjects, err := svc.repo.List() - if err != nil { - xlog.Warnf("list all projects error:%v", err) - time.Sleep(time.Second * 60) - continue - } - for _, p := range allProjects { - apiAddr := p.GetApiAddr() - if apiAddr == "" { - continue - } - - po := p.ToPo().(*model.Project) - handler := projects.GetProjectValueChoicesGetHook(po.ProjectId) - if handler != nil { - itemsChoices, err := handler.GetItems(p) - if err != nil { - xlog.Warnf("get project %v items error:%v", po.ProjectId, err) - } else { - if len(itemsChoices) > 100 { - itemsChoices = itemsChoices[:100] - } - model.ProjectsAllItems[po.ProjectId] = itemsChoices - } - } - } - - time.Sleep(time.Second * 60) +func (svc *ProjectService) Edit(dtoObj dto.CommonDtoValues) error { + err := svc.repo.Edit(dtoObj) + if err != nil { + return err } + return nil +} + +func (svc *ProjectService) Delete(id int) error { + _, find, err := svc.repo.Delete(id) + if find { + + } + return err +} + +func (svc *ProjectService) GetProjectInvokeApiAddr(projectId int, serverIds []int) ([]string, error) { + _, et, _, err := svc.repo.GetById(projectId) + if err != nil { + return nil, err + } + return []string{et.Po.ApiAddr}, nil } diff --git a/admin/apps/game/domain/projects/projects.go b/admin/apps/game/domain/projects/projects.go index b5c59a5..1749158 100644 --- a/admin/apps/game/domain/projects/projects.go +++ b/admin/apps/game/domain/projects/projects.go @@ -1,6 +1,7 @@ package projects import ( + "admin/apps/game/domain/entity" "admin/apps/game/domain/projects/smdl" "admin/internal/consts" ) @@ -23,12 +24,12 @@ var projectsValueChoicesGetHook = map[string]IGetAllValueChoicesHook{ consts.RegisteredProjectId_shenmodalu: &smdl.Items{}, } -func GetProjectResourceHook(projectId, resource string) any { - project, find := projectsResourceHookMgr[projectId] +func GetProjectResourceHook(project *entity.Project, resource string) any { + projectResourceHooks, find := projectsResourceHookMgr[project.Po.ProjectType] if !find { return nil } - return project[resource] + return projectResourceHooks[resource] } func GetProjectValueChoicesGetHook(projectId string) IGetAllValueChoicesHook { diff --git a/admin/apps/game/domain/projects/smdl/account.go b/admin/apps/game/domain/projects/smdl/account.go index 8776822..bfb9207 100644 --- a/admin/apps/game/domain/projects/smdl/account.go +++ b/admin/apps/game/domain/projects/smdl/account.go @@ -18,7 +18,7 @@ func (hook *AccountHook) List(projectInfo *entity.Project, resource string, page []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } type RspData struct { diff --git a/admin/apps/game/domain/projects/smdl/ban.go b/admin/apps/game/domain/projects/smdl/ban.go index 11f4b3c..5578846 100644 --- a/admin/apps/game/domain/projects/smdl/ban.go +++ b/admin/apps/game/domain/projects/smdl/ban.go @@ -4,9 +4,10 @@ import ( "admin/apps/game/domain/entity" "admin/apps/game/model" "admin/apps/game/model/dto" + "admin/internal/consts" "admin/internal/errcode" "admin/lib/httpclient" - "fmt" + "admin/lib/xlog" "net/url" "strconv" "time" @@ -18,12 +19,25 @@ type BanHook struct { func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.Ban{}).FromDto(dtoObj) banInfo := et.ToPo().(*model.Ban) + + banApi := "" + switch banInfo.BanType { + case consts.BanType_Role: + banApi = "banrole" + case consts.BanType_RoleChat: + banApi = "banroletalk" + default: + xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType) + return nil + } + roleId := banInfo.Value + params := &url.Values{} params.Add("server", banInfo.ServerConfID) params.Add("roleid", roleId) @@ -33,22 +47,22 @@ func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj if expireAt <= 0 { // 解封 params.Add("forbidtime", "0") - params.Add("desc", "待写原因") - params.Add("desc", "你被永久封禁了") + params.Add("desc", banInfo.BanReason) + params.Add("notifytouser", banInfo.BanNotifyReason) } else { - dura := expireAt - time.Now().Unix() + dura := (expireAt - time.Now().Unix()) / 60 // 神魔大陆封禁是分钟 if dura <= 0 { // 解封 params.Add("forbidtime", "0") } else { params.Add("forbidtime", strconv.FormatInt(dura, 10)) - params.Add("desc", "待写原因") - params.Add("desc", fmt.Sprintf("你被封禁了,封禁到期时间:%v", banInfo.ExpireAt)) + params.Add("desc", banInfo.BanReason) + params.Add("notifytouser", banInfo.BanNotifyReason) } } rsp := make(map[string]any) - err := httpclient.Request(alisrvAddr+"/banrole", "get", params, &rsp) + err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp) if err != nil { return err } @@ -59,23 +73,35 @@ func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj func (hook *BanHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.Ban{}).FromDto(dtoObj) banInfo := et.ToPo().(*model.Ban) + + banApi := "" + switch banInfo.BanType { + case consts.BanType_Role: + banApi = "banrole" + case consts.BanType_RoleChat: + banApi = "banroletalkdel" + default: + xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType) + return nil + } + roleId := banInfo.Value params := &url.Values{} params.Add("server", banInfo.ServerConfID) params.Add("roleid", roleId) // 解封 - params.Add("forbidtime", "0") + params.Add("forbidtime", "-1") params.Add("desc", "") params.Add("notifytouser", "") rsp := make(map[string]any) - err := httpclient.Request(alisrvAddr+"/banrole", "get", params, &rsp) + err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp) if err != nil { return err } diff --git a/admin/apps/game/domain/projects/smdl/items.go b/admin/apps/game/domain/projects/smdl/items.go index b9aa1e4..51b3b66 100644 --- a/admin/apps/game/domain/projects/smdl/items.go +++ b/admin/apps/game/domain/projects/smdl/items.go @@ -13,7 +13,7 @@ type Items struct { func (items *Items) GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error) { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } type RspData struct { diff --git a/admin/apps/game/domain/projects/smdl/mail_global.go b/admin/apps/game/domain/projects/smdl/mail_global.go index e949513..534dc75 100644 --- a/admin/apps/game/domain/projects/smdl/mail_global.go +++ b/admin/apps/game/domain/projects/smdl/mail_global.go @@ -19,7 +19,7 @@ type MailGlobalHook struct { func (hook *MailGlobalHook) Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.GlobalMail{}).FromDto(dtoObj) @@ -48,10 +48,10 @@ func (hook *MailGlobalHook) Create(projectInfo *entity.Project, resource string, switch { case serverIdsLen == 0: // 为空就发给所有服 - serverList, err := repo.ServerRepoInstance.List(projectInfo.ProjectPo.ProjectId) + serverList, err := repo.ServerRepoInstance.List(projectInfo.Po.ID) if err != nil { - xlog.Errorf("list all server by project:%v error:%v", projectInfo.ProjectPo.ProjectId, err) - return errcode.New(errcode.ServerError, "list all server by project:%v error:%v", projectInfo.ProjectPo.ProjectId, err) + xlog.Errorf("list all server by project:%v error:%v", projectInfo.Po.ID, err) + return errcode.New(errcode.ServerError, "list all server by project:%v error:%v", projectInfo.Po.ID, err) } for _, v := range serverList { serverIds = append(serverIds, v.ToPo().(*model.Server).ServerConfID) @@ -93,7 +93,7 @@ func (hook *MailGlobalHook) Create(projectInfo *entity.Project, resource string, func (hook *MailGlobalHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.GlobalMail{}).FromDto(dtoObj) diff --git a/admin/apps/game/domain/projects/smdl/mail_role.go b/admin/apps/game/domain/projects/smdl/mail_role.go index 1073c77..5f73e56 100644 --- a/admin/apps/game/domain/projects/smdl/mail_role.go +++ b/admin/apps/game/domain/projects/smdl/mail_role.go @@ -18,7 +18,7 @@ type MailRoleHook struct { func (hook *MailRoleHook) Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.RoleMail{}).FromDto(dtoObj) @@ -65,7 +65,7 @@ func (hook *MailRoleHook) Create(projectInfo *entity.Project, resource string, d func (hook *MailRoleHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } et := (&entity.CommonResource{}).FromPo(&model.RoleMail{}).FromDto(dtoObj) diff --git a/admin/apps/game/domain/projects/smdl/role.go b/admin/apps/game/domain/projects/smdl/role.go index c611e57..7c2e6e9 100644 --- a/admin/apps/game/domain/projects/smdl/role.go +++ b/admin/apps/game/domain/projects/smdl/role.go @@ -28,7 +28,7 @@ func (hook *RoleHook) List(projectInfo *entity.Project, resource string, pageNo, []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } type RspData struct { diff --git a/admin/apps/game/domain/projects/smdl/server.go b/admin/apps/game/domain/projects/smdl/server.go index 431256c..2a2b710 100644 --- a/admin/apps/game/domain/projects/smdl/server.go +++ b/admin/apps/game/domain/projects/smdl/server.go @@ -15,7 +15,7 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, pageN []*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { - return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.ProjectPo.Name) + return nil, nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) } fields = append(fields, &dto.CommonDtoFieldDesc{ @@ -23,7 +23,7 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, pageN Key: "RunningStatus", Type: "string", HelpText: "进程运行状态:未知、运行中、停止", - Readonly: false, + Readonly: true, Required: false, Choices: make([]*dto.CommonDtoFieldChoice, 0), MultiChoice: false, diff --git a/admin/apps/game/domain/repo/comm_resource.go b/admin/apps/game/domain/repo/comm_resource.go index b91718e..3ddb3b3 100644 --- a/admin/apps/game/domain/repo/comm_resource.go +++ b/admin/apps/game/domain/repo/comm_resource.go @@ -11,11 +11,11 @@ import ( ) type ICommonResourceRepo interface { - List(projectId string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) - GetById(projectId string, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) - Create(projectId string, et dto.CommonDtoValues) (*entity.CommonResource, error) - Edit(projectId string, et dto.CommonDtoValues) error - Delete(projectId string, id int) (*entity.CommonResource, bool, error) + List(project *entity.Project, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) + GetById(projectEt *entity.Project, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) + Create(projectEt *entity.Project, et dto.CommonDtoValues) (*entity.CommonResource, error) + Edit(projectEt *entity.Project, et dto.CommonDtoValues) error + Delete(projectEt *entity.Project, id int) (*entity.CommonResource, bool, error) } func NewCommonResourceRepo(db *gorm.DB, poTemplate model.IModel) ICommonResourceRepo { @@ -25,7 +25,7 @@ func NewCommonResourceRepo(db *gorm.DB, poTemplate model.IModel) ICommonResource type commonResourceRepoImpl struct { db *gorm.DB poTemplate model.IModel - fieldsDescInfoFun func(projectId string) []*dto.CommonDtoFieldDesc + fieldsDescInfoFun func(project *entity.Project) []*dto.CommonDtoFieldDesc } func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonResourceRepoImpl { @@ -33,7 +33,9 @@ func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonReso return &commonResourceRepoImpl{db: db, poTemplate: poTemplate, fieldsDescInfoFun: fieldsInfo} } -func (repo *commonResourceRepoImpl) List(projectId string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { +func (repo *commonResourceRepoImpl) List(projectEt *entity.Project, pageNo, pageLen int, + extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { + listType := reflect.New(reflect.SliceOf(reflect.TypeOf(repo.poTemplate))) var err error if extraQuery == "" { @@ -56,22 +58,22 @@ func (repo *commonResourceRepoImpl) List(projectId string, pageNo, pageLen int, entityList = append(entityList, et) } - return repo.fieldsDescInfoFun(projectId), entityList, nil + return repo.fieldsDescInfoFun(projectEt), entityList, nil } -func (repo *commonResourceRepoImpl) GetById(projectId string, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) { +func (repo *commonResourceRepoImpl) GetById(projectEt *entity.Project, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) { po := repo.newEmptyPo() - err := repo.db.Where("id = ?", id).Find(po).Error + err := repo.db.Where("id = ?", id).First(po).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return repo.fieldsDescInfoFun(projectId), (&entity.CommonResource{}).FromPo(repo.newEmptyPo()), false, nil + return repo.fieldsDescInfoFun(projectEt), (&entity.CommonResource{}).FromPo(repo.newEmptyPo()), false, nil } return nil, nil, false, errcode.New(errcode.DBError, "get resource:%v by id:%v error:%v", repo.poTemplate.TableName(), id, err) } - return repo.fieldsDescInfoFun(projectId), (&entity.CommonResource{}).FromPo(po), true, nil + return repo.fieldsDescInfoFun(projectEt), (&entity.CommonResource{}).FromPo(po), true, nil } -func (repo *commonResourceRepoImpl) Create(projectId string, dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) { +func (repo *commonResourceRepoImpl) Create(projectEt *entity.Project, dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) { et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj) err := repo.db.Create(et.Po).Error if err != nil { @@ -80,7 +82,7 @@ func (repo *commonResourceRepoImpl) Create(projectId string, dtoObj dto.CommonDt return et, nil } -func (repo *commonResourceRepoImpl) Edit(projectId string, dtoObj dto.CommonDtoValues) error { +func (repo *commonResourceRepoImpl) Edit(projectEt *entity.Project, dtoObj dto.CommonDtoValues) error { et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj) err := repo.db.Where("id=?", et.Po.GetId()).Updates(et.Po).Error if err != nil { @@ -89,8 +91,8 @@ func (repo *commonResourceRepoImpl) Edit(projectId string, dtoObj dto.CommonDtoV return nil } -func (repo *commonResourceRepoImpl) Delete(projectId string, id int) (*entity.CommonResource, bool, error) { - _, et, find, err := repo.GetById(projectId, id) +func (repo *commonResourceRepoImpl) Delete(projectEt *entity.Project, id int) (*entity.CommonResource, bool, error) { + _, et, find, err := repo.GetById(projectEt, id) if err != nil { return nil, false, err } @@ -108,22 +110,3 @@ func (repo *commonResourceRepoImpl) Delete(projectId string, id int) (*entity.Co func (repo *commonResourceRepoImpl) newEmptyPo() model.IModel { return reflect.New(reflect.TypeOf(repo.poTemplate).Elem()).Interface().(model.IModel) } - -type nopRepoImpl struct { -} - -func (impl *nopRepoImpl) List(projectId string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { - return nil, nil, nil -} -func (impl *nopRepoImpl) GetById(projectId string, id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) { - return nil, nil, false, nil -} -func (impl *nopRepoImpl) Create(projectId string, et dto.CommonDtoValues) (*entity.CommonResource, error) { - return nil, nil -} -func (impl *nopRepoImpl) Edit(projectId string, et dto.CommonDtoValues) error { - return nil -} -func (impl *nopRepoImpl) Delete(projectId string, id int) error { - return nil -} diff --git a/admin/apps/game/domain/repo/project.go b/admin/apps/game/domain/repo/project.go index aa5684c..517d8fd 100644 --- a/admin/apps/game/domain/repo/project.go +++ b/admin/apps/game/domain/repo/project.go @@ -3,24 +3,21 @@ package repo import ( "admin/apps/game/domain/entity" "admin/apps/game/model" - "admin/internal/consts" + "admin/apps/game/model/dto" "admin/internal/errcode" "errors" "gorm.io/gorm" ) type IProjectRepo interface { - EnsureProjectsDBData() ([]*model.Project, error) - List() ([]*entity.Project, error) - GetByProjectId(id string) (*entity.Project, bool, error) + List() ([]*dto.CommonDtoFieldDesc, []*entity.Project, error) + GetById(id int) ([]*dto.CommonDtoFieldDesc, *entity.Project, bool, error) + Create(obj dto.CommonDtoValues) (*entity.Project, error) + Edit(obj dto.CommonDtoValues) error + Delete(id int) (*entity.Project, bool, error) } func NewProjectRepo(db *gorm.DB) IProjectRepo { - model.GetProjectServersHandler = func(projectId string) ([]*model.Server, error) { - servers := make([]*model.Server, 0) - err := db.Where("project_id=?", projectId).Find(&servers).Error - return servers, err - } return &projectRepoImpl{db: db} } @@ -28,54 +25,68 @@ type projectRepoImpl struct { db *gorm.DB } -func (impl *projectRepoImpl) EnsureProjectsDBData() ([]*model.Project, error) { - var curProjects []*model.Project - err := impl.db.Find(&curProjects).Error - if err != nil { - return nil, errcode.New(errcode.DBError, "find all projects error:%v", err) - } - for k, v := range consts.RegisteredProjects { - find := false - for _, cur := range curProjects { - if k == cur.ProjectId { - find = true - break - } - } - if !find { - po := &model.Project{ProjectId: k, Name: v.Name} - err := impl.db.Create(po).Error - if err != nil { - return nil, errcode.New(errcode.DBError, "create project:%v error:%v", k, err) - } - curProjects = append(curProjects, po) - } - } - return curProjects, nil +var projectFieldsDescInfo []*dto.CommonDtoFieldDesc + +func init() { + et := entity.FromProjectPo(&model.Project{}) + projectFieldsDescInfo = et.ToCommonResource().GetDtoFieldsDescInfo(et) } -func (impl *projectRepoImpl) List() ([]*entity.Project, error) { +func (impl *projectRepoImpl) List() ([]*dto.CommonDtoFieldDesc, []*entity.Project, error) { list := make([]*model.Project, 0) err := impl.db.Find(&list).Error if err != nil { - return nil, errcode.New(errcode.DBError, "list project error:%v", err) + return projectFieldsDescInfo, nil, errcode.New(errcode.DBError, "list project error:%v", err) } - + list1 := make([]*entity.Project, 0, len(list)) for _, v := range list { - list1 = append(list1, (&entity.CommonResource{}).FromPo(v).ToProjectEntity()) + list1 = append(list1, entity.FromProjectPo(v)) } - return list1, nil + return projectFieldsDescInfo, list1, nil } -func (impl *projectRepoImpl) GetByProjectId(id string) (*entity.Project, bool, error) { +func (impl *projectRepoImpl) GetById(id int) ([]*dto.CommonDtoFieldDesc, *entity.Project, bool, error) { po := &model.Project{} - err := impl.db.Where("project_id=?", id).Find(po).Error + err := impl.db.Where("id=?", id).First(po).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return (&entity.CommonResource{}).FromPo(po).ToProjectEntity(), false, nil + return projectFieldsDescInfo, entity.FromProjectPo(&model.Project{}), false, nil } } - return (&entity.CommonResource{}).FromPo(po).ToProjectEntity(), true, nil + return projectFieldsDescInfo, entity.FromProjectPo(po), true, nil +} + +func (impl *projectRepoImpl) Create(obj dto.CommonDtoValues) (*entity.Project, error) { + et := entity.FromProjectPo(&model.Project{}).FromDto(obj) + err := impl.db.Create(et).Error + if err != nil { + return et, errcode.New(errcode.DBError, "create project obj:%+v error:%v", et, err) + } + return et, nil +} + +func (impl *projectRepoImpl) Edit(obj dto.CommonDtoValues) error { + et := entity.FromProjectPo(&model.Project{}).FromDto(obj) + err := impl.db.Where("id=?", et.Po.GetId()).Updates(et.Po).Error + if err != nil { + return errcode.New(errcode.DBError, "edit project obj:%+v error:%v", et, err) + } + return nil +} +func (impl *projectRepoImpl) Delete(id int) (*entity.Project, bool, error) { + _, et, find, err := impl.GetById(id) + if err != nil { + return nil, false, err + } + if !find { + return et, false, nil + } + + err = impl.db.Where("id=?", id).Unscoped().Delete(&model.Project{}).Error + if err != nil { + return nil, false, errcode.New(errcode.DBError, "delete project obj:%+v error:%v", id, err) + } + return et, true, nil } diff --git a/admin/apps/game/domain/repo/server.go b/admin/apps/game/domain/repo/server.go index 94396ff..afbfd71 100644 --- a/admin/apps/game/domain/repo/server.go +++ b/admin/apps/game/domain/repo/server.go @@ -10,7 +10,7 @@ import ( var ServerRepoInstance IServerRepo type IServerRepo interface { - List(projectId string) ([]*entity.CommonResource, error) + List(projectId int) ([]*entity.CommonResource, error) } func NewServerRepo(db *gorm.DB) IServerRepo { @@ -21,7 +21,7 @@ type serverRepoImpl struct { db *gorm.DB } -func (impl *serverRepoImpl) List(projectId string) ([]*entity.CommonResource, error) { +func (impl *serverRepoImpl) List(projectId int) ([]*entity.CommonResource, error) { list := make([]*model.Server, 0) err := impl.db.Where("project_id = ?", projectId).Find(&list).Error if err != nil { diff --git a/admin/apps/game/model/account.go b/admin/apps/game/model/account.go index 0487e67..9aab85a 100644 --- a/admin/apps/game/model/account.go +++ b/admin/apps/game/model/account.go @@ -11,6 +11,7 @@ func init() { // Account 空表,就是用来兼容资源增删改查公共操作的,实际列举账号等都是走各个项目api拉取 type Account struct { + ProjectId int Account string `name:"账号" json:"account"` ServerConfId string `name:"区服id" json:"serveId"` RoleIds []string `gorm:"type:json;serializer:json" name:"角色id列表" json:"roleIds"` diff --git a/admin/apps/game/model/ban.go b/admin/apps/game/model/ban.go index d1e3bac..725d1b4 100644 --- a/admin/apps/game/model/ban.go +++ b/admin/apps/game/model/ban.go @@ -2,6 +2,7 @@ package model import ( "admin/apps/game/model/dto" + "admin/internal/consts" "admin/internal/db" "time" ) @@ -11,13 +12,14 @@ func init() { } type Ban struct { - ID int `gorm:"primarykey" readonly:"true"` - ProjectId string `gorm:"type:varchar(255);uniqueIndex:idx_ban"` - ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetServerConfIDChoices" uneditable:"true"` - BanType string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" uneditable:"true"` - Value string `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁值" required:"true" desc:"根据封禁类型填对应值,例如ip就填ip地址" uneditable:"true"` - BanReason string `name:"封禁理由" desc:"封禁理由,会推送给客户端弹窗" required:"true"` - ExpireAt time.Time `name:"封禁到期时间" desc:"封禁到期时间,0表示永久封禁"` + ID int `gorm:"primarykey" readonly:"true"` + ProjectId int `gorm:"uniqueIndex:idx_ban"` + ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetServerConfIDChoices" uneditable:"true"` + BanType string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" uneditable:"true"` + Value string `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁值" required:"true" desc:"根据封禁类型填对应值,例如ip就填ip地址" uneditable:"true"` + BanReason string `name:"封禁描述" desc:"封禁描述,入库记录的描述信息" required:"true"` + BanNotifyReason string `name:"封禁弹窗" desc:"封禁弹窗内容,会推送给客户端弹窗" required:"true"` + ExpireAt time.Time `name:"封禁到期时间" desc:"封禁到期时间,0表示永久封禁"` CreatedAt time.Time `readonly:"true"` } @@ -30,35 +32,48 @@ func (m *Ban) GetId() int { return m.ID } -func (m *Ban) GetServerConfIDChoices(projectId string) []*dto.CommonDtoFieldChoice { - return getChoiceServers(projectId) +func (m *Ban) GetServerConfIDChoices(project *Project) []*dto.CommonDtoFieldChoice { + return getChoiceServers(project) } -func (m *Ban) GetBanTypeChoices(projectId string) []*dto.CommonDtoFieldChoice { +func (m *Ban) GetBanTypeChoices(project *Project) []*dto.CommonDtoFieldChoice { + switch project.ProjectType { + case consts.RegisteredProjectId_shenmodalu: + return []*dto.CommonDtoFieldChoice{ + { + Desc: "角色", + Value: consts.BanType_Role, + }, + { + Desc: "角色发言", + Value: consts.BanType_RoleChat, + }, + } + } return []*dto.CommonDtoFieldChoice{ { Desc: "账号", - Value: "account", + Value: consts.BanType_Account, }, { Desc: "角色", - Value: "role", + Value: consts.BanType_Role, }, { Desc: "IP", - Value: "ip", + Value: consts.BanType_Ip, }, { Desc: "账号发言", - Value: "account_chat", + Value: consts.BanType_AccountChat, }, { Desc: "角色发言", - Value: "role_chat", + Value: consts.BanType_RoleChat, }, { Desc: "设备号", - Value: "device_id", + Value: consts.BanType_Device, }, } } diff --git a/admin/apps/game/model/devicepush.go b/admin/apps/game/model/devicepush.go index 5142122..60d4a6f 100644 --- a/admin/apps/game/model/devicepush.go +++ b/admin/apps/game/model/devicepush.go @@ -11,7 +11,7 @@ func init() { type DevicePush struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string + ProjectId int Account string `name:"账号" desc:"给账号所属的设备做推送"` Title string `name:"通知标题" desc:""` Content string `name:"通知内容"` diff --git a/admin/apps/game/model/dto/common.go b/admin/apps/game/model/dto/common.go index e3f5247..4b68a30 100644 --- a/admin/apps/game/model/dto/common.go +++ b/admin/apps/game/model/dto/common.go @@ -6,18 +6,6 @@ type WebRspData struct { Data any `json:"data"` } -type ResourceInitInfo struct { - Resource string `json:"resource"` - Desc string `json:"desc"` - ShowMethods []string `json:"show_methods"` -} - -type ProjectInitInfo struct { - ProjectId string `json:"project_id"` - ProjectName string `json:"project_name"` - ResourceList []*ResourceInitInfo `json:"resource_list"` -} - type CommonDtoFieldChoice struct { Desc string `json:"desc"` Value any `json:"value"` diff --git a/admin/apps/game/model/dto/msg.go b/admin/apps/game/model/dto/msg.go index 5b56944..8868fdf 100644 --- a/admin/apps/game/model/dto/msg.go +++ b/admin/apps/game/model/dto/msg.go @@ -46,11 +46,3 @@ type CommandListReq struct { type CommandListRsp struct { List []*PathInfo `json:"list"` } - -type ResourceListRsp struct { - List []*ResourceInitInfo `json:"list"` -} - -type RoutesListRsp struct { - Projects []*ProjectInitInfo `json:"projects"` -} diff --git a/admin/apps/game/model/globalmail.go b/admin/apps/game/model/globalmail.go index 46600f8..50e92da 100644 --- a/admin/apps/game/model/globalmail.go +++ b/admin/apps/game/model/globalmail.go @@ -14,7 +14,7 @@ var ProjectsAllItems = make(map[string][]*dto.CommonDtoFieldChoice) type GlobalMail struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string + ProjectId int ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" type:"[]string" choices:"GetChoiceServers" multi_choice:"true"` Title string `name:"邮件标题" required:"true"` Content string `name:"邮件内容" required:"true"` @@ -31,10 +31,10 @@ func (m *GlobalMail) GetId() int { return m.ID } -func (m *GlobalMail) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice { - return getChoiceServers(projectId) +func (m *GlobalMail) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { + return getChoiceServers(project) } -func (m *GlobalMail) GetChoiceItems(projectId string) []*dto.CommonDtoFieldChoice { - return ProjectsAllItems[projectId] +func (m *GlobalMail) GetChoiceItems(project *Project) []*dto.CommonDtoFieldChoice { + return ProjectsAllItems[project.ProjectType] } diff --git a/admin/apps/game/model/imodel.go b/admin/apps/game/model/imodel.go index 17d87f0..68f8967 100644 --- a/admin/apps/game/model/imodel.go +++ b/admin/apps/game/model/imodel.go @@ -9,10 +9,8 @@ type IModel interface { GetId() int } -var GetProjectServersHandler func(projectId string) ([]*Server, error) - -func getChoiceServers(projectId string) []*dto.CommonDtoFieldChoice { - servers, err := GetProjectServersHandler(projectId) +func getChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { + servers, err := new(Server).ListByProjectId(project.ID) if err != nil { panic(err) } diff --git a/admin/apps/game/model/notice.go b/admin/apps/game/model/notice.go index 9f3d836..00cb493 100644 --- a/admin/apps/game/model/notice.go +++ b/admin/apps/game/model/notice.go @@ -12,7 +12,7 @@ func init() { type Notice struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string + ProjectId int ServerIDs []int `gorm:"type:json;serializer:json" name:"公告生效服务器" desc:"为空表示所有服" choices:"GetChoiceServers"` Content string `name:"公告内容" required:"true"` StartAt time.Time `name:"开始时间" required:"true"` @@ -29,6 +29,6 @@ func (m *Notice) GetId() int { return m.ID } -func (m *Notice) GetChoiceServers(args ...any) []*dto.CommonDtoFieldChoice { - return getChoiceServers(args[0].(string)) +func (m *Notice) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { + return getChoiceServers(project) } diff --git a/admin/apps/game/model/project.go b/admin/apps/game/model/project.go index 3462283..e441ee7 100644 --- a/admin/apps/game/model/project.go +++ b/admin/apps/game/model/project.go @@ -1,7 +1,12 @@ package model import ( + "admin/apps/game/model/dto" + "admin/internal/consts" "admin/internal/db" + "admin/internal/global" + "errors" + "gorm.io/gorm" "time" ) @@ -11,10 +16,10 @@ func init() { // Project 游戏项目,例如神谕、神魔大陆 type Project struct { - ID int `gorm:"primarykey" readonly:"true"` - ProjectId string `gorm:"type:varchar(255);unique" name:"项目id" readonly:"true"` - Name string `gorm:"primarykey" required:"true" name:"项目名" readonly:"true" uneditable:"true"` - Desc string `name:"项目描述"` + ID int `gorm:"primarykey" readonly:"true"` + ProjectType string `gorm:"type:varchar(255);uniqueIndex:project" name:"项目类型" choices:"GetProjectChoices" uneditable:"true" required:"true"` + Name string `gorm:"primarykey;uniqueIndex:project" required:"true" name:"项目名" desc:"例如神魔大陆-正式服、神魔大陆-主播服" uneditable:"true"` + Desc string `name:"项目描述"` // command_list接口服务器地址,为空代表由由项目下各个逻辑服提供command_list. // 取决于每个项目改造难度: // 不为空就代表项目要实现一个自己统一对外暴露的gm调用服务对内聚合、分发指令执行,本后台执行指令只调用一次; @@ -32,3 +37,20 @@ func (lm *Project) TableName() string { func (m *Project) GetId() int { return m.ID } + +func (m *Project) GetProjectChoices(_ *Project) []*dto.CommonDtoFieldChoice { + return []*dto.CommonDtoFieldChoice{ + {Desc: "神魔大陆", Value: consts.RegisteredProjectId_shenmodalu, Type: 0}, + } +} + +func (p *Project) GetByID() (bool, error) { + err := global.GLOB_DB.Where("id=?", p.ID).First(p).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/admin/apps/game/model/rewardcode.go b/admin/apps/game/model/rewardcode.go index dc22547..d68aa87 100644 --- a/admin/apps/game/model/rewardcode.go +++ b/admin/apps/game/model/rewardcode.go @@ -11,7 +11,7 @@ func init() { type RewardCode struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string + ProjectId int Group int `name:"奖励码组"` Code int `name:"奖励码"` diff --git a/admin/apps/game/model/role.go b/admin/apps/game/model/role.go index d8aba89..a303ea1 100644 --- a/admin/apps/game/model/role.go +++ b/admin/apps/game/model/role.go @@ -11,6 +11,7 @@ func init() { // Role 空表,就是用来兼容资源增删改查公共操作的,实际列举账号等都是走各个项目api拉取 type Role struct { + ProjectId int RoleId string `name:"角色ID" json:"roleId"` Account string `name:"账号" json:"account"` ServerConfId string `name:"区服id" json:"serverId"` diff --git a/admin/apps/game/model/rolemail.go b/admin/apps/game/model/rolemail.go index d2f6f90..c890145 100644 --- a/admin/apps/game/model/rolemail.go +++ b/admin/apps/game/model/rolemail.go @@ -18,7 +18,7 @@ type MailAttachItem struct { type RoleMail struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string + ProjectId int RoleIDs []string `gorm:"type:json;serializer:json" name:"生效的角色id" desc:"生效的角色id,逗号分隔多个" required:"true"` ServerID string `name:"所属区服" choices:"GetChoiceServers" required:"true"` Title string `name:"邮件标题" required:"true"` @@ -36,10 +36,10 @@ func (m *RoleMail) GetId() int { return m.ID } -func (m *RoleMail) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice { - return getChoiceServers(projectId) +func (m *RoleMail) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { + return getChoiceServers(project) } -func (m *RoleMail) GetChoiceItems(projectId string) []*dto.CommonDtoFieldChoice { - return ProjectsAllItems[projectId] +func (m *RoleMail) GetChoiceItems(project *Project) []*dto.CommonDtoFieldChoice { + return ProjectsAllItems[project.ProjectType] } diff --git a/admin/apps/game/model/server.go b/admin/apps/game/model/server.go index d242f26..8b365a7 100644 --- a/admin/apps/game/model/server.go +++ b/admin/apps/game/model/server.go @@ -2,6 +2,7 @@ package model import ( "admin/internal/db" + "admin/internal/global" "time" ) @@ -12,7 +13,7 @@ func init() { // Server 逻辑服 type Server struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string `gorm:"type:varchar(200);uniqueIndex:idx_server"` + ProjectId int `gorm:"uniqueIndex:idx_server"` ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" uneditable:"true"` Desc string `name:"描述"` // command_list接口服务器地址,为空代表由由项目统一提供command_list. @@ -32,3 +33,9 @@ func (lm *Server) TableName() string { func (m *Server) GetId() int { return m.ID } + +func (m *Server) ListByProjectId(projectId int) ([]*Server, error) { + list := make([]*Server, 0) + err := global.GLOB_DB.Where("project_id=?", projectId).Find(&list).Error + return list, err +} diff --git a/admin/apps/game/model/whitelist.go b/admin/apps/game/model/whitelist.go index 57350ca..d60c851 100644 --- a/admin/apps/game/model/whitelist.go +++ b/admin/apps/game/model/whitelist.go @@ -12,7 +12,7 @@ func init() { type WhiteList struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId string `gorm:"type:varchar(256);uniqueIndex:idx_whitelist"` + ProjectId int `gorm:"uniqueIndex:idx_whitelist"` ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers"` Account string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"` Desc string `name:"描述"` @@ -28,6 +28,6 @@ func (m *WhiteList) GetId() int { return m.ID } -func (m *WhiteList) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice { - return getChoiceServers(projectId) +func (m *WhiteList) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { + return getChoiceServers(project) } diff --git a/admin/apps/game/server/ctl_game.go b/admin/apps/game/server/ctl_game.go index ddf592f..1e05bb8 100644 --- a/admin/apps/game/server/ctl_game.go +++ b/admin/apps/game/server/ctl_game.go @@ -6,32 +6,10 @@ import ( "admin/internal/context" "admin/lib/httpclient" "admin/lib/xlog" + "strconv" "strings" ) -func (ctl *controller) GetRoutes(ctx *context.WebContext, params *dto.NilReq, rsp *dto.RoutesListRsp) error { - projects, err := ctl.svc.GetProjectList() - if err != nil { - return err - } - - for _, project := range projects { - projectDto := &dto.ProjectInitInfo{ - ProjectId: project.ProjectPo.ProjectId, - ProjectName: project.ProjectPo.Name, - } - for _, v := range ctl.svc.GetSupportResourcesList() { - if v.Resource == consts.ResourcesName_Project { - continue - } - projectDto.ResourceList = append(projectDto.ResourceList, v) - } - rsp.Projects = append(rsp.Projects, projectDto) - } - - return nil -} - func (ctl *controller) CommandList(ctx *context.WebContext, params *dto.CommandListReq, rsp *dto.CommandListRsp) error { url := params.Addr + "/api/commandlist" @@ -93,7 +71,7 @@ func (ctl *controller) getProjectResourceCommandApiAddr(ctx *context.WebContext) return ctl.svc.GetProjectInvokeApiAddr(projectId, nil) } -func getCtxURIProjectIdAndResource(ctx *context.WebContext) (string, string) { +func getCtxURIProjectIdAndResource(ctx *context.WebContext) (int, string) { return getCtxURIProjectId(ctx), getCtxURIResource(ctx) } @@ -107,9 +85,10 @@ func getCtxURIResource(ctx *context.WebContext) string { return resource } -func getCtxURIProjectId(ctx *context.WebContext) string { +func getCtxURIProjectId(ctx *context.WebContext) int { projectId := ctx.GinCtx().Param("projectId") - return projectId + projectId1, _ := strconv.Atoi(projectId) + return projectId1 } func (ctl *controller) apiRequest() { diff --git a/admin/apps/game/server/middleware.go b/admin/apps/game/server/middleware.go new file mode 100644 index 0000000..1e06e27 --- /dev/null +++ b/admin/apps/game/server/middleware.go @@ -0,0 +1,72 @@ +package server + +import ( + apiUser "admin/apps/user/api" + "admin/internal/context" + "admin/internal/errcode" + "admin/internal/permission" +) + +func (srv *Server) CheckToken(ctx *context.WebContext) { + err := ctx.ExtractHeader() + if err != nil { + ctx.Fail(errcode.New(errcode.HeaderParamsInvalid, "header invalid")) + return + } + + authRsp, err := apiUser.GetUserApiInstance().Auth(ctx, &apiUser.AuthReq{ + Token: ctx.Header.Token, + UserId: ctx.Header.UserId, + }) + if err != nil { + ctx.Fail(err) + return + } + + ctx.GinCtx().Set("userInfo", authRsp) + return +} + +func (srv *Server) CheckPermissions(ctx *context.WebContext) { + userInfoI, find := ctx.GinCtx().Get("userInfo") + if !find { + ctx.Fail(errcode.New(errcode.TokenInvalid, "not found ctx userInfo")) + return + } + userInfo, _ := userInfoI.(*apiUser.UserInfo) + + projectId, resource := getCtxURIProjectIdAndResource(ctx) + if projectId <= 0 { + if ctx.GinCtx().Request.URL.Path == "/api/project" { + if userInfo.Character != "admin" { + ctx.Fail(errcode.New(errcode.NoPermission, "user %v is not admin, can't operate project", ctx.Header.UserId)) + return + } + ctx.GinCtx().Next() + return + } + ctx.Fail(errcode.New(errcode.NoPermission, "project_id %v in ctx invalid", projectId)) + return + } + if resource == "" { + ctx.Fail(errcode.New(errcode.NoPermission, "resource %v in ctx invalid", resource)) + return + } + + reqPermission := permission.GetProjectResourcePermission(projectId, resource, ctx.GinCtx().Request.Method) + + if userInfo.Character == "admin" { + ctx.GinCtx().Next() + return + } + + for _, v := range userInfo.Permissions { + if reqPermission == v { + ctx.GinCtx().Next() + return + } + } + + ctx.Fail(errcode.New(errcode.NoPermission, "user %v don't have permission %v", ctx.Header.UserId, reqPermission)) + return +} diff --git a/admin/apps/game/server/route.go b/admin/apps/game/server/route.go index efdc1a3..b379aae 100644 --- a/admin/apps/game/server/route.go +++ b/admin/apps/game/server/route.go @@ -25,6 +25,4 @@ func (srv *Server) Route(engine *web.Engine) { resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) } } - - apiGroup.Get("/routes", "获取路由列表,用于客户端生成前端操作菜单", consts.WebPathPermit_Read, srv.ctl.GetRoutes) } diff --git a/admin/apps/game/service/service.go b/admin/apps/game/service/service.go index 89a3747..4a42237 100644 --- a/admin/apps/game/service/service.go +++ b/admin/apps/game/service/service.go @@ -1,6 +1,7 @@ package service import ( + "admin/apps/game/api" "admin/apps/game/domain" "admin/apps/game/model/dto" "admin/internal/consts" @@ -20,14 +21,15 @@ func New(db *gorm.DB) (*Service, error) { resourceSvc: domain.NewCommonResourceService(db), projectSvc: domain.NewProjectService(db), } - err := svc.ensureProjectsDBData() - if err != nil { - return nil, err - } + api.RegisterGameApi(svc) + //err := svc.ensureProjectsDBData() + //if err != nil { + // return nil, err + //} return svc, nil } -func (svc *Service) CommonList(ctx context.Context, projectId string, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) { +func (svc *Service) CommonList(ctx context.Context, projectId int, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) { var ( query string args []any @@ -42,24 +44,24 @@ func (svc *Service) CommonList(ctx context.Context, projectId string, resourceNa return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, Rows: rows}, err } -func (svc *Service) CommonPost(ctx context.Context, projectId string, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) { +func (svc *Service) CommonPost(ctx context.Context, projectId int, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) { if resourceName != consts.ResourcesName_Project { params["ProjectId"] = projectId } return svc.resourceSvc.Create(projectId, resourceName, params) } -func (svc *Service) CommonPut(ctx context.Context, projectId string, resourceName string, params dto.CommonDtoValues) error { +func (svc *Service) CommonPut(ctx context.Context, projectId int, resourceName string, params dto.CommonDtoValues) error { if resourceName != consts.ResourcesName_Project { params["ProjectId"] = projectId } return svc.resourceSvc.Edit(projectId, resourceName, params) } -func (svc *Service) CommonDelete(ctx context.Context, projectId string, resourceName string, id int) error { +func (svc *Service) CommonDelete(ctx context.Context, projectId int, resourceName string, id int) error { return svc.resourceSvc.Delete(projectId, resourceName, id) } -func (svc *Service) GetSupportResourcesList() []*dto.ResourceInitInfo { - return svc.resourceSvc.GetSupportResourcesList() +func (svc *Service) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo { + return svc.resourceSvc.GetSupportResourcesList(permissions) } diff --git a/admin/apps/game/service/service_project.go b/admin/apps/game/service/service_project.go index 234f387..a4cfa85 100644 --- a/admin/apps/game/service/service_project.go +++ b/admin/apps/game/service/service_project.go @@ -1,15 +1,68 @@ package service -import "admin/apps/game/domain/entity" +import ( + "admin/apps/game/api" + "admin/apps/game/domain/entity" + "admin/internal/consts" + "admin/internal/permission" +) + +func (svc *Service) GetRoutes(req *api.GetRoutesReq) (*api.GetRoutesRsp, error) { + rsp := &api.GetRoutesRsp{} + rsp.Projects = make([]*api.ProjectInitInfo, 0) + projects, err := svc.GetProjectList() + if err != nil { + return nil, err + } + + for _, project := range projects { + projectDto := &api.ProjectInitInfo{ + ProjectId: project.Po.ID, + ProjectName: project.Po.Name, + } + for _, v := range svc.GetSupportResourcesList(req.Permissions) { + if v.Resource == consts.ResourcesName_Project { + continue + } + permitV := &api.ResourceInitInfo{ + Resource: v.Resource, + Desc: v.Desc, + ShowMethods: make([]string, 0), + MethodsPermissionStr: make([]string, 0), + } + + for _, resourceMethod := range v.ShowMethods { + curMethodPermissionStr := permission.GetProjectResourcePermission(project.Po.ID, v.Resource, resourceMethod) + v.MethodsPermissionStr = append(v.MethodsPermissionStr, curMethodPermissionStr) + + findPermit := false + for _, hasPermission := range req.Permissions { + if curMethodPermissionStr == hasPermission { + // 有权限 + findPermit = true + break + } + } + + if req.IsAdmin || findPermit { + permitV.ShowMethods = append(permitV.ShowMethods, resourceMethod) + permitV.MethodsPermissionStr = append(permitV.MethodsPermissionStr, curMethodPermissionStr) + } + } + + projectDto.ResourceList = append(projectDto.ResourceList, permitV) + projectDto.ResourceTotalList = append(projectDto.ResourceList, v) + } + rsp.Projects = append(rsp.Projects, projectDto) + } + return rsp, nil +} func (svc *Service) GetProjectList() ([]*entity.Project, error) { - return svc.projectSvc.List() + _, _, list, err := svc.projectSvc.List() + return list, err } -func (svc *Service) ensureProjectsDBData() error { - return svc.projectSvc.EnsureProjectsDBData() -} - -func (svc *Service) GetProjectInvokeApiAddr(projectId string, serverIds []int) ([]string, error) { +func (svc *Service) GetProjectInvokeApiAddr(projectId int, serverIds []int) ([]string, error) { return svc.projectSvc.GetProjectInvokeApiAddr(projectId, serverIds) } diff --git a/admin/apps/user/api/api_user.go b/admin/apps/user/api/api_user.go new file mode 100644 index 0000000..fc16cf9 --- /dev/null +++ b/admin/apps/user/api/api_user.go @@ -0,0 +1,40 @@ +package api + +import "context" + +var userApiInstance IUserApi + +func GetUserApiInstance() IUserApi { + return userApiInstance +} + +type IUserApi interface { + Auth(ctx context.Context, req *AuthReq) (*AuthRsp, error) +} + +func RegisterUserApiHandler(handler IUserApi) { + userApiInstance = handler +} + +type AuthReq struct { + Token string + UserId int + Url string +} + +type UserInfo struct { + UserId int `json:"user_id"` + NickName string `json:"nick_name"` + Icon string `json:"icon"` + Character string `json:"character"` + Permissions []string `json:"permissions"` +} + +type TokenInfo struct { + Token string `json:"token"` + ExpireAt int64 `json:"expire_at"` +} +type AuthRsp struct { + User *UserInfo + Token *TokenInfo +} diff --git a/admin/apps/user/boot.go b/admin/apps/user/boot.go index 53e3e8d..1136544 100644 --- a/admin/apps/user/boot.go +++ b/admin/apps/user/boot.go @@ -1,8 +1,19 @@ package user -import "admin/lib/node" +import ( + "admin/apps/user/server" + "admin/apps/user/service" + "admin/internal/global" + "admin/lib/node" +) func initFun(app *node.Application) error { + svc, err := service.New(global.GLOB_DB) // 初始化应用服务 + if err != nil { + panic(err) + } + srv := server.New(svc) // 初始化http服务 + srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由 return nil } diff --git a/admin/apps/user/domain/comm_resource.go b/admin/apps/user/domain/comm_resource.go new file mode 100644 index 0000000..d09057d --- /dev/null +++ b/admin/apps/user/domain/comm_resource.go @@ -0,0 +1,146 @@ +package domain + +import ( + "admin/apps/user/domain/entity" + "admin/apps/user/domain/repo" + "admin/apps/user/model" + "admin/apps/user/model/dto" + "admin/internal/consts" + "fmt" + "gorm.io/gorm" +) + +type CommonResourceService struct { + userRepo repo.IUserRepo + tokenRepo repo.ITokenRepo +} + +func initCommonResourcesRepo(db *gorm.DB) { + r(consts.ResourcesName_SysUser, "用户管理", repo.NewCommonResourceRepo(db, &model.User{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) + r(consts.ResourcesName_SysCharacter, "角色管理", repo.NewCommonResourceRepo(db, &model.Character{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) +} + +func NewCommonResourceService(db *gorm.DB) *CommonResourceService { + initCommonResourcesRepo(db) + svc := &CommonResourceService{ + userRepo: repo.NewUserRepo(db), + tokenRepo: repo.NewTokenRepo(db), + } + return svc +} + +func (svc *CommonResourceService) List(resource string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { + fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(pageNo, pageLen, extraQuery, args...) + if err != nil { + return nil, nil, err + } + retList := make([]dto.CommonDtoValues, 0, len(etList)) + for _, v := range etList { + retList = append(retList, v.ToCommonDto()) + } + + return fieldsDescInfo, retList, nil +} + +func (svc *CommonResourceService) GetById(resource string, id int) ([]*dto.CommonDtoFieldDesc, dto.CommonDtoValues, *entity.CommonResource, bool, error) { + fieldsDescInfo, et, find, err := findCommResourceRepo(resource).GetById(id) + if err != nil { + return nil, nil, nil, false, err + } + + return fieldsDescInfo, et.ToCommonDto(), et, find, nil +} + +func (svc *CommonResourceService) Create(resource string, dtoObj dto.CommonDtoValues) (dto.CommonDtoValues, *entity.CommonResource, error) { + if resource == consts.ResourcesName_SysUser { + + } + + et, err := findCommResourceRepo(resource).Create(dtoObj) + if err != nil { + return nil, et, err + } + + // 返回新的实体,插入数据库之后会填入数据库id,所以要返给前端刷新id数据 + return et.ToCommonDto(), et, err +} + +func (svc *CommonResourceService) Edit(resource string, dtoObj dto.CommonDtoValues) error { + err := findCommResourceRepo(resource).Edit(dtoObj) + if err != nil { + return err + } + + return nil +} + +func (svc *CommonResourceService) Delete(resource string, id int) error { + _, _, err := findCommResourceRepo(resource).Delete(id) + if err != nil { + return err + } + + return nil +} + +func (svc *CommonResourceService) GetSupportResourcesList() []*dto.ResourceInitInfo { + list := make([]*dto.ResourceInitInfo, 0, len(commResourcesRepo)) + for _, v := range commResourcesRepo { + list = append(list, &dto.ResourceInitInfo{ + Resource: v.Resource, + Desc: v.Desc, + ShowMethods: v.ShowMethods, + }) + } + return list +} + +const ( + ShowMethod_Get = 1 + ShowMethod_Post = 1 << 1 + ShowMethod_Put = 1 << 2 + ShowMethod_Delete = 1 << 3 +) + +type resourceRepoInfo struct { + Resource string + Desc string + Repo repo.ICommonResourceRepo + ShowMethods []string + showMethods int +} + +var commResourcesRepo = make([]*resourceRepoInfo, 0) + +func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) { + curRepo := &resourceRepoInfo{ + Resource: resource, + Desc: desc, + Repo: repo, + } + + if showMethods&ShowMethod_Get == ShowMethod_Get { + curRepo.ShowMethods = append(curRepo.ShowMethods, "get") + } + if showMethods&ShowMethod_Post == ShowMethod_Post { + curRepo.ShowMethods = append(curRepo.ShowMethods, "post") + } + if showMethods&ShowMethod_Put == ShowMethod_Put { + curRepo.ShowMethods = append(curRepo.ShowMethods, "put") + } + if showMethods&ShowMethod_Delete == ShowMethod_Delete { + curRepo.ShowMethods = append(curRepo.ShowMethods, "delete") + } + + commResourcesRepo = append(commResourcesRepo, curRepo) +} + +func findCommResourceRepo(resource string) repo.ICommonResourceRepo { + for _, v := range commResourcesRepo { + if v.Resource == resource { + return v.Repo + } + } + panic(fmt.Errorf("not found Resource:%v", resource)) + return nil +} diff --git a/admin/apps/user/domain/entity/comm_resouce.go b/admin/apps/user/domain/entity/comm_resouce.go new file mode 100644 index 0000000..6fc1451 --- /dev/null +++ b/admin/apps/user/domain/entity/comm_resouce.go @@ -0,0 +1,67 @@ +package entity + +import ( + "admin/apps/user/model" + "admin/apps/user/model/dto" + "reflect" +) + +type CommonResource struct { + Po model.IModel +} + +func (et *CommonResource) FromPo(po model.IModel) *CommonResource { + et.Po = po + return et +} + +func (et *CommonResource) FromDto(dto dto.CommonDtoValues) *CommonResource { + po := et.Po + + //to := reflect.TypeOf(Po) + vo := reflect.ValueOf(po).Elem() + to := reflect.TypeOf(po).Elem() + + for k, v := range dto { + fo := vo.FieldByName(k) + ft, find := to.FieldByName(k) + if !find { + continue + } + fo.Set(parseStr2FieldValue(ft, v)) + } + + return et +} + +func (et *CommonResource) ToPo() model.IModel { + return et.Po +} + +func (et *CommonResource) ToCommonDto() dto.CommonDtoValues { + return poToCommonDtoNo(et.Po) +} + +func (et *CommonResource) GetDtoFieldsDescInfo() []*dto.CommonDtoFieldDesc { + + ptrVo := reflect.ValueOf(et.Po) + + to := reflect.TypeOf(et.Po).Elem() + vo := ptrVo.Elem() + + obj := make([]*dto.CommonDtoFieldDesc, 0, to.NumField()) + + for i := 0; i < vo.NumField(); i++ { + ft := to.Field(i) + //fo := vo.Field(i) + + if ft.Tag.Get("server_use") == "true" { + continue + } + + f1 := getFieldTypeDtoDescInfo(ptrVo, ft) + obj = append(obj, f1) + } + + return obj +} diff --git a/admin/apps/user/domain/entity/user.go b/admin/apps/user/domain/entity/user.go new file mode 100644 index 0000000..4c76f52 --- /dev/null +++ b/admin/apps/user/domain/entity/user.go @@ -0,0 +1,36 @@ +package entity + +import ( + "admin/apps/user/model" + "admin/apps/user/model/dto" +) + +type User struct { + Po *model.User + Character *model.Character +} + +func FromUserPo(po *model.User, cPo *model.Character) *User { + return &User{Po: po, Character: cPo} +} + +func (et *User) GetUserId() int { + return et.Po.ID +} + +func (et *User) IsAdmin() bool { + return et.Character.Name == "admin" +} + +// GetPermissions 返回权限列表 +func (et *User) GetPermissions() []string { + return et.Character.Permissions +} + +func (et *User) ToDtoInfo() *dto.UserInfo { + return &dto.UserInfo{ + UserId: et.Po.ID, + NickName: et.Po.NickName, + Character: et.Po.CharacterName, + } +} diff --git a/admin/apps/user/domain/entity/utils.go b/admin/apps/user/domain/entity/utils.go new file mode 100644 index 0000000..f13858f --- /dev/null +++ b/admin/apps/user/domain/entity/utils.go @@ -0,0 +1,186 @@ +package entity + +import ( + "admin/apps/user/model/dto" + "admin/lib/xlog" + "fmt" + "gorm.io/gorm" + "reflect" + "strconv" + "strings" + "time" +) + +func poToCommonDtoNo(po any) dto.CommonDtoValues { + obj := make(dto.CommonDtoValues) + + to := reflect.TypeOf(po).Elem() + vo := reflect.ValueOf(po).Elem() + for i := 0; i < vo.NumField(); i++ { + ft := to.Field(i) + fo := vo.Field(i) + + if ft.Tag.Get("server_use") == "true" { + continue + } + + if ft.Name == "UserPass" { + obj[ft.Name] = "***" // 密码不下发了 + } else { + obj[ft.Name] = fo.Interface() + } + + if ft.Type.Name() == "Time" && ft.Type.PkgPath() == "time" { + obj[ft.Name] = fo.Interface().(time.Time).Format("2006/01/02 15:04:05") + } + } + + return obj +} + +func getFieldTypeDtoDescInfo(poValue reflect.Value, fieldType reflect.StructField) *dto.CommonDtoFieldDesc { + name := fieldType.Tag.Get("name") + if name == "" { + name = fieldType.Name + } + f1 := &dto.CommonDtoFieldDesc{ + Name: name, + Key: fieldType.Name, + Type: fieldType.Type.Name(), + HelpText: fieldType.Tag.Get("desc"), + Readonly: fieldType.Tag.Get("readonly") == "true", + Required: fieldType.Tag.Get("required") == "true", + Choices: make([]*dto.CommonDtoFieldChoice, 0), + MultiChoice: fieldType.Tag.Get("multi_choice") == "true", + Uneditable: fieldType.Tag.Get("uneditable") == "true", + } + + if f1.Key == "CreatedAt" { + f1.Name = "创建时间" + } + + if tagType := fieldType.Tag.Get("type"); tagType != "" { + f1.Type = tagType + } + + cf := fieldType.Tag.Get("choices") + if cf != "" { + method := poValue.MethodByName(cf) + if !method.IsValid() { + xlog.Warnf("po %v choices %v function not found in model", poValue.Type().Name(), cf) + } else { + rets := method.Call([]reflect.Value{}) + f1.Choices = rets[0].Interface().([]*dto.CommonDtoFieldChoice) + } + + } + + return f1 +} + +func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue reflect.Value) { + setValue := fmt.Sprintf("%v", rawValue) + var parsedValue any + 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" { + xlog.Debugf("time content:%v", setValue) + t, _ := time.ParseInLocation("2006/01/02 15:04:05", setValue, time.Local) + return reflect.ValueOf(t) + } + if typeName == "DeletedAt" { + return reflect.ValueOf(gorm.DeletedAt{}) + } + fallthrough + case reflect.Slice: + typeName := field.Type.String() + if typeName == "[]string" { + list := make([]string, 0) + if rawValueStr, ok := rawValue.(string); ok { + elems := strings.Split(rawValueStr, ",") + list = append(list, elems...) + // 空的字符串 + } else { + if rawValueList, ok := rawValue.([]string); ok { + for _, v := range rawValueList { + list = append(list, v) + } + } else { + for _, v := range rawValue.([]interface{}) { + list = append(list, v.(string)) + } + } + } + parsedValue = list + } + //else if typeName == "[]*model.MailAttachItem" { + // items := make([]*model.MailAttachItem, 0) + // if rawValueList, ok := rawValue.([]*model.MailAttachItem); ok { + // for _, item := range rawValueList { + // items = append(items, item) + // } + // } else { + // for _, itemI := range rawValue.([]interface{}) { + // item := &model.MailAttachItem{} + // for k, vI := range itemI.(map[string]any) { + // v := vI.(float64) + // if k == "id" { + // item.ID = int32(v) + // } else if k == "num" { + // item.Num = int64(v) + // } else if k == "item_type" { + // item.ItemType = int(v) + // } + // } + // items = append(items, item) + // } + // } + // + // parsedValue = items + //} + default: + panic(fmt.Errorf("暂时不支持的前后端交互字段类型:%v, 类型名:%v", field.Type.Kind(), field.Type.Name())) + } + return reflect.ValueOf(parsedValue) +} diff --git a/admin/apps/user/domain/iresource.go b/admin/apps/user/domain/iresource.go new file mode 100644 index 0000000..9acd6b7 --- /dev/null +++ b/admin/apps/user/domain/iresource.go @@ -0,0 +1,19 @@ +package domain + +import ( + "admin/apps/game/model/dto" +) + +type IRestfulEntity interface { + ToCommonDto() dto.CommonDtoValues +} + +type IRestfulResourceSvc interface { + List(pageNo, pageLen int, whereValues ...string) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error) + Post(obj dto.CommonDtoValues) (IRestfulEntity, error) + Put(obj dto.CommonDtoValues) (IRestfulEntity, error) + Delete(id int) error +} + +type IResourceOpPostHook interface { +} diff --git a/admin/apps/user/domain/repo/comm_resource.go b/admin/apps/user/domain/repo/comm_resource.go new file mode 100644 index 0000000..1075aca --- /dev/null +++ b/admin/apps/user/domain/repo/comm_resource.go @@ -0,0 +1,126 @@ +package repo + +import ( + "admin/apps/user/domain/entity" + "admin/apps/user/model" + "admin/apps/user/model/dto" + "admin/internal/errcode" + "admin/lib/passlib" + "admin/lib/xlog" + "errors" + "gorm.io/gorm" + "reflect" +) + +type ICommonResourceRepo interface { + List(pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) + GetById(id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) + Create(et dto.CommonDtoValues) (*entity.CommonResource, error) + Edit(et dto.CommonDtoValues) error + Delete(id int) (*entity.CommonResource, bool, error) +} + +func NewCommonResourceRepo(db *gorm.DB, poTemplate model.IModel) ICommonResourceRepo { + return newCommonResourceRepoImpl(db, poTemplate) +} + +type commonResourceRepoImpl struct { + db *gorm.DB + poTemplate model.IModel + fieldsDescInfoFun func() []*dto.CommonDtoFieldDesc +} + +func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonResourceRepoImpl { + fieldsInfo := (&entity.CommonResource{}).FromPo(poTemplate).GetDtoFieldsDescInfo + return &commonResourceRepoImpl{db: db, poTemplate: poTemplate, fieldsDescInfoFun: fieldsInfo} +} + +func (repo *commonResourceRepoImpl) List(pageNo, pageLen int, + extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { + + listType := reflect.New(reflect.SliceOf(reflect.TypeOf(repo.poTemplate))) + var err error + if extraQuery == "" { + err = repo.db.Find(listType.Interface()).Error + } else { + err = repo.db.Where(extraQuery, args...).Find(listType.Interface()).Error + } + if err != nil { + return nil, nil, errcode.New(errcode.DBError, "list resource %v error:%v", repo.poTemplate.TableName(), err) + } + + listType1 := listType.Elem() + listLen := listType1.Len() + + entityList := make([]*entity.CommonResource, 0, listLen) + for i := 0; i < listType1.Len(); i++ { + po := listType1.Index(i).Interface().(model.IModel) + et := &entity.CommonResource{} + et.FromPo(po) + entityList = append(entityList, et) + } + + return repo.fieldsDescInfoFun(), entityList, nil +} + +func (repo *commonResourceRepoImpl) GetById(id int) ([]*dto.CommonDtoFieldDesc, *entity.CommonResource, bool, error) { + po := repo.newEmptyPo() + err := repo.db.Where("id = ?", id).First(po).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return repo.fieldsDescInfoFun(), (&entity.CommonResource{}).FromPo(repo.newEmptyPo()), false, nil + } + return nil, nil, false, errcode.New(errcode.DBError, "get resource:%v by id:%v error:%v", repo.poTemplate.TableName(), id, err) + } + return repo.fieldsDescInfoFun(), (&entity.CommonResource{}).FromPo(po), true, nil +} + +func (repo *commonResourceRepoImpl) Create(dtoObj dto.CommonDtoValues) (*entity.CommonResource, error) { + if pass, find := dtoObj["UserPass"]; find { + dtoObj["UserPass"] = passlib.EncryptPassword(pass.(string)) + } + et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj) + err := repo.db.Create(et.Po).Error + if err != nil { + return et, errcode.New(errcode.DBError, "create resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), et, err) + } + return et, nil +} + +func (repo *commonResourceRepoImpl) Edit(dtoObj dto.CommonDtoValues) error { + xlog.Infof("update obj:%+v", dtoObj) + if pass, find := dtoObj["UserPass"]; find { + if pass.(string) == "***" { + // 没有改密码 + delete(dtoObj, "UserPass") + } else { + dtoObj["UserPass"] = passlib.EncryptPassword(pass.(string)) + } + } + et := (&entity.CommonResource{}).FromPo(repo.newEmptyPo()).FromDto(dtoObj) + err := repo.db.Where("id=?", et.Po.GetId()).Updates(et.Po).Error + if err != nil { + return errcode.New(errcode.DBError, "edit resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), et, err) + } + return nil +} + +func (repo *commonResourceRepoImpl) Delete(id int) (*entity.CommonResource, bool, error) { + _, et, find, err := repo.GetById(id) + if err != nil { + return nil, false, err + } + if !find { + return et, false, nil + } + + err = repo.db.Where("id=?", id).Unscoped().Delete(repo.poTemplate).Error + if err != nil { + return nil, false, errcode.New(errcode.DBError, "delete resource:%v obj:%+v error:%v", repo.poTemplate.TableName(), id, err) + } + return et, true, nil +} + +func (repo *commonResourceRepoImpl) newEmptyPo() model.IModel { + return reflect.New(reflect.TypeOf(repo.poTemplate).Elem()).Interface().(model.IModel) +} diff --git a/admin/apps/user/domain/repo/token.go b/admin/apps/user/domain/repo/token.go new file mode 100644 index 0000000..21da69a --- /dev/null +++ b/admin/apps/user/domain/repo/token.go @@ -0,0 +1,71 @@ +package repo + +import ( + "admin/apps/user/model" + "admin/lib/tokenlib" + "errors" + "gorm.io/gorm" + "time" +) + +type ITokenRepo interface { + GetToken(token string) (*model.Token, bool, error) + GetTokenByUserId(userId int) (*model.Token, bool, error) + CreateToken(userId int, dura time.Duration) (*model.Token, error) + DeleteToken(token string) error + DeleteTokenByUserId(userId int) error +} + +func NewTokenRepo(db *gorm.DB) ITokenRepo { + return &tokenRepoImpl{db: db} +} + +type tokenRepoImpl struct { + db *gorm.DB +} + +func (impl *tokenRepoImpl) GetToken(token string) (*model.Token, bool, error) { + info := &model.Token{} + err := impl.db.Where("token = ?", token).First(info).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return info, false, nil + } + return nil, false, err + } + return info, true, nil +} + +func (impl *tokenRepoImpl) GetTokenByUserId(userId int) (*model.Token, bool, error) { + info := &model.Token{} + err := impl.db.Where("user_id = ?", userId).First(info).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return info, false, nil + } + return nil, false, err + } + return info, true, nil +} + +func (impl *tokenRepoImpl) CreateToken(userId int, dura time.Duration) (*model.Token, error) { + tokenStr, err := tokenlib.GenToken(userId, dura) + if err != nil { + return nil, err + } + po := &model.Token{ + Token: tokenStr, + UserId: userId, + ExpireAt: time.Now().Add(dura), + } + err = impl.db.Create(po).Error + return po, err +} + +func (impl *tokenRepoImpl) DeleteToken(token string) error { + return impl.db.Where("token = ?", token).Unscoped().Delete(new(model.Token)).Error +} + +func (impl *tokenRepoImpl) DeleteTokenByUserId(userId int) error { + return impl.db.Where("user_id = ?", userId).Unscoped().Delete(new(model.Token)).Error +} diff --git a/admin/apps/user/domain/repo/user.go b/admin/apps/user/domain/repo/user.go new file mode 100644 index 0000000..f3b0c8a --- /dev/null +++ b/admin/apps/user/domain/repo/user.go @@ -0,0 +1,95 @@ +package repo + +import ( + "admin/apps/user/domain/entity" + "admin/apps/user/model" + "admin/internal/errcode" + "admin/lib/passlib" + "errors" + "gorm.io/gorm" + "strings" +) + +type IUserRepo interface { + GetById(userId int) (*entity.User, bool, error) + GetByUser(user string) (*entity.User, bool, error) + CreateAdminUsers() error +} + +func NewUserRepo(db *gorm.DB) IUserRepo { + return &userRepoImpl{db: db} +} + +type userRepoImpl struct { + db *gorm.DB +} + +func (impl *userRepoImpl) CreateAdminUsers() error { + adminCharacter := &model.Character{ + Name: "admin", + } + adminList := []*model.User{ + &model.User{ + UserName: "admin", + NickName: "admin", + UserPass: passlib.EncryptPassword("admin@123"), + CharacterName: adminCharacter.Name, + }, + } + + if err := impl.db.Create(adminCharacter).Error; err != nil { + if !strings.Contains(err.Error(), "Duplicate entry") { + return errcode.New(errcode.DBError, "create admin character fail:%v", err) + } + } + + if err := impl.db.Create(adminList).Error; err != nil { + if !strings.Contains(err.Error(), "Duplicate entry") { + return errcode.New(errcode.DBError, "create admin fail:%v", err) + } + } + + return nil +} + +func (impl *userRepoImpl) GetById(userId int) (*entity.User, bool, error) { + po := new(model.User) + chPo := new(model.Character) + err := impl.db.Where("id = ?", userId).First(po).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return entity.FromUserPo(po, chPo), false, nil + } + return nil, false, errcode.New(errcode.DBError, "get user by id:%v error:%v", userId, err) + } + err = impl.db.Where("name = ?", po.CharacterName).First(chPo).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return entity.FromUserPo(po, chPo), false, nil + } + return nil, false, errcode.New(errcode.DBError, "get character by id:%v error:%v", po.CharacterName, err) + } + + return entity.FromUserPo(po, chPo), true, nil +} + +func (impl *userRepoImpl) GetByUser(user string) (*entity.User, bool, error) { + po := new(model.User) + chPo := new(model.Character) + err := impl.db.Where("user_name = ?", user).First(po).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return entity.FromUserPo(po, chPo), false, nil + } + return nil, false, errcode.New(errcode.DBError, "get user by id:%v error:%v", user, err) + } + err = impl.db.Where("name = ?", po.CharacterName).First(chPo).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return entity.FromUserPo(po, chPo), false, nil + } + return nil, false, errcode.New(errcode.DBError, "get character by id:%v error:%v", po.CharacterName, err) + } + + return entity.FromUserPo(po, chPo), true, nil +} diff --git a/admin/apps/user/domain/user.go b/admin/apps/user/domain/user.go new file mode 100644 index 0000000..c3760a6 --- /dev/null +++ b/admin/apps/user/domain/user.go @@ -0,0 +1,64 @@ +package domain + +import ( + "admin/apps/user/domain/entity" + "admin/apps/user/model" + "admin/internal/errcode" + "admin/lib/passlib" + "time" +) + +func (svc *CommonResourceService) EnsureInitDB() error { + return svc.userRepo.CreateAdminUsers() +} + +func (svc *CommonResourceService) Login(user, pwd string) (*entity.User, error) { + userEt, find, err := svc.userRepo.GetByUser(user) + if err != nil { + return nil, err + } + if !find { + return nil, errcode.New(errcode.UserOrPassInValid, "") + } + + if !passlib.ComparePassword(userEt.Po.UserPass, pwd) { + return nil, errcode.New(errcode.UserOrPassInValid, "") + } + + return userEt, nil +} + +func (svc *CommonResourceService) GetUserById(userId int) (*entity.User, bool, error) { + et, find, err := svc.userRepo.GetById(userId) + if err != nil { + return et, false, err + } + return et, find, nil +} + +func (svc *CommonResourceService) GetOrCreateToken(userId int) (*model.Token, error) { + tokenInfo, find, err := svc.tokenRepo.GetTokenByUserId(userId) + if err != nil { + return nil, err + } + if !find { + tokenInfo, err = svc.tokenRepo.CreateToken(userId, time.Hour*24*15) // 15天过期的token,可以使用中加上token刷新功能 + if err != nil { + return nil, err + } + } + + return tokenInfo, nil +} + +func (svc *CommonResourceService) GetToken(userId int) (*model.Token, error) { + tokenInfo, find, err := svc.tokenRepo.GetTokenByUserId(userId) + if err != nil { + return nil, err + } + if !find { + return tokenInfo, errcode.New(errcode.TokenInvalid, "user %v token not found", userId) + } + + return tokenInfo, nil +} diff --git a/admin/apps/user/model/character.go b/admin/apps/user/model/character.go new file mode 100644 index 0000000..e1bb99c --- /dev/null +++ b/admin/apps/user/model/character.go @@ -0,0 +1,43 @@ +package model + +import ( + "admin/internal/db" + "admin/internal/global" + "admin/lib/xlog" + "time" +) + +func init() { + db.RegisterTableModels(Character{}) +} + +type Permission struct { +} + +// Character 角色权限组 +type Character struct { + ID int `gorm:"primarykey" readonly:"true"` + Name string `name:"角色名" desc:"区别一组用户的名字,例如qa、策划、运营" gorm:"type:varchar(255);unique" required:"true" uneditable:"true"` + // 权限列表,格式就是 ["project:::", "sys:user:get", ...], + // 例如项目3封禁功能的列表获取权限:"project:3:ban:list" + Permissions []string `gorm:"type:json;serializer:json" name:"权限列表" type:"Permissions"` + CreatedAt time.Time `readonly:"true"` +} + +func (m *Character) TableName() string { + return "character" +} + +func (m *Character) GetId() int { + return m.ID +} + +func (m *Character) List() []*Character { + list := make([]*Character, 0) + err := global.GLOB_DB.Find(&list).Error + if err != nil { + xlog.Errorf("get all character error:%v", err) + return list + } + return list +} diff --git a/admin/apps/user/model/dto/common.go b/admin/apps/user/model/dto/common.go new file mode 100644 index 0000000..edb017c --- /dev/null +++ b/admin/apps/user/model/dto/common.go @@ -0,0 +1,64 @@ +package dto + +type WebRspData struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data any `json:"data"` +} + +type ResourceInitInfo struct { + Resource string `json:"resource"` + Desc string `json:"desc"` + ShowMethods []string `json:"show_methods"` +} + +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"` + Readonly bool `json:"readonly"` // 是否只读,就只展示在表格中 + Required bool `json:"required"` // 是否必填,不能为空 + Choices []*CommonDtoFieldChoice `json:"choices"` // 可选项,用于字段做下拉框 + MultiChoice bool `json:"multi_choice"` // 是否多选 + Uneditable bool `json:"uneditable"` // 不可编辑,某些数据一旦新增之后不能修改,例如封禁的值、服务器的id等 +} + +//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"` // 数据行 +} + +type PathInfo struct { + Path string `json:"path"` + Method string `json:"method"` +} + +type UserInfo struct { + UserId int `json:"user_id"` + NickName string `json:"nick_name"` + Icon string `json:"icon"` + Character string `json:"character"` + Permissions []string `json:"permissions"` +} + +type TokenInfo struct { + Token string `json:"token"` + ExpireAt int64 `json:"expire_at"` +} diff --git a/admin/apps/user/model/dto/msg.go b/admin/apps/user/model/dto/msg.go new file mode 100644 index 0000000..4dcb7f6 --- /dev/null +++ b/admin/apps/user/model/dto/msg.go @@ -0,0 +1,67 @@ +package dto + +import "admin/apps/game/api" + +type NilReq struct { +} + +type NilRsp = NilReq + +type CommonListReq struct { + PageNo int `json:"page_no"` + PageLen int `json:"page_len"` + WhereValue1 string `json:"where_value1"` + WhereValue2 string `json:"where_value2"` + WhereValue3 string `json:"where_value3"` +} + +type CommonPostReq struct { + 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"` +} + +type ResourceListRsp struct { + List []*api.ResourceInitInfo `json:"list"` +} + +type LoginReq struct { + User string `json:"user"` + Password string `json:"password"` +} + +type LoginRsp struct { + User *UserInfo `json:"user_info"` + Token *TokenInfo `json:"token_info"` + ProjectsRoutes []*api.ProjectInitInfo `json:"projects"` // 项目路由 +} + +type GetUserInfoRsp = LoginRsp diff --git a/admin/apps/user/model/imodel.go b/admin/apps/user/model/imodel.go new file mode 100644 index 0000000..ac01475 --- /dev/null +++ b/admin/apps/user/model/imodel.go @@ -0,0 +1,6 @@ +package model + +type IModel interface { + TableName() string + GetId() int +} diff --git a/admin/apps/user/model/token.go b/admin/apps/user/model/token.go new file mode 100644 index 0000000..738caef --- /dev/null +++ b/admin/apps/user/model/token.go @@ -0,0 +1,43 @@ +package model + +import ( + "admin/internal/db" + "admin/internal/errcode" + "admin/internal/global" + "errors" + "gorm.io/gorm" + "time" +) + +func init() { + db.RegisterTableModels(Token{}) +} + +// Token token +type Token struct { + ID int `gorm:"primarykey" readonly:"true"` + Token string `gorm:"type:varchar(256);unique" required:"true"` + UserId int `name:"用户id" readonly:"true"` + ExpireAt time.Time `name:"到期时间" readonly:"true"` + CreatedAt time.Time `readonly:"true"` +} + +func (m *Token) TableName() string { + return "token" +} + +func (m *Token) GetId() int { + return m.ID +} + +func (m *Token) GetByUserId(userId int) error { + err := global.GLOB_DB.Where("user_id=?", userId).First(m).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errcode.New(errcode.TokenInvalid, "user %v not found token", userId) + } + return errcode.New(errcode.TokenInvalid, "user %v found token error:%v", userId, err) + } + + return nil +} diff --git a/admin/apps/user/model/user.go b/admin/apps/user/model/user.go new file mode 100644 index 0000000..00c9033 --- /dev/null +++ b/admin/apps/user/model/user.go @@ -0,0 +1,52 @@ +package model + +import ( + "admin/apps/user/model/dto" + "admin/internal/db" + "time" +) + +func init() { + db.RegisterTableModels(User{}) +} + +// User 用户表 +type User struct { + ID int `gorm:"primarykey" readonly:"true"` + UserName string `name:"账号名" gorm:"type:varchar(255);unique" required:"true" uneditable:"true"` + NickName string `name:"昵称" gorm:"type:varchar(255);unique" required:"true"` + UserPass string `gorm:"type:varchar(255)" name:"密码" required:"true"` // 用户密码hash后的值 + CharacterName string `gorm:"type:varchar(255);" name:"角色组" choices:"GetCharChoices"` + Status int `gorm:"type:int(1);default:0;" choices:"GetStatusChoices"` + CreatedAt time.Time `readonly:"true"` +} + +func (m *User) TableName() string { + return "user" +} + +func (m *User) GetId() int { + return m.ID +} + +func (u *User) GetStatusChoices() []*dto.CommonDtoFieldChoice { + return []*dto.CommonDtoFieldChoice{ + {Desc: "正常", Value: 0}, + {Desc: "停用", Value: 1}, + } +} + +func (u *User) GetCharChoices() []*dto.CommonDtoFieldChoice { + choices := make([]*dto.CommonDtoFieldChoice, 0) + choices = append(choices, &dto.CommonDtoFieldChoice{ + Desc: "空", + Value: "", + }) + for _, v := range new(Character).List() { + choices = append(choices, &dto.CommonDtoFieldChoice{ + Desc: v.Name, + Value: v.Name, + }) + } + return choices +} diff --git a/admin/apps/user/server/ctl.go b/admin/apps/user/server/ctl.go new file mode 100644 index 0000000..8bb014b --- /dev/null +++ b/admin/apps/user/server/ctl.go @@ -0,0 +1,11 @@ +package server + +import "admin/apps/user/service" + +type controller struct { + svc *service.Service +} + +func newController(svc *service.Service) *controller { + return &controller{svc: svc} +} diff --git a/admin/apps/user/server/ctl_common_rest.go b/admin/apps/user/server/ctl_common_rest.go new file mode 100644 index 0000000..b9e98f0 --- /dev/null +++ b/admin/apps/user/server/ctl_common_rest.go @@ -0,0 +1,57 @@ +package server + +import ( + "admin/apps/user/model/dto" + "admin/internal/context" +) + +func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error { + resource := getCtxURIResource(ctx) + list, err := ctl.svc.CommonList(ctx, resource, params) + if err != nil { + return err + } + *rsp = *list + return nil +} + +func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error { + resource := getCtxURIResource(ctx) + newObj, err := ctl.svc.CommonPost(ctx, resource, *params.Dto) + if err != nil { + return err + } + rsp.Dto = &newObj + return nil +} + +func (ctl *controller) CommonPut(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error { + resource := getCtxURIResource(ctx) + err := ctl.svc.CommonPut(ctx, resource, *params.Dto) + if err != nil { + return err + } + rsp.Dto = params.Dto + return nil +} + +func (ctl *controller) CommonDelete(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { + resource := getCtxURIResource(ctx) + err := ctl.svc.CommonDelete(ctx, resource, params.Id) + if err != nil { + return err + } + return nil +} + +func (ctl *controller) OnClickCustomButton(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { + return nil +} + +func getCtxURIResource(ctx *context.WebContext) string { + resource := ctx.GinCtx().Param("resource") + if resource == "" { + return "not_found_resource_in_url" + } + return resource +} diff --git a/admin/apps/user/server/ctl_user.go b/admin/apps/user/server/ctl_user.go new file mode 100644 index 0000000..899d312 --- /dev/null +++ b/admin/apps/user/server/ctl_user.go @@ -0,0 +1,24 @@ +package server + +import ( + "admin/apps/user/model/dto" + "admin/internal/context" +) + +func (ctl *controller) Login(ctx *context.WebContext, params *dto.LoginReq, rsp *dto.LoginRsp) error { + loginInfo, err := ctl.svc.Login(params.User, params.Password) + if err != nil { + return err + } + *rsp = *loginInfo + return nil +} + +func (ctl *controller) GetUserInfo(ctx *context.WebContext, params *dto.NilReq, rsp *dto.GetUserInfoRsp) error { + svcRsp, err := ctl.svc.GetUserInfo(ctx.Header.UserId) + if err != nil { + return err + } + *rsp = *svcRsp + return nil +} diff --git a/admin/apps/user/server/middleware.go b/admin/apps/user/server/middleware.go new file mode 100644 index 0000000..46ff6dd --- /dev/null +++ b/admin/apps/user/server/middleware.go @@ -0,0 +1,26 @@ +package server + +import ( + "admin/internal/context" + "admin/internal/errcode" + "strings" +) + +func (srv *Server) CheckToken(ctx *context.WebContext) { + if strings.Contains(ctx.GinCtx().Request.URL.Path, "/login") { + return + } + err := ctx.ExtractHeader() + if err != nil { + ctx.Fail(errcode.New(errcode.HeaderParamsInvalid, "header invalid")) + return + } + + err = srv.svc.CheckToken(ctx.Header.Token, ctx.Header.UserId) + if err != nil { + ctx.Fail(err) + ctx.GinCtx().Abort() + } else { + ctx.GinCtx().Next() + } +} diff --git a/admin/apps/user/server/route.go b/admin/apps/user/server/route.go new file mode 100644 index 0000000..e7712af --- /dev/null +++ b/admin/apps/user/server/route.go @@ -0,0 +1,28 @@ +package server + +import ( + "admin/internal/consts" + "admin/lib/web" +) + +func (srv *Server) Route(engine *web.Engine) { + engine.Use(srv.CheckToken) + + apiGroup := engine.Group("/api", "") + + { + userGroup := apiGroup.Group("/user", "用户操作组") + userGroup.Post("/login", "登录", consts.WebPathPermit_Write, srv.ctl.Login) + userGroup.Get("/info", "获取用户信息,里面包含用户权限信息,用于前端生成动态菜单", consts.WebPathPermit_Read, srv.ctl.GetUserInfo) + } + + { + // 操作所有资源增删改查的接口 + userResourceGroup := apiGroup.Group("/resource/:resource", "用户管理") + userResourceGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList) + userResourceGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost) + userResourceGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) + userResourceGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) + } + +} diff --git a/admin/apps/user/server/server.go b/admin/apps/user/server/server.go new file mode 100644 index 0000000..5280a6f --- /dev/null +++ b/admin/apps/user/server/server.go @@ -0,0 +1,15 @@ +package server + +import "admin/apps/user/service" + +type Server struct { + svc *service.Service + ctl *controller +} + +func New(svc *service.Service) *Server { + return &Server{ + svc: svc, + ctl: newController(svc), + } +} diff --git a/admin/apps/user/service/service.go b/admin/apps/user/service/service.go new file mode 100644 index 0000000..cb2e55c --- /dev/null +++ b/admin/apps/user/service/service.go @@ -0,0 +1,57 @@ +package service + +import ( + apiUser "admin/apps/user/api" + "admin/apps/user/domain" + "admin/apps/user/model/dto" + "admin/internal/context" + "gorm.io/gorm" +) + +type Service struct { + db *gorm.DB + resourceSvc *domain.CommonResourceService +} + +func New(db *gorm.DB) (*Service, error) { + svc := &Service{ + db: db, + resourceSvc: domain.NewCommonResourceService(db), + } + err := svc.resourceSvc.EnsureInitDB() + if err != nil { + return nil, err + } + apiUser.RegisterUserApiHandler(svc) + return svc, nil +} + +func (svc *Service) CommonList(ctx *context.WebContext, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) { + var ( + query string + args []any + ) + + fieldsDescInfo, rows, err := svc.resourceSvc.List(resourceName, params.PageNo, params.PageLen, query, args) + return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, Rows: rows}, err +} + +func (svc *Service) CommonPost(ctx *context.WebContext, resourceName string, params dto.CommonDtoValues) (dto.CommonDtoValues, error) { + values, _, err := svc.resourceSvc.Create(resourceName, params) + if err != nil { + return nil, err + } + return values, err +} + +func (svc *Service) CommonPut(ctx *context.WebContext, resourceName string, params dto.CommonDtoValues) error { + return svc.resourceSvc.Edit(resourceName, params) +} + +func (svc *Service) CommonDelete(ctx *context.WebContext, resourceName string, id int) error { + return svc.resourceSvc.Delete(resourceName, id) +} + +func (svc *Service) GetSupportResourcesList() []*dto.ResourceInitInfo { + return svc.resourceSvc.GetSupportResourcesList() +} diff --git a/admin/apps/user/service/service_user.go b/admin/apps/user/service/service_user.go new file mode 100644 index 0000000..80bfd80 --- /dev/null +++ b/admin/apps/user/service/service_user.go @@ -0,0 +1,107 @@ +package service + +import ( + "admin/apps/game/api" + apiUser "admin/apps/user/api" + "admin/apps/user/domain/entity" + "admin/apps/user/model" + "admin/apps/user/model/dto" + "admin/internal/errcode" + "admin/lib/tokenlib" + "context" +) + +func (svc *Service) CheckToken(token string, userId int) error { + if err := tokenlib.ValidToken(token, userId); err != nil { + return err + } + + dbToken := new(model.Token) + err := dbToken.GetByUserId(userId) + if err != nil { + return err + } + if token != dbToken.Token { + return errcode.New(errcode.TokenInvalid, "token not equal:%v,%v", token, dbToken.Token) + } + return nil +} + +func (svc *Service) Auth(ctx context.Context, req *apiUser.AuthReq) (*apiUser.AuthRsp, error) { + rsp := &apiUser.AuthRsp{} + err := svc.CheckToken(req.Token, req.UserId) + if err != nil { + return rsp, err + } + + info, err := svc.GetUserInfo(req.UserId) + if err != nil { + return nil, err + } + rsp.User = &apiUser.UserInfo{ + UserId: info.User.UserId, + NickName: info.User.NickName, + Icon: info.User.Icon, + Character: info.User.Character, + Permissions: info.User.Permissions, + } + rsp.Token = &apiUser.TokenInfo{ + Token: info.Token.Token, + ExpireAt: info.Token.ExpireAt, + } + return rsp, nil +} + +func (svc *Service) Login(user, pwd string) (info *dto.LoginRsp, err error) { + userInfo, err := svc.resourceSvc.Login(user, pwd) + if err != nil { + return nil, err + } + + tokenInfo, err := svc.resourceSvc.GetOrCreateToken(userInfo.GetUserId()) + if err != nil { + return nil, err + } + + projectRoutesRsp, err := api.GetGameApiInstance().GetRoutes(&api.GetRoutesReq{IsAdmin: userInfo.IsAdmin(), Permissions: userInfo.GetPermissions()}) + if err != nil { + return nil, err + } + + info = &dto.LoginRsp{} + info.User = userInfo.ToDtoInfo() + info.Token = &dto.TokenInfo{Token: tokenInfo.Token, ExpireAt: tokenInfo.ExpireAt.Unix()} + info.ProjectsRoutes = projectRoutesRsp.Projects + return info, nil +} + +func (svc *Service) GetUserInfo(userId int) (*dto.GetUserInfoRsp, error) { + user, find, err := svc.GetUserById(userId) + if err != nil { + return nil, err + } + if !find { + return nil, errcode.New(errcode.NotLogin, "not found user by id:%v", userId) + } + + tokenInfo, err := svc.resourceSvc.GetToken(user.GetUserId()) + if err != nil { + return nil, err + } + + projectRoutesRsp, err := api.GetGameApiInstance().GetRoutes(&api.GetRoutesReq{IsAdmin: user.IsAdmin(), Permissions: user.GetPermissions()}) + if err != nil { + return nil, err + } + + rsp := &dto.GetUserInfoRsp{ + User: user.ToDtoInfo(), + Token: &dto.TokenInfo{Token: tokenInfo.Token, ExpireAt: tokenInfo.ExpireAt.Unix()}, + ProjectsRoutes: projectRoutesRsp.Projects, + } + return rsp, nil +} + +func (svc *Service) GetUserById(userId int) (*entity.User, bool, error) { + return svc.resourceSvc.GetUserById(userId) +} diff --git a/admin/cmd/all_in_one/main.go b/admin/cmd/all_in_one/main.go index ea3a78b..db8bf27 100644 --- a/admin/cmd/all_in_one/main.go +++ b/admin/cmd/all_in_one/main.go @@ -2,7 +2,7 @@ package main import ( "admin/apps/game" - "admin/apps/mockpro" + "admin/apps/user" "admin/internal/mynode" "admin/lib/node" ) @@ -11,8 +11,9 @@ var appList []*node.ApplicationDescInfo func init() { appList = []*node.ApplicationDescInfo{ + user.New(), game.New(), - mockpro.New(), + //mockpro.New(), } } diff --git a/admin/go.mod b/admin/go.mod index b7c9beb..cd296ee 100644 --- a/admin/go.mod +++ b/admin/go.mod @@ -6,6 +6,7 @@ require ( github.com/gin-contrib/pprof v1.5.3 github.com/gin-gonic/gin v1.10.0 github.com/go-sql-driver/mysql v1.7.0 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/prometheus/client_golang v1.22.0 github.com/rs/zerolog v1.34.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/admin/go.sum b/admin/go.sum index ee894ae..9c71f49 100644 --- a/admin/go.sum +++ b/admin/go.sum @@ -36,6 +36,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/admin/internal/consts/consts.go b/admin/internal/consts/consts.go index 4bbb7ef..77436f7 100644 --- a/admin/internal/consts/consts.go +++ b/admin/internal/consts/consts.go @@ -18,6 +18,11 @@ var ( ) const ( + // system + ResourcesName_SysUser = "user" + ResourcesName_SysCharacter = "character" + + // game ResourcesName_Project = "project" ResourcesName_Server = "server" ResourcesName_Account = "account" @@ -35,3 +40,12 @@ const ( WebPathPermit_Read = 1 WebPathPermit_Write = 2 ) + +const ( + BanType_Account = "account" + BanType_Role = "role" + BanType_Ip = "ip" + BanType_AccountChat = "account_chat" + BanType_RoleChat = "role_chat" + BanType_Device = "device" +) diff --git a/admin/internal/context/ctx_web.go b/admin/internal/context/ctx_web.go index 0d7d0d5..6970e54 100644 --- a/admin/internal/context/ctx_web.go +++ b/admin/internal/context/ctx_web.go @@ -8,9 +8,16 @@ import ( "github.com/gin-gonic/gin" ) +type WebHeader struct { + UserId int `json:"UserId"` // 用户id,会与下面token解析出用户id做匹配校验 + Token string `json:"Token"` // jwt token,内置一些信息 + Ip string `json:"IP"` +} + type WebContext struct { context.Context rawCtx *gin.Context + Header *WebHeader alreadySetRsp bool } @@ -18,6 +25,20 @@ func NewWebContext(rawCtx *gin.Context) web.IContext { return &WebContext{rawCtx: rawCtx} } +func (ctx *WebContext) ExtractHeader() error { + header := &WebHeader{} + err := ctx.rawCtx.ShouldBindHeader(header) + if err != nil { + return err + } + ctx.Header = header + return nil +} + +func (ctx *WebContext) IsUserLogin() bool { + return ctx.Header != nil && ctx.Header.UserId > 0 +} + func (ctx *WebContext) GinCtx() *gin.Context { return ctx.rawCtx } diff --git a/admin/internal/errcode/code.go b/admin/internal/errcode/code.go index 2b8afd2..1101ac9 100644 --- a/admin/internal/errcode/code.go +++ b/admin/internal/errcode/code.go @@ -1,7 +1,12 @@ package errcode const ( - Ok = 0 - ServerError = 1 // 服务器错误 - DBError = 2 // 数据库错误 + Ok = 0 + ServerError = 1 // 服务器错误 + DBError = 2 // 数据库错误 + NotLogin = 3 // 没有登录,重定向到登录页面 + HeaderParamsInvalid = 4 + TokenInvalid = 5 + UserOrPassInValid = 7 // 用户名或密码错误 + NoPermission = 8 // 没有权限 ) diff --git a/admin/internal/permission/strings.go b/admin/internal/permission/strings.go new file mode 100644 index 0000000..f75b22c --- /dev/null +++ b/admin/internal/permission/strings.go @@ -0,0 +1,18 @@ +package permission + +import ( + "fmt" + "strings" +) + +func GetProjectResourcePermission(projectId int, resource string, method string) string { + return fmt.Sprintf("project:%v:%v:%v", projectId, resource, strings.ToLower(method)) +} + +func ParseProjectResourcePermission(permission string) (int, string, string, error) { + projectId := 0 + resource := "" + method := "" + _, err := fmt.Scanf(permission, &projectId, resource, method) + return projectId, resource, method, err +} diff --git a/admin/lib/passlib/password.go b/admin/lib/passlib/password.go new file mode 100644 index 0000000..5e48047 --- /dev/null +++ b/admin/lib/passlib/password.go @@ -0,0 +1,12 @@ +package passlib + +import "golang.org/x/crypto/bcrypt" + +func EncryptPassword(password string) string { + binPwd, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(binPwd) +} + +func ComparePassword(encryptPwd, plaintPwd string) bool { + return bcrypt.CompareHashAndPassword([]byte(encryptPwd), []byte(plaintPwd)) == nil +} diff --git a/admin/lib/tokenlib/token.go b/admin/lib/tokenlib/token.go new file mode 100644 index 0000000..46132dc --- /dev/null +++ b/admin/lib/tokenlib/token.go @@ -0,0 +1,56 @@ +package tokenlib + +import ( + "admin/internal/errcode" + "fmt" + "github.com/golang-jwt/jwt/v5" + "strconv" + "time" +) + +type RegisteredTokenClaims struct { + User string `json:"user,omitempty"` + jwt.RegisteredClaims +} + +var ( + tokenSign = []byte("token_sign") // token加盐 +) + +func ValidToken(token string, userId int) error { + checkToken, err := jwt.ParseWithClaims(token, &RegisteredTokenClaims{}, func(token *jwt.Token) (interface{}, error) { + return tokenSign, nil + }) + if err != nil { + return errcode.New(errcode.TokenInvalid, "ParseWithClaims token (%v) error:%v", token, err) + } + if !checkToken.Valid { + return errcode.New(errcode.TokenInvalid, "ParseWithClaims token (%v) invalid", token) + } + + claims := checkToken.Claims.(*RegisteredTokenClaims) + if claims.User != strconv.Itoa(userId) { + return errcode.New(errcode.TokenInvalid, "token:%v extract user:%v not equal header give id:%v", token, claims.User, userId) + } + + return nil +} + +func GenToken(userId int, expire time.Duration) (string, error) { + user := strconv.Itoa(userId) + claims := &RegisteredTokenClaims{ + User: user, + } + jwtClaims := jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(expire)), + Issuer: "test", + } + claims.RegisteredClaims = jwtClaims + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + ss, err := token.SignedString(tokenSign) + if err != nil { + return "", fmt.Errorf("token SignedString error:%v", err) + } + + return ss, nil +} diff --git a/admin/lib/tokenlib/token_test.go b/admin/lib/tokenlib/token_test.go new file mode 100644 index 0000000..fc9974d --- /dev/null +++ b/admin/lib/tokenlib/token_test.go @@ -0,0 +1,20 @@ +package tokenlib + +import ( + "testing" + "time" +) + +func TestToken(t *testing.T) { + token, err := GenToken(123, time.Second*5) + if err != nil { + panic(err) + } + if err := ValidToken(token, 123); err != nil { + panic(err) + } + time.Sleep(time.Second * 6) + if err := ValidToken(token, 123); err == nil { + panic(token) + } +} diff --git a/admin/lib/web/routes_group.go b/admin/lib/web/routes_group.go index e675c94..ef3436e 100644 --- a/admin/lib/web/routes_group.go +++ b/admin/lib/web/routes_group.go @@ -74,12 +74,23 @@ func getGinHandlerFunWithRequest(newContextFun func(ctx *gin.Context) IContext, for _, handler := range handlers { list = append(list, func(rawCtx *gin.Context) { - ctx := newContextFun(rawCtx) + customCtx, find := rawCtx.Get("custom_ctx") + if !find { + customCtx = newContextFun(rawCtx) + rawCtx.Set("custom_ctx", customCtx) + } + ctx := customCtx.(IContext) 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)) + if numParams != 1 { + ctx.HandleError(rawCtx.Request.RequestURI, fmt.Errorf("register callback handler params len(%v) invalid", numParams)) + return + } + // middleware + reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(ctx)}) + // 中间件内部已经走了响应数据打包了,这里直接返回 return } diff --git a/ui/src/api/game_api.js b/ui/src/api/game_api.js deleted file mode 100644 index d58d021..0000000 --- a/ui/src/api/game_api.js +++ /dev/null @@ -1,23 +0,0 @@ -import request from '@/utils/request' - -export function generateRoutes() { - return request({ - url: "/routes", - method: "get" - }) -} - -export function gameApiRequest(url, method, data) { - if (method === "get") { - return request({ - url: url, - method: method, - params: data - }) - } - return request({ - url: url, - method: method, - data: data - }) -} diff --git a/ui/src/api/sys.js b/ui/src/api/sys.js new file mode 100644 index 0000000..7fa7284 --- /dev/null +++ b/ui/src/api/sys.js @@ -0,0 +1,24 @@ +import request from '@/utils/request' + +export function login(params) { + return request({ + url: "/user/login", + method: "post", + data: params + }) +} + +export function getUserInfo(params) { + return request({ + url: "/user/info", + method: "get", + data: params + }) +} + +export function generateRoutes() { + return request({ + url: "/routes", + method: "get" + }) +} \ No newline at end of file diff --git a/ui/src/assets/img/login-background.svg b/ui/src/assets/img/login-background.svg new file mode 100644 index 0000000..91195ff --- /dev/null +++ b/ui/src/assets/img/login-background.svg @@ -0,0 +1,67 @@ + \ No newline at end of file diff --git a/ui/src/components/restful/empty.vue b/ui/src/components/restful/empty.vue new file mode 100644 index 0000000..0d7c49a --- /dev/null +++ b/ui/src/components/restful/empty.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/restful/table.vue b/ui/src/components/restful/table.vue index 5ececcb..c96c6fe 100644 --- a/ui/src/components/restful/table.vue +++ b/ui/src/components/restful/table.vue @@ -3,18 +3,24 @@ import {ElNotification} from "element-plus"; import {resourceDelete, resourceList, resourcePost, resourcePut} from "@/api/resource.js"; import {ref, toRaw} from "vue"; import {useRoute} from 'vue-router'; -import LocalCache from "@/stores/project.js"; +import LocalCache from "@/stores/localCache.js"; +import empty from '@/components/restful/empty.vue'; const cachedResource = LocalCache.getCache("resource"); const listRsp = ref({fields_desc: [], rows: []}) const listDataOK = ref(false) const resource_raw_node = cachedResource; +const hasListPermit = resource_raw_node.meta.methods.get !== undefined && resource_raw_node.meta.methods.get === true; + const resource_url = cachedResource.meta.resource_url; const fieldsDescInfo = ref([]) const rows = ref([]) const rules = ref({}) +const current_page = ref(0) +const page_size = ref(0) + const item = ref({ id: 0, number: 1, @@ -205,168 +211,102 @@ const handleCloseDialog = () => {