save
This commit is contained in:
parent
e9d01aa19f
commit
29cb1e0bb8
38
admin/apps/game/api/api.go
Normal file
38
admin/apps/game/api/api.go
Normal file
@ -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
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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"`
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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:"通知内容"`
|
||||
|
@ -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"`
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:"奖励码"`
|
||||
|
||||
|
@ -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"`
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
72
admin/apps/game/server/middleware.go
Normal file
72
admin/apps/game/server/middleware.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
40
admin/apps/user/api/api_user.go
Normal file
40
admin/apps/user/api/api_user.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
146
admin/apps/user/domain/comm_resource.go
Normal file
146
admin/apps/user/domain/comm_resource.go
Normal file
@ -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
|
||||
}
|
67
admin/apps/user/domain/entity/comm_resouce.go
Normal file
67
admin/apps/user/domain/entity/comm_resouce.go
Normal file
@ -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
|
||||
}
|
36
admin/apps/user/domain/entity/user.go
Normal file
36
admin/apps/user/domain/entity/user.go
Normal file
@ -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,
|
||||
}
|
||||
}
|
186
admin/apps/user/domain/entity/utils.go
Normal file
186
admin/apps/user/domain/entity/utils.go
Normal file
@ -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)
|
||||
}
|
19
admin/apps/user/domain/iresource.go
Normal file
19
admin/apps/user/domain/iresource.go
Normal file
@ -0,0 +1,19 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"admin/apps/game/model/dto"
|
||||
)
|
||||
|
||||
type IRestfulEntity interface {
|
||||
ToCommonDto() dto.CommonDtoValues
|
||||
}
|
||||
|
||||
type IRestfulResourceSvc interface {
|
||||
List(pageNo, pageLen int, whereValues ...string) ([]*dto.CommonDtoFieldDesc, []IRestfulEntity, error)
|
||||
Post(obj dto.CommonDtoValues) (IRestfulEntity, error)
|
||||
Put(obj dto.CommonDtoValues) (IRestfulEntity, error)
|
||||
Delete(id int) error
|
||||
}
|
||||
|
||||
type IResourceOpPostHook interface {
|
||||
}
|
126
admin/apps/user/domain/repo/comm_resource.go
Normal file
126
admin/apps/user/domain/repo/comm_resource.go
Normal file
@ -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)
|
||||
}
|
71
admin/apps/user/domain/repo/token.go
Normal file
71
admin/apps/user/domain/repo/token.go
Normal file
@ -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
|
||||
}
|
95
admin/apps/user/domain/repo/user.go
Normal file
95
admin/apps/user/domain/repo/user.go
Normal file
@ -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
|
||||
}
|
64
admin/apps/user/domain/user.go
Normal file
64
admin/apps/user/domain/user.go
Normal file
@ -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
|
||||
}
|
43
admin/apps/user/model/character.go
Normal file
43
admin/apps/user/model/character.go
Normal file
@ -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:<projectId>:<resource>:<get>", "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
|
||||
}
|
64
admin/apps/user/model/dto/common.go
Normal file
64
admin/apps/user/model/dto/common.go
Normal file
@ -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"`
|
||||
}
|
67
admin/apps/user/model/dto/msg.go
Normal file
67
admin/apps/user/model/dto/msg.go
Normal file
@ -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
|
6
admin/apps/user/model/imodel.go
Normal file
6
admin/apps/user/model/imodel.go
Normal file
@ -0,0 +1,6 @@
|
||||
package model
|
||||
|
||||
type IModel interface {
|
||||
TableName() string
|
||||
GetId() int
|
||||
}
|
43
admin/apps/user/model/token.go
Normal file
43
admin/apps/user/model/token.go
Normal file
@ -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
|
||||
}
|
52
admin/apps/user/model/user.go
Normal file
52
admin/apps/user/model/user.go
Normal file
@ -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
|
||||
}
|
11
admin/apps/user/server/ctl.go
Normal file
11
admin/apps/user/server/ctl.go
Normal file
@ -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}
|
||||
}
|
57
admin/apps/user/server/ctl_common_rest.go
Normal file
57
admin/apps/user/server/ctl_common_rest.go
Normal file
@ -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
|
||||
}
|
24
admin/apps/user/server/ctl_user.go
Normal file
24
admin/apps/user/server/ctl_user.go
Normal file
@ -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
|
||||
}
|
26
admin/apps/user/server/middleware.go
Normal file
26
admin/apps/user/server/middleware.go
Normal file
@ -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()
|
||||
}
|
||||
}
|
28
admin/apps/user/server/route.go
Normal file
28
admin/apps/user/server/route.go
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
15
admin/apps/user/server/server.go
Normal file
15
admin/apps/user/server/server.go
Normal file
@ -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),
|
||||
}
|
||||
}
|
57
admin/apps/user/service/service.go
Normal file
57
admin/apps/user/service/service.go
Normal file
@ -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()
|
||||
}
|
107
admin/apps/user/service/service_user.go
Normal file
107
admin/apps/user/service/service_user.go
Normal file
@ -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)
|
||||
}
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 // 没有权限
|
||||
)
|
||||
|
18
admin/internal/permission/strings.go
Normal file
18
admin/internal/permission/strings.go
Normal file
@ -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
|
||||
}
|
12
admin/lib/passlib/password.go
Normal file
12
admin/lib/passlib/password.go
Normal file
@ -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
|
||||
}
|
56
admin/lib/tokenlib/token.go
Normal file
56
admin/lib/tokenlib/token.go
Normal file
@ -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
|
||||
}
|
20
admin/lib/tokenlib/token_test.go
Normal file
20
admin/lib/tokenlib/token_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
})
|
||||
}
|
24
ui/src/api/sys.js
Normal file
24
ui/src/api/sys.js
Normal file
@ -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"
|
||||
})
|
||||
}
|
67
ui/src/assets/img/login-background.svg
Normal file
67
ui/src/assets/img/login-background.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<svg width="100%" height="100%" id="svg" viewBox="0 0 1440 400" xmlns="http://www.w3.org/2000/svg" class="transition duration-300 ease-in-out delay-150"><style>
|
||||
.path-0{
|
||||
animation:pathAnim-0 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
@keyframes pathAnim-0{
|
||||
0%{
|
||||
d: path("M 0,400 C 0,400 0,100 0,100 C 50.8103747173373,109.20471951536959 101.6207494346746,118.4094390307392 143,110 C 184.3792505653254,101.5905609692608 216.32737697863888,75.56696339241286 262,74 C 307.6726230213611,72.43303660758714 367.0697426507697,95.32270739960937 415,100 C 462.9302573492303,104.67729260039063 499.3936524182825,91.14220700914969 539,86 C 578.6063475817175,80.85779299085031 621.3556476761007,84.1084645637919 664,85 C 706.6443523238993,85.8915354362081 749.1837568773142,84.42393473568269 789,75 C 828.8162431226858,65.57606526431731 865.9093248146424,48.19579649347729 914,66 C 962.0906751853576,83.80420350652271 1021.1789438641165,136.79287929040817 1067,137 C 1112.8210561358835,137.20712070959183 1145.3748997288917,84.63268634489012 1179,69 C 1212.6251002711083,53.36731365510988 1247.3214572203167,74.67637533003139 1291,86 C 1334.6785427796833,97.32362466996861 1387.3392713898415,98.6618123349843 1440,100 C 1440,100 1440,400 1440,400 Z");
|
||||
}
|
||||
25%{
|
||||
d: path("M 0,400 C 0,400 0,100 0,100 C 38.085950711769925,108.68663884161857 76.17190142353985,117.37327768323715 122,109 C 167.82809857646015,100.62672231676285 221.3983450176105,75.19352810866998 267,81 C 312.6016549823895,86.80647189133002 350.2347185060181,123.85260988208296 390,128 C 429.7652814939819,132.14739011791704 471.66278095831717,103.39603236299823 522,94 C 572.3372190416828,84.60396763700177 631.1141576607127,94.5632606659241 675,109 C 718.8858423392873,123.4367393340759 747.880588398832,142.35092497330533 791,132 C 834.119411601168,121.64907502669469 891.3634887439594,82.03303944085465 931,79 C 970.6365112560406,75.96696055914535 992.6654566253305,109.51691726327608 1035,108 C 1077.3345433746695,106.48308273672392 1139.974684754719,69.89929150604104 1189,66 C 1238.025315245281,62.100708493958955 1273.4358043557945,90.8859167125597 1313,102 C 1352.5641956442055,113.1140832874403 1396.2820978221027,106.55704164372014 1440,100 C 1440,100 1440,400 1440,400 Z");
|
||||
}
|
||||
50%{
|
||||
d: path("M 0,400 C 0,400 0,100 0,100 C 37.01395207173833,109.69011844829022 74.02790414347666,119.38023689658044 118,124 C 161.97209585652334,128.61976310341956 212.90233549783164,128.16917086196852 257,118 C 301.09766450216836,107.83082913803148 338.3627538651966,87.94307965554552 379,89 C 419.6372461348034,90.05692034445448 463.6466490413816,112.05851051584946 506,126 C 548.3533509586184,139.94148948415054 589.0506499692768,145.8228782810566 636,128 C 682.9493500307232,110.1771217189434 736.1507510815113,68.64997635992414 788,67 C 839.8492489184887,65.35002364007586 890.3463457046779,103.57721627924685 928,111 C 965.6536542953221,118.42278372075315 990.4638660997771,95.04115852308847 1038,95 C 1085.536133900223,94.95884147691153 1155.798189896214,118.25814962839925 1199,128 C 1242.201810103786,137.74185037160075 1258.3433743153673,133.9262429633145 1294,127 C 1329.6566256846327,120.0737570366855 1384.8283128423163,110.03687851834275 1440,100 C 1440,100 1440,400 1440,400 Z");
|
||||
}
|
||||
75%{
|
||||
d: path("M 0,400 C 0,400 0,100 0,100 C 45.604500645453754,100.93447183466468 91.20900129090751,101.86894366932935 134,96 C 176.7909987090925,90.13105633067065 216.76849548182372,77.45869715734729 262,80 C 307.2315045181763,82.54130284265271 357.7170167817977,100.29626770128152 403,97 C 448.2829832182023,93.70373229871848 488.3634373909856,69.35623203752661 522,63 C 555.6365626090144,56.64376796247338 582.82923365426,68.27880414861198 634,70 C 685.17076634574,71.72119585138802 760.3196279919748,63.528551368025504 809,76 C 857.6803720080252,88.4714486319745 879.8922543778411,121.606990379286 917,133 C 954.1077456221589,144.393009620714 1006.1113544966613,134.04348711483055 1052,117 C 1097.8886455033387,99.95651288516945 1137.6623276355142,76.21906116139176 1177,73 C 1216.3376723644858,69.78093883860824 1255.2393349612817,87.08026823960235 1299,95 C 1342.7606650387183,102.91973176039765 1391.3803325193592,101.45986588019883 1440,100 C 1440,100 1440,400 1440,400 Z");
|
||||
}
|
||||
100%{
|
||||
d: path("M 0,400 C 0,400 0,100 0,100 C 50.8103747173373,109.20471951536959 101.6207494346746,118.4094390307392 143,110 C 184.3792505653254,101.5905609692608 216.32737697863888,75.56696339241286 262,74 C 307.6726230213611,72.43303660758714 367.0697426507697,95.32270739960937 415,100 C 462.9302573492303,104.67729260039063 499.3936524182825,91.14220700914969 539,86 C 578.6063475817175,80.85779299085031 621.3556476761007,84.1084645637919 664,85 C 706.6443523238993,85.8915354362081 749.1837568773142,84.42393473568269 789,75 C 828.8162431226858,65.57606526431731 865.9093248146424,48.19579649347729 914,66 C 962.0906751853576,83.80420350652271 1021.1789438641165,136.79287929040817 1067,137 C 1112.8210561358835,137.20712070959183 1145.3748997288917,84.63268634489012 1179,69 C 1212.6251002711083,53.36731365510988 1247.3214572203167,74.67637533003139 1291,86 C 1334.6785427796833,97.32362466996861 1387.3392713898415,98.6618123349843 1440,100 C 1440,100 1440,400 1440,400 Z");
|
||||
}
|
||||
}</style><defs><linearGradient id="gradient" x1="4%" y1="70%" x2="96%" y2="30%"><stop offset="5%" stop-color="#8ed1fc"></stop><stop offset="95%" stop-color="#32ded4"></stop></linearGradient></defs><path d="M 0,400 C 0,400 0,100 0,100 C 50.8103747173373,109.20471951536959 101.6207494346746,118.4094390307392 143,110 C 184.3792505653254,101.5905609692608 216.32737697863888,75.56696339241286 262,74 C 307.6726230213611,72.43303660758714 367.0697426507697,95.32270739960937 415,100 C 462.9302573492303,104.67729260039063 499.3936524182825,91.14220700914969 539,86 C 578.6063475817175,80.85779299085031 621.3556476761007,84.1084645637919 664,85 C 706.6443523238993,85.8915354362081 749.1837568773142,84.42393473568269 789,75 C 828.8162431226858,65.57606526431731 865.9093248146424,48.19579649347729 914,66 C 962.0906751853576,83.80420350652271 1021.1789438641165,136.79287929040817 1067,137 C 1112.8210561358835,137.20712070959183 1145.3748997288917,84.63268634489012 1179,69 C 1212.6251002711083,53.36731365510988 1247.3214572203167,74.67637533003139 1291,86 C 1334.6785427796833,97.32362466996861 1387.3392713898415,98.6618123349843 1440,100 C 1440,100 1440,400 1440,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="0.4" class="transition-all duration-300 ease-in-out delay-150 path-0"></path><style>
|
||||
.path-1{
|
||||
animation:pathAnim-1 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
@keyframes pathAnim-1{
|
||||
0%{
|
||||
d: path("M 0,400 C 0,400 0,200 0,200 C 33.037747977312605,204.17407595636058 66.07549595462521,208.3481519127212 114,202 C 161.9245040453748,195.6518480872788 224.73576415881178,178.78146830547584 277,172 C 329.2642358411882,165.21853169452416 370.98144741012777,168.5259748653755 407,172 C 443.01855258987223,175.4740251346245 473.3384462006769,179.1146322330221 516,189 C 558.6615537993231,198.8853677669779 613.6647677871649,215.01549620253616 666,207 C 718.3352322128351,198.98450379746384 768.0024826506634,166.8233829568334 801,163 C 833.9975173493366,159.1766170431666 850.3253016101814,183.69097197013028 892,189 C 933.6746983898186,194.30902802986972 1000.6963109086112,180.41272916264552 1056,174 C 1111.3036890913888,167.58727083735448 1154.8894547553741,168.65811137928773 1197,175 C 1239.1105452446259,181.34188862071227 1279.745870069893,192.9548253202035 1320,198 C 1360.254129930107,203.0451746797965 1400.1270649650535,201.52258733989825 1440,200 C 1440,200 1440,400 1440,400 Z");
|
||||
}
|
||||
25%{
|
||||
d: path("M 0,400 C 0,400 0,200 0,200 C 50.144999548448,205.21959416984376 100.289999096896,210.43918833968752 145,205 C 189.710000903104,199.56081166031248 228.98500316086404,183.46284081109366 270,183 C 311.01499683913596,182.53715918890634 353.76998825964773,197.70944841593783 395,201 C 436.23001174035227,204.29055158406217 475.93504380054515,195.699365525155 521,193 C 566.0649561994549,190.300634474845 616.489836538172,193.49308948344222 657,190 C 697.510163461828,186.50691051655778 728.1056100467666,176.32827654107615 778,184 C 827.8943899532334,191.67172345892385 897.0877232747614,217.1938043522531 938,223 C 978.9122767252386,228.8061956477469 991.5434968541877,214.89650604991155 1031,213 C 1070.4565031458123,211.10349395008845 1136.738289308488,221.22017144810076 1183,218 C 1229.261710691512,214.77982855189924 1255.5033459118606,198.2228081576855 1295,193 C 1334.4966540881394,187.7771918423145 1387.2483270440698,193.88859592115725 1440,200 C 1440,200 1440,400 1440,400 Z");
|
||||
}
|
||||
50%{
|
||||
d: path("M 0,400 C 0,400 0,200 0,200 C 36.84512474345648,198.16914961246212 73.69024948691296,196.33829922492424 119,194 C 164.30975051308704,191.66170077507576 218.08412679580465,188.81595271276512 264,200 C 309.91587320419535,211.18404728723488 347.97324332986847,236.39788992401526 385,236 C 422.02675667013153,235.60211007598474 458.02289988472137,209.5924875911737 504,198 C 549.9771001152786,186.4075124088263 605.9351571312458,189.23215971129002 656,203 C 706.0648428687542,216.76784028870998 750.2364715902953,241.47887356366618 794,228 C 837.7635284097047,214.52112643633382 881.1189565075728,162.85234603404527 919,166 C 956.8810434924272,169.14765396595473 989.2877023794135,227.1117423001528 1038,233 C 1086.7122976205865,238.8882576998472 1151.730233974773,192.70068476534348 1196,180 C 1240.269766025227,167.29931523465652 1263.7913617214933,188.0855186384733 1301,197 C 1338.2086382785067,205.9144813615267 1389.1043191392532,202.95724068076333 1440,200 C 1440,200 1440,400 1440,400 Z");
|
||||
}
|
||||
75%{
|
||||
d: path("M 0,400 C 0,400 0,200 0,200 C 52.74494394557294,199.86307526398085 105.48988789114588,199.7261505279617 149,193 C 192.51011210885412,186.2738494720383 226.78539238098944,172.95847315213408 271,185 C 315.21460761901056,197.04152684786592 369.3685425848962,234.43995686350206 413,235 C 456.6314574151038,235.56004313649794 489.7404372794257,199.28169939385782 527,183 C 564.2595627205743,166.71830060614218 605.669708297401,170.43324556106666 646,178 C 686.330291702599,185.56675443893334 725.5807295309703,196.98531836187553 773,211 C 820.4192704690297,225.01468163812447 876.0073735787179,241.62548099143115 926,229 C 975.9926264212821,216.37451900856885 1020.3897761541582,174.51275767239986 1059,173 C 1097.6102238458418,171.48724232760014 1130.4335218046497,210.32348831896928 1170,227 C 1209.5664781953503,243.67651168103072 1255.8761366272429,238.19328905172307 1302,230 C 1348.1238633727571,221.80671094827693 1394.0619316863786,210.90335547413846 1440,200 C 1440,200 1440,400 1440,400 Z");
|
||||
}
|
||||
100%{
|
||||
d: path("M 0,400 C 0,400 0,200 0,200 C 33.037747977312605,204.17407595636058 66.07549595462521,208.3481519127212 114,202 C 161.9245040453748,195.6518480872788 224.73576415881178,178.78146830547584 277,172 C 329.2642358411882,165.21853169452416 370.98144741012777,168.5259748653755 407,172 C 443.01855258987223,175.4740251346245 473.3384462006769,179.1146322330221 516,189 C 558.6615537993231,198.8853677669779 613.6647677871649,215.01549620253616 666,207 C 718.3352322128351,198.98450379746384 768.0024826506634,166.8233829568334 801,163 C 833.9975173493366,159.1766170431666 850.3253016101814,183.69097197013028 892,189 C 933.6746983898186,194.30902802986972 1000.6963109086112,180.41272916264552 1056,174 C 1111.3036890913888,167.58727083735448 1154.8894547553741,168.65811137928773 1197,175 C 1239.1105452446259,181.34188862071227 1279.745870069893,192.9548253202035 1320,198 C 1360.254129930107,203.0451746797965 1400.1270649650535,201.52258733989825 1440,200 C 1440,200 1440,400 1440,400 Z");
|
||||
}
|
||||
}</style><defs><linearGradient id="gradient" x1="4%" y1="70%" x2="96%" y2="30%"><stop offset="5%" stop-color="#8ed1fc"></stop><stop offset="95%" stop-color="#32ded4"></stop></linearGradient></defs><path d="M 0,400 C 0,400 0,200 0,200 C 33.037747977312605,204.17407595636058 66.07549595462521,208.3481519127212 114,202 C 161.9245040453748,195.6518480872788 224.73576415881178,178.78146830547584 277,172 C 329.2642358411882,165.21853169452416 370.98144741012777,168.5259748653755 407,172 C 443.01855258987223,175.4740251346245 473.3384462006769,179.1146322330221 516,189 C 558.6615537993231,198.8853677669779 613.6647677871649,215.01549620253616 666,207 C 718.3352322128351,198.98450379746384 768.0024826506634,166.8233829568334 801,163 C 833.9975173493366,159.1766170431666 850.3253016101814,183.69097197013028 892,189 C 933.6746983898186,194.30902802986972 1000.6963109086112,180.41272916264552 1056,174 C 1111.3036890913888,167.58727083735448 1154.8894547553741,168.65811137928773 1197,175 C 1239.1105452446259,181.34188862071227 1279.745870069893,192.9548253202035 1320,198 C 1360.254129930107,203.0451746797965 1400.1270649650535,201.52258733989825 1440,200 C 1440,200 1440,400 1440,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="0.53" class="transition-all duration-300 ease-in-out delay-150 path-1"></path><style>
|
||||
.path-2{
|
||||
animation:pathAnim-2 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
@keyframes pathAnim-2{
|
||||
0%{
|
||||
d: path("M 0,400 C 0,400 0,300 0,300 C 51.227644191181795,301.9763528409705 102.45528838236359,303.952705681941 145,297 C 187.5447116176364,290.047294318059 221.4064906617274,274.16553011320667 262,285 C 302.5935093382726,295.83446988679333 349.91874897072705,333.38517386523216 398,331 C 446.08125102927295,328.61482613476784 494.9185134553645,286.29377442586485 537,283 C 579.0814865446355,279.70622557413515 614.4071972078149,315.4397284313083 656,328 C 697.5928027921851,340.5602715686917 745.452697713376,329.94731184890185 792,315 C 838.547302286624,300.05268815109815 883.782011938681,280.77102417308436 929,279 C 974.217988061319,277.22897582691564 1019.4192545318997,292.9685914587609 1058,289 C 1096.5807454681003,285.0314085412391 1128.5409699337195,261.35460999187205 1174,264 C 1219.4590300662805,266.64539000812795 1278.416865733223,295.61296857375083 1325,306 C 1371.583134266777,316.38703142624917 1405.7915671333885,308.1935157131246 1440,300 C 1440,300 1440,400 1440,400 Z");
|
||||
}
|
||||
25%{
|
||||
d: path("M 0,400 C 0,400 0,300 0,300 C 54.53282428960243,293.6367148971435 109.06564857920486,287.2734297942871 148,277 C 186.93435142079514,266.7265702057129 210.27022997278294,252.54299571999525 247,264 C 283.72977002721706,275.45700428000475 333.8534315296634,312.5545873257319 378,320 C 422.1465684703366,327.4454126742681 460.3160439085634,305.2386549770771 509,294 C 557.6839560914366,282.7613450229229 616.8823928360831,282.4907927659598 664,290 C 711.1176071639169,297.5092072340402 746.1543847471042,312.798173959084 787,319 C 827.8456152528958,325.201826040916 874.5000681754998,322.31651139770406 924,313 C 973.4999318245002,303.68348860229594 1025.8453425508972,287.93578045009997 1062,281 C 1098.1546574491028,274.06421954990003 1118.1185616209123,275.9403668018961 1160,286 C 1201.8814383790877,296.0596331981039 1265.6804109654536,314.3027523423154 1316,318 C 1366.3195890345464,321.6972476576846 1403.1597945172732,310.8486238288423 1440,300 C 1440,300 1440,400 1440,400 Z");
|
||||
}
|
||||
50%{
|
||||
d: path("M 0,400 C 0,400 0,300 0,300 C 32.00253223284501,282.3792505653254 64.00506446569003,264.7585011306508 106,273 C 147.99493553430997,281.2414988693492 199.98227437008495,315.3452460427221 249,317 C 298.01772562991505,318.6547539572779 344.0658380539702,287.8605146984607 386,284 C 427.9341619460298,280.1394853015393 465.75437341403415,303.2126951634353 508,307 C 550.2456265859659,310.7873048365647 596.9166682898929,295.2887046477983 642,297 C 687.0833317101071,298.7112953522017 730.5789534263944,317.6324862453716 784,310 C 837.4210465736056,302.3675137546284 900.7675180045297,268.1813503707154 939,267 C 977.2324819954703,265.8186496292846 990.3509745554868,297.64211227176696 1030,315 C 1069.6490254445132,332.35788772823304 1135.8285837735227,335.2502005422166 1185,320 C 1234.1714162264773,304.7497994577834 1266.334690350422,271.3570855593667 1306,265 C 1345.665309649578,258.6429144406333 1392.832654824789,279.3214572203167 1440,300 C 1440,300 1440,400 1440,400 Z");
|
||||
}
|
||||
75%{
|
||||
d: path("M 0,400 C 0,400 0,300 0,300 C 54.93754592992266,285.7678031020738 109.87509185984533,271.53560620414754 149,274 C 188.12490814015467,276.46439379585246 211.43717849054133,295.6253782854836 252,301 C 292.5628215094587,306.3746217145164 350.3761941779894,297.96288065391815 393,306 C 435.6238058220106,314.03711934608185 463.058044797501,338.52309909884383 502,336 C 540.941955202499,333.47690090115617 591.3916266320064,303.9447229507065 636,295 C 680.6083733679936,286.0552770492935 719.3754486744734,297.69800909833026 766,307 C 812.6245513255266,316.30199090166974 867.1065786700998,323.26324065597225 921,309 C 974.8934213299002,294.73675934402775 1028.1982366451277,259.24902827778067 1071,267 C 1113.8017633548723,274.75097172221933 1146.1004747493887,325.74064623290525 1185,328 C 1223.8995252506113,330.25935376709475 1269.3998643573175,283.7883867905985 1313,271 C 1356.6001356426825,258.2116132094015 1398.3000678213411,279.1058066047008 1440,300 C 1440,300 1440,400 1440,400 Z");
|
||||
}
|
||||
100%{
|
||||
d: path("M 0,400 C 0,400 0,300 0,300 C 51.227644191181795,301.9763528409705 102.45528838236359,303.952705681941 145,297 C 187.5447116176364,290.047294318059 221.4064906617274,274.16553011320667 262,285 C 302.5935093382726,295.83446988679333 349.91874897072705,333.38517386523216 398,331 C 446.08125102927295,328.61482613476784 494.9185134553645,286.29377442586485 537,283 C 579.0814865446355,279.70622557413515 614.4071972078149,315.4397284313083 656,328 C 697.5928027921851,340.5602715686917 745.452697713376,329.94731184890185 792,315 C 838.547302286624,300.05268815109815 883.782011938681,280.77102417308436 929,279 C 974.217988061319,277.22897582691564 1019.4192545318997,292.9685914587609 1058,289 C 1096.5807454681003,285.0314085412391 1128.5409699337195,261.35460999187205 1174,264 C 1219.4590300662805,266.64539000812795 1278.416865733223,295.61296857375083 1325,306 C 1371.583134266777,316.38703142624917 1405.7915671333885,308.1935157131246 1440,300 C 1440,300 1440,400 1440,400 Z");
|
||||
}
|
||||
}</style><defs><linearGradient id="gradient" x1="4%" y1="70%" x2="96%" y2="30%"><stop offset="5%" stop-color="#8ed1fc"></stop><stop offset="95%" stop-color="#32ded4"></stop></linearGradient></defs><path d="M 0,400 C 0,400 0,300 0,300 C 51.227644191181795,301.9763528409705 102.45528838236359,303.952705681941 145,297 C 187.5447116176364,290.047294318059 221.4064906617274,274.16553011320667 262,285 C 302.5935093382726,295.83446988679333 349.91874897072705,333.38517386523216 398,331 C 446.08125102927295,328.61482613476784 494.9185134553645,286.29377442586485 537,283 C 579.0814865446355,279.70622557413515 614.4071972078149,315.4397284313083 656,328 C 697.5928027921851,340.5602715686917 745.452697713376,329.94731184890185 792,315 C 838.547302286624,300.05268815109815 883.782011938681,280.77102417308436 929,279 C 974.217988061319,277.22897582691564 1019.4192545318997,292.9685914587609 1058,289 C 1096.5807454681003,285.0314085412391 1128.5409699337195,261.35460999187205 1174,264 C 1219.4590300662805,266.64539000812795 1278.416865733223,295.61296857375083 1325,306 C 1371.583134266777,316.38703142624917 1405.7915671333885,308.1935157131246 1440,300 C 1440,300 1440,400 1440,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="1" class="transition-all duration-300 ease-in-out delay-150 path-2"></path></svg>
|
After Width: | Height: | Size: 20 KiB |
11
ui/src/components/restful/empty.vue
Normal file
11
ui/src/components/restful/empty.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty description="没有权限!请联系管理员添加权限!"></el-empty>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -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 = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container v-if="listDataOK">
|
||||
<el-header>
|
||||
<el-button @click="dialogAddVisible = true" size="large" type="primary"
|
||||
v-if="(resource_raw_node.meta.methods.post === true)">
|
||||
添加
|
||||
</el-button>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
<el-table-column prop="jsonValue" :label="fieldDescInfo.name"
|
||||
v-if="(fieldDescInfo.type === 'items')"></el-table-column>
|
||||
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"
|
||||
v-else></el-table-column>
|
||||
</template>
|
||||
<el-table-column prop="func" label="功 能">
|
||||
<template #default="scope">
|
||||
|
||||
<el-button size="default" type="success" @click="handleEdit( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.put === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Edit/>
|
||||
</el-icon>
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
<el-button size="default" type="danger" @click="handleDelete( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.delete === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules" label-position="right"
|
||||
label-width="130px">
|
||||
<template v-if="!hasListPermit">
|
||||
<component :is="empty"></component>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-container v-if="listDataOK">
|
||||
<el-header>
|
||||
<el-button @click="dialogAddVisible = true" size="large" type="primary"
|
||||
v-if="(resource_raw_node.meta.methods.post === true)">
|
||||
添加
|
||||
</el-button>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
<!--如何是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key" label-width="130px">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="num">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="Attach">
|
||||
<el-table :data="dialogAddForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<!-- 有可选项的字段,走下拉框或者多选框 -->
|
||||
<template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
v-model="dialogAddForm[fieldDescInfo.key]" style="width: 150px"
|
||||
:multiple="(fieldDescInfo.multi_choice === true)">
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 时间戳字段,展示时间选择器 -->
|
||||
<template v-else-if="(fieldDescInfo.type === 'Time')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-date-picker v-model="dialogAddForm[fieldDescInfo.key]" type="datetime"
|
||||
placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 否则就是普通字段 -->
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogAddForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="jsonValue" :label="fieldDescInfo.name"
|
||||
v-if="(fieldDescInfo.type === 'items')"></el-table-column>
|
||||
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"
|
||||
v-else></el-table-column>
|
||||
</template>
|
||||
<el-table-column prop="func" label="功 能">
|
||||
<template #default="scope">
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules" class="operation_form"
|
||||
label-width="130px">
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
|
||||
<!--如果是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right"
|
||||
label-width="130px">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="number">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="attachmentsList">
|
||||
<el-table :data="dialogEditForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-button size="default" type="success" @click="handleEdit( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.put === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Edit/>
|
||||
</el-icon>
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
<el-button size="default" type="danger" @click="handleDelete( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.delete === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<template v-if="(fieldDescInfo.uneditable !== true)">
|
||||
<!-- 表格数据分页 -->
|
||||
<div class="demo-pagination-block">
|
||||
<div class="demonstration"></div>
|
||||
<el-pagination
|
||||
v-model:current-page="current_page"
|
||||
v-model:page-size="page_size"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="400"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules" label-position="right"
|
||||
label-width="130px">
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
<!--如何是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key" label-width="130px">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="num">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="Attach">
|
||||
<el-table :data="dialogAddForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<!-- 有可选项的字段,走下拉框或者多选框 -->
|
||||
<template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
v-model="dialogEditForm[fieldDescInfo.key]" style="width: 150px"
|
||||
v-model="dialogAddForm[fieldDescInfo.key]" style="width: 150px"
|
||||
:multiple="(fieldDescInfo.multi_choice === true)">
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
@ -378,7 +318,7 @@ const handleCloseDialog = () => {
|
||||
<!-- 时间戳字段,展示时间选择器 -->
|
||||
<template v-else-if="(fieldDescInfo.type === 'Time')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-date-picker v-model="dialogEditForm[fieldDescInfo.key]" type="datetime"
|
||||
<el-date-picker v-model="dialogAddForm[fieldDescInfo.key]" type="datetime"
|
||||
placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
@ -387,36 +327,127 @@ const handleCloseDialog = () => {
|
||||
<!-- 否则就是普通字段 -->
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
<el-input v-model="dialogAddForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text" disabled></el-input>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules" class="operation_form"
|
||||
label-width="130px">
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
|
||||
<!--如果是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right"
|
||||
label-width="130px">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="number">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="attachmentsList">
|
||||
<el-table :data="dialogEditForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<template v-if="(fieldDescInfo.uneditable !== true)">
|
||||
<!-- 有可选项的字段,走下拉框或者多选框 -->
|
||||
<template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
v-model="dialogEditForm[fieldDescInfo.key]" style="width: 150px"
|
||||
:multiple="(fieldDescInfo.multi_choice === true)">
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 时间戳字段,展示时间选择器 -->
|
||||
<template v-else-if="(fieldDescInfo.type === 'Time')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-date-picker v-model="dialogEditForm[fieldDescInfo.key]" type="datetime"
|
||||
placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 否则就是普通字段 -->
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text" disabled></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">-->
|
||||
<!-- <el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
</template>
|
||||
|
||||
<!-- <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">-->
|
||||
<!-- <el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
</template>
|
||||
<el-form-item>
|
||||
<el-button @click="submitEdit(dialogEditFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="submitEdit(dialogEditFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-pagination-block {
|
||||
margin-top: 10px;
|
||||
|
||||
.el-pagination {
|
||||
right: 40px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
551
ui/src/components/restful/tableUser.vue
Normal file
551
ui/src/components/restful/tableUser.vue
Normal file
@ -0,0 +1,551 @@
|
||||
<script setup>
|
||||
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/localCache.js";
|
||||
import {getPermissionTreeByProjects, getSelectedPermissions} from "@/utils/permission.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,
|
||||
})
|
||||
|
||||
// console.log("enter table, resource:", cachedResource)
|
||||
|
||||
const projectsRoute = LocalCache.getCache("projectsRoute")
|
||||
|
||||
const permitTreeDefaultProps = {children: 'children', label: 'label'}
|
||||
const permitTree = ref([])
|
||||
const selectedPermitList = ref([])
|
||||
|
||||
const listData = async () => {
|
||||
try {
|
||||
let listParams = {
|
||||
page_no: 0,
|
||||
page_len: 100,
|
||||
}
|
||||
// console.log("list params:", listParams)
|
||||
const rspData = await resourceList(resource_url, listParams);
|
||||
listRsp.value = rspData;
|
||||
if (listRsp.value.code !== 200) throw new Error("请求失败,错误码:", listRsp.code);
|
||||
fieldsDescInfo.value = listRsp.value.data.fields_desc
|
||||
rows.value = listRsp.value.data.rows
|
||||
|
||||
|
||||
for (let i = 0; i < fieldsDescInfo.value.length; i++) {
|
||||
var field = fieldsDescInfo.value[i]
|
||||
dialogAddForm.value[field.key] = ''
|
||||
|
||||
if (field.required == true) {
|
||||
rules.value[field.key] = [{required: true, message: field.name + "不能为空", trigger: "blur"}]
|
||||
}
|
||||
|
||||
if (field.type == "items") {
|
||||
dialogAddForm.value[field.key] = []
|
||||
for (let j = 0; j < rows.value.length; j++) {
|
||||
rows.value[j].jsonValue = JSON.stringify(rows.value[j][field.key])
|
||||
}
|
||||
} else if (field.key == "Permissions") {
|
||||
// console.log("后端权限列表:", projectsRoute)
|
||||
const permitNodeList = getPermissionTreeByProjects(projectsRoute)
|
||||
// console.log("后端权限列表转换:", permitNodeList)
|
||||
permitTree.value.push(...permitNodeList)
|
||||
// console.log("权限树:", permitTree.value)
|
||||
} else if (field.key == 'UserPass') {
|
||||
for (let j = 0; j < rows.value.length; j++) {
|
||||
rows.value[j].jsonValue = "***"
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("字段:", field)
|
||||
}
|
||||
|
||||
// console.log('await list rsp:', listRsp.value, fieldsDescInfo.value, toRaw(rows.value), toRaw(rules.value))
|
||||
|
||||
listDataOK.value = true
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (hasListPermit) {
|
||||
listData();
|
||||
}
|
||||
})
|
||||
|
||||
const dialogAddVisible = ref(false)
|
||||
const dialogEditVisible = ref(false)
|
||||
const dialogAddFormRef = ref(null)
|
||||
const dialogEditFormRef = ref(null)
|
||||
|
||||
const dialogAddForm = ref({
|
||||
ServerIDs: [],
|
||||
Attach: [],
|
||||
Permissions: [],
|
||||
})
|
||||
const dialogEditForm = ref({})
|
||||
|
||||
|
||||
const submitAdd = async () => {
|
||||
try {
|
||||
await dialogAddFormRef.value.validate(valid => {
|
||||
if (valid) {
|
||||
dialogAddForm.value.Permissions = selectedPermitList.value
|
||||
console.log("commit add form:", dialogAddForm.value)
|
||||
resourcePost(resource_url, dialogAddForm.value).then((res) => {
|
||||
ElNotification({
|
||||
title: "添加结果通知",
|
||||
message: "添加成功!",
|
||||
type: 'success',
|
||||
duration: 4000,
|
||||
"show-close": true,
|
||||
})
|
||||
rows.value.push(res.data.dto)
|
||||
dialogAddVisible.value = false
|
||||
handleCloseDialog()
|
||||
}, (err) => {
|
||||
console.log("添加报错:", err)
|
||||
})
|
||||
console.log("提交数据:", dialogAddForm.value)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("校验失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const submitEdit = async () => {
|
||||
try {
|
||||
await dialogEditFormRef.value.validate(valid => {
|
||||
if (valid) {
|
||||
dialogEditForm.value.Permissions = selectedPermitList.value
|
||||
resourcePut(resource_url, dialogEditForm.value).then((res) => {
|
||||
ElNotification({
|
||||
title: "编辑结果通知",
|
||||
message: "编辑成功!",
|
||||
type: 'success',
|
||||
duration: 4000,
|
||||
"show-close": true,
|
||||
})
|
||||
dialogEditVisible.value = false
|
||||
rows.value[dialogEditForm.value.oldIndex] = res.data.dto
|
||||
handleCloseDialog()
|
||||
}, (err) => {
|
||||
console.log("添加报错:", err)
|
||||
})
|
||||
console.log("提交数据:", dialogEditForm.value)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("校验失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = (index, row) => {
|
||||
dialogEditForm.value.oldData = row
|
||||
dialogEditForm.value.oldIndex = index
|
||||
dialogEditForm.value = row
|
||||
console.log("edit data:", row)
|
||||
dialogEditVisible.value = true
|
||||
|
||||
if (permitTree.value.length != 0) {
|
||||
selectedPermitList.value = getSelectedPermissions(permitTree.value, row.Permissions)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = (index, row) => {
|
||||
ElMessageBox.confirm("确定要删除吗?").then(() => {
|
||||
resourceDelete(resource_url, {id: row.ID}).then((res) => {
|
||||
ElNotification({
|
||||
title: "删除结果通知",
|
||||
message: "删除数据[" + row.ID + "]成功!",
|
||||
type: 'success',
|
||||
duration: 4000,
|
||||
"show-close": true,
|
||||
})
|
||||
rows.value.splice(index, 1)
|
||||
}, (err) => {
|
||||
console.log("delet error:", err)
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function addItem(fieldDescInfo) {
|
||||
if (item.value.id == null || item.value.id == '' || item.value.id < 0) {
|
||||
ElMessage('请选择道具!')
|
||||
return;
|
||||
}
|
||||
if (item.value.num == null || item.value.num == '' || item.value.num <= 0) {
|
||||
ElMessage('请输入有效道具数量!')
|
||||
return;
|
||||
}
|
||||
|
||||
let d = {id: item.value.id, num: Number(item.value.num)};
|
||||
|
||||
for (let i = 0; i < fieldDescInfo.choices.length; i++) {
|
||||
const field = fieldDescInfo.choices[i]
|
||||
if (field.value === item.value.id) {
|
||||
d.item_type = field.type
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
console.log("add item:", d)
|
||||
|
||||
if (typeof dialogAddForm.value.Attach === typeof "") {
|
||||
dialogAddForm.value.Attach = [];
|
||||
}
|
||||
dialogAddForm.value.Attach.push(d);
|
||||
}
|
||||
|
||||
function deleteItem(row) {
|
||||
// 移除该对象
|
||||
let number = form.value.Attach.findIndex(item => item === row);
|
||||
dialogAddForm.value.Attach.splice(number, 1);
|
||||
}
|
||||
|
||||
const handlePermitCheckChange = (node, isChecked, hasChildChecked) => {
|
||||
if (isChecked) {
|
||||
if (node.key == "project") {
|
||||
// 直接选了整个项目
|
||||
node.children.forEach(resource => {
|
||||
resource.children.forEach(method => {
|
||||
selectedPermitList.value.push(method.permissionStr)
|
||||
})
|
||||
})
|
||||
} else if (node.key == "resource") {
|
||||
// 选了整个资源
|
||||
node.children.forEach(method => {
|
||||
selectedPermitList.value.push(method.permissionStr)
|
||||
})
|
||||
} else {
|
||||
selectedPermitList.value.push(node.permissionStr)
|
||||
}
|
||||
} else {
|
||||
if (node.key == "project") {
|
||||
// 直接去掉了整个项目
|
||||
node.children.forEach(resource => {
|
||||
resource.children.forEach(method => {
|
||||
selectedPermitList.value = selectedPermitList.value.filter(e => e !== method.permissionStr)
|
||||
})
|
||||
})
|
||||
} else if (node.key == "resource") {
|
||||
// 选了整个资源
|
||||
node.children.forEach(method => {
|
||||
selectedPermitList.value = selectedPermitList.value.filter(e => e !== method.permissionStr)
|
||||
})
|
||||
} else {
|
||||
selectedPermitList.value = selectedPermitList.value.filter(e => e !== node.permissionStr)
|
||||
}
|
||||
}
|
||||
console.log("权限被点击了:", node, isChecked, hasChildChecked);
|
||||
console.log("权限点击后:", selectedPermitList.value)
|
||||
}
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
console.log("关闭添加/编辑弹窗")
|
||||
dialogAddVisible.value = false
|
||||
dialogEditVisible.value = false
|
||||
dialogAddForm.value = {
|
||||
Attach: [],
|
||||
Permissions: [],
|
||||
}
|
||||
dialogEditForm.value = {}
|
||||
selectedPermitList.value = []
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="!hasListPermit">
|
||||
<component :is="empty"></component>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-container v-if="listDataOK">
|
||||
<el-header>
|
||||
<el-button @click="dialogAddVisible = true" size="large" type="primary"
|
||||
v-if="(resource_raw_node.meta.methods.post === true)">
|
||||
添加
|
||||
</el-button>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
<el-table-column prop="jsonValue" :label="fieldDescInfo.name"
|
||||
v-if="(fieldDescInfo.type === 'items')"></el-table-column>
|
||||
<el-table-column prop="jsonValue" :label="fieldDescInfo.name"
|
||||
v-else-if="(fieldDescInfo.key === 'UserPass')"></el-table-column>
|
||||
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"
|
||||
v-else-if="(fieldDescInfo.key === 'Permissions')" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :prop="fieldDescInfo.key" :label="fieldDescInfo.name"
|
||||
v-else></el-table-column>
|
||||
</template>
|
||||
<el-table-column prop="func" label="功 能">
|
||||
<template #default="scope">
|
||||
|
||||
<el-button size="default" type="success" @click="handleEdit( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.put === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Edit/>
|
||||
</el-icon>
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
<el-button size="default" type="danger" @click="handleDelete( scope.$index, scope.row)"
|
||||
v-if="(resource_raw_node.meta.methods.delete === true)">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 表格数据分页 -->
|
||||
<div class="demo-pagination-block">
|
||||
<div class="demonstration"></div>
|
||||
<el-pagination
|
||||
v-model:current-page="current_page"
|
||||
v-model:page-size="page_size"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="400"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules" label-position="right"
|
||||
label-width="130px">
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
<!--如何是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key" label-width="130px">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="num">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="Attach">
|
||||
<el-table :data="dialogAddForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<!-- 有可选项的字段,走下拉框或者多选框 -->
|
||||
<template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
v-model="dialogAddForm[fieldDescInfo.key]" style="width: 150px"
|
||||
:multiple="(fieldDescInfo.multi_choice === true)">
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 时间戳字段,展示时间选择器 -->
|
||||
<template v-else-if="(fieldDescInfo.type === 'Time')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-date-picker v-model="dialogAddForm[fieldDescInfo.key]" type="datetime"
|
||||
placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.key === 'Permissions')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tree ref="treeRef" :data="permitTree" show-checkbox node-key="id"
|
||||
:props="permitTreeDefaultProps" @check-change="handlePermitCheckChange">
|
||||
</el-tree>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 否则就是普通字段 -->
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogAddForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text" show-password
|
||||
v-if="(fieldDescInfo.key === 'UserPass')"></el-input>
|
||||
<el-input v-model="dialogAddForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text"
|
||||
v-else></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
|
||||
destroy-on-close>
|
||||
<el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules" class="operation_form"
|
||||
label-width="130px">
|
||||
<template v-for="fieldDescInfo in fieldsDescInfo">
|
||||
|
||||
<!--如果是items类型,就是物品下拉框+道具组合-->
|
||||
<template v-if="(fieldDescInfo.type === 'items')">
|
||||
<el-form :inline="true" :model="item" label-position="right"
|
||||
label-width="130px">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
|
||||
filterable>
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="number">
|
||||
<el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="奖励列表" prop="attachmentsList">
|
||||
<el-table :data="dialogEditForm.Attach" border>
|
||||
<el-table-column label="道具id" prop="id"/>
|
||||
<el-table-column label="数量" prop="num"/>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.readonly !== true)">
|
||||
<template v-if="(fieldDescInfo.uneditable !== true)">
|
||||
<!-- 有可选项的字段,走下拉框或者多选框 -->
|
||||
<template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
|
||||
<el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
v-model="dialogEditForm[fieldDescInfo.key]" style="width: 150px"
|
||||
:multiple="(fieldDescInfo.multi_choice === true)">
|
||||
<el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 时间戳字段,展示时间选择器 -->
|
||||
<template v-else-if="(fieldDescInfo.type === 'Time')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-date-picker v-model="dialogEditForm[fieldDescInfo.key]" type="datetime"
|
||||
placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else-if="(fieldDescInfo.key === 'Permissions')">
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-tree ref="treeRef" :data="permitTree" show-checkbox node-key="id"
|
||||
:default-checked-keys="selectedPermitList"
|
||||
:props="permitTreeDefaultProps" @check-change="handlePermitCheckChange">
|
||||
</el-tree>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 否则就是普通字段 -->
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text" show-password
|
||||
v-if="(fieldDescInfo.key === 'UserPass')"></el-input>
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text"
|
||||
v-else></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
|
||||
<el-input v-model="dialogEditForm[fieldDescInfo.key]"
|
||||
:placeholder="fieldDescInfo.help_text" disabled></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">-->
|
||||
<!-- <el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="submitEdit(dialogEditFormRef)" size="large" type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-pagination-block {
|
||||
margin-top: 10px;
|
||||
|
||||
.el-pagination {
|
||||
right: 40px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@ -1,12 +1,21 @@
|
||||
import router, {projectOpTreeRoutes, setProjectOperationRoutes} from './router'
|
||||
import {generateRoutes} from "@/api/game_api.js";
|
||||
import LocalCache, {cachedProject, cachedProjectResource} from '@/stores/project.js'
|
||||
import router, {isGetUserInfo, projectOpTreeRoutes, setProjectOperationRoutes} from './router'
|
||||
import {cachedProject, cachedProjectResource} from '@/stores/project.js'
|
||||
import {getUserInfo} from "@/api/sys.js";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.path === '/login') {
|
||||
// 推首页
|
||||
next({path: '/'})
|
||||
next()
|
||||
} else {
|
||||
const token = LocalCache.getCache("token");
|
||||
if (!token) {
|
||||
// 没有token
|
||||
console.log("重定向到login")
|
||||
next('/login?redirect=/home') // 没有token都重定向到登录页面
|
||||
return
|
||||
}
|
||||
|
||||
// 有token就去拉最新的用户数据,动态生成菜单
|
||||
if (to.path.startsWith('/project')) {
|
||||
const pathSegments = to.path.split("/").filter(segment => segment !== "");
|
||||
if (pathSegments.length === 3) {
|
||||
@ -22,18 +31,23 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (projectOpTreeRoutes.value.length === 0) {
|
||||
generateRoutes().then((res) => {
|
||||
if (isGetUserInfo.value === false) {
|
||||
getUserInfo().then((res) => {
|
||||
const projectList = ref([])
|
||||
// res.data.user
|
||||
// res.data.token
|
||||
projectList.value = res.data.projects
|
||||
LocalCache.setCache('user', res.data.user_info)
|
||||
setProjectOperationRoutes(projectList.value)
|
||||
// console.log("all routes:", router.getRoutes())
|
||||
next({...to, replace: true}); // 在已有页面里查找
|
||||
}, (err) => {
|
||||
console.log("跳转路径:", to.path, " 报错:", err)
|
||||
LocalCache.deleteCache("token")
|
||||
LocalCache.deleteCache("user")
|
||||
})
|
||||
} else {
|
||||
console.log("op tree routes length valid:", projectOpTreeRoutes.value.length)
|
||||
// console.log("op tree routes length valid:", projectOpTreeRoutes.value.length)
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
export const isGetUserInfo = ref(false) // 是否获取过后端路由接口,用于第一次进页面或者刷新页面拉取路由恢复菜单
|
||||
export const projectOpTreeRoutes = ref([]) // 项目操作的树形路由,用于菜单树生成
|
||||
export const projectOpFlatRoutes = ref([]) // 项目操作的展平路由,用于直接挂钩到父组件(即home页面),否则点击子菜单无法在父页面内容区显示)
|
||||
|
||||
@ -13,13 +15,41 @@ export const constProjectResourceRoute = {
|
||||
resource_url: "/project",
|
||||
methods: {
|
||||
get: true,
|
||||
post: true,
|
||||
put: true,
|
||||
delete: true,
|
||||
}
|
||||
},
|
||||
component: () => import('@/views/project/project.vue'),
|
||||
}
|
||||
|
||||
export const constUserChildrenRoutes = [
|
||||
{
|
||||
path: "/usermanager",
|
||||
name: "usermanager",
|
||||
meta: {
|
||||
name: "用户管理",
|
||||
icon: "User",
|
||||
},
|
||||
component: () => import('@/views/user/user.vue')
|
||||
},
|
||||
{
|
||||
path: "/character",
|
||||
name: "character",
|
||||
meta: {
|
||||
name: "角色管理",
|
||||
icon: "Avatar",
|
||||
},
|
||||
component: () => import('@/views/user/character.vue'),
|
||||
}
|
||||
]
|
||||
|
||||
export const constHomeChildrenRoutes = [
|
||||
{
|
||||
path: '/welcome',
|
||||
name: 'welcome',
|
||||
component: () => import('@/views/welcome.vue')
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
name: 'user',
|
||||
@ -27,7 +57,7 @@ export const constHomeChildrenRoutes = [
|
||||
name: "user",
|
||||
icon: "User",
|
||||
},
|
||||
component: () => import('@/views/user/user.vue')
|
||||
children: constUserChildrenRoutes,
|
||||
},
|
||||
constProjectResourceRoute,
|
||||
]
|
||||
@ -42,6 +72,12 @@ const homeRoute = {
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
component: () => import("@/views/Login.vue"),
|
||||
hidden: true
|
||||
},
|
||||
homeRoute,
|
||||
],
|
||||
})
|
||||
@ -51,11 +87,13 @@ export default router
|
||||
export function setProjectOperationRoutes(projectList) {
|
||||
// console.log("resourceList:", projectList)
|
||||
|
||||
isGetUserInfo.value = true
|
||||
projectOpTreeRoutes.value = []
|
||||
projectOpFlatRoutes.value = []
|
||||
|
||||
for (let i = 0; i < projectList.length; i++) {
|
||||
const project = projectList[i]
|
||||
let projectHasAnyResourcePermission = false // 判断项目是否至少有一个资源权限,否则就不显示这个项目菜单
|
||||
const projectRoute = {
|
||||
path: '/project/' + project.project_id,
|
||||
name: project.project_id,
|
||||
@ -85,6 +123,9 @@ export function setProjectOperationRoutes(projectList) {
|
||||
props: true
|
||||
}
|
||||
resource.show_methods.forEach((method) => {
|
||||
if (method == "get") {
|
||||
projectHasAnyResourcePermission = true
|
||||
}
|
||||
resourceRoute.meta.methods[method] = true
|
||||
})
|
||||
|
||||
@ -95,10 +136,13 @@ export function setProjectOperationRoutes(projectList) {
|
||||
// console.log("add resource route:", resourceRoute)
|
||||
})
|
||||
|
||||
projectOpTreeRoutes.value.push(projectRoute)
|
||||
if (projectHasAnyResourcePermission) {
|
||||
projectOpTreeRoutes.value.push(projectRoute)
|
||||
}
|
||||
homeRoute.children = constHomeChildrenRoutes.concat(projectOpFlatRoutes.value)
|
||||
router.addRoute(homeRoute)
|
||||
}
|
||||
|
||||
console.log("after set all routes:", router.getRoutes())
|
||||
LocalCache.setCache("projectsRoute", projectList)
|
||||
// console.log("after set all routes:", router.getRoutes())
|
||||
}
|
||||
|
43
ui/src/stores/expireCache.js
Normal file
43
ui/src/stores/expireCache.js
Normal file
@ -0,0 +1,43 @@
|
||||
import localCache from "@/stores/localCache";
|
||||
|
||||
class ExpireCache {
|
||||
// 添加
|
||||
setCache(key, value, expire) {
|
||||
let obj = {
|
||||
data: value,
|
||||
time: Date.now(), // 毫秒时间戳
|
||||
expire: expire * 1000, // 传进来秒
|
||||
}
|
||||
const realKey = key + ".expire"
|
||||
localCache.setCache(realKey, obj)
|
||||
// window.localStorage.setItem(key, JSON.stringify(obj))
|
||||
}
|
||||
|
||||
// 查找
|
||||
getCache(key) {
|
||||
const realKey = key + ".expire"
|
||||
let val = localCache.getCache(realKey)
|
||||
if (!val) {
|
||||
return val
|
||||
} else {
|
||||
if (Date.now() - val.time > val.expire) {
|
||||
localCache.deleteCache(realKey)
|
||||
return null
|
||||
}
|
||||
return val.data
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
deleteCache(key) {
|
||||
const realKey = key + ".expire"
|
||||
localCache.deleteCache(realKey)
|
||||
}
|
||||
|
||||
// 清理
|
||||
clearCache() {
|
||||
// localCache.clearCache()
|
||||
}
|
||||
}
|
||||
|
||||
export default new ExpireCache()
|
27
ui/src/stores/localCache.js
Normal file
27
ui/src/stores/localCache.js
Normal file
@ -0,0 +1,27 @@
|
||||
class LocalCache {
|
||||
// 添加
|
||||
setCache(key, value) {
|
||||
window.localStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
// 查找
|
||||
getCache(key) {
|
||||
// obj=>sting=>obj
|
||||
const value = window.localStorage.getItem(key)
|
||||
if (value) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
deleteCache(key) {
|
||||
window.localStorage.removeItem(key)
|
||||
}
|
||||
|
||||
// 清理
|
||||
clearCache() {
|
||||
// window.localStorage.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export default new LocalCache()
|
@ -1,33 +1,9 @@
|
||||
import {ref} from 'vue'
|
||||
import {defineStore} from 'pinia'
|
||||
|
||||
class LocalCache {
|
||||
// 添加
|
||||
setCache(key, value) {
|
||||
window.localStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
// 查找
|
||||
getCache(key) {
|
||||
// obj=>sting=>obj
|
||||
const value = window.localStorage.getItem(key)
|
||||
if (value) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
deleteCache(key) {
|
||||
window.localStorage.removeItem(key)
|
||||
}
|
||||
|
||||
// 清理
|
||||
clearCache() {
|
||||
// window.localStorage.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export default new LocalCache()
|
||||
/*
|
||||
项目pinia缓存,刷新页面就清空了,适合一些页面跳转传参逻辑
|
||||
*/
|
||||
|
||||
export const cachedProject = defineStore('clickedProject', () => {
|
||||
const project = ref({})
|
||||
|
73
ui/src/utils/permission.js
Normal file
73
ui/src/utils/permission.js
Normal file
@ -0,0 +1,73 @@
|
||||
export function getPermissionTreeByProjects(projectsRoute) {
|
||||
// 权限字段
|
||||
let projectNodes = []
|
||||
let id = 0;
|
||||
|
||||
|
||||
projectsRoute.forEach(pro => {
|
||||
id++
|
||||
let proNode = {
|
||||
id: id,
|
||||
label: pro.project_name,
|
||||
key: "project",
|
||||
children: []
|
||||
}
|
||||
pro.resource_total_list.forEach(resource => {
|
||||
id++
|
||||
let resourceNode = {
|
||||
id: id,
|
||||
label: resource.desc,
|
||||
key: "resource",
|
||||
children: []
|
||||
}
|
||||
// console.log("资源方法:", resource.desc, resource.show_methods)
|
||||
for (let i = 0; i < resource.show_methods.length; i++) {
|
||||
id++
|
||||
const method = resource.show_methods[i]
|
||||
const permissionStr = resource.methods_permission[i]
|
||||
|
||||
let methodDesc = ""
|
||||
if (method === "get") {
|
||||
methodDesc = "查看"
|
||||
} else if (method === "post") {
|
||||
methodDesc = "添加"
|
||||
} else if (method === "put") {
|
||||
methodDesc = "编辑"
|
||||
} else if (method === "delete") {
|
||||
methodDesc = "删除"
|
||||
}
|
||||
|
||||
const methodNode = {
|
||||
id: permissionStr,
|
||||
label: methodDesc,
|
||||
key: pro.project_id + "_" + resource.resource + "_" + method,
|
||||
permissionStr: permissionStr,
|
||||
}
|
||||
// console.log("资源方法:", resource.desc, methodNodea)
|
||||
resourceNode.children.push(methodNode)
|
||||
}
|
||||
proNode.children.push(resourceNode)
|
||||
})
|
||||
projectNodes.push(proNode)
|
||||
})
|
||||
|
||||
|
||||
return projectNodes
|
||||
}
|
||||
|
||||
export function getSelectedPermissions(projectNodes, hasPermissions) {
|
||||
let defaultCheckedKeys = []
|
||||
projectNodes.forEach(proNode => {
|
||||
proNode.children.forEach(resourceNode => {
|
||||
resourceNode.children.forEach(methodNode => {
|
||||
for (let i = 0; i < hasPermissions.length; i++) {
|
||||
if (hasPermissions[i] === methodNode.permissionStr) {
|
||||
defaultCheckedKeys.push(methodNode.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return defaultCheckedKeys
|
||||
}
|
@ -2,16 +2,19 @@ import axios from 'axios'
|
||||
import {ElMessageBox} from "element-plus";
|
||||
// import LocalCache from "@/stores/localCache";
|
||||
import {useRouter} from "vue-router";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
// import ExpireCache from "@/stores/expireCache";
|
||||
|
||||
// 创建axios实例
|
||||
const service=axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"Content-type": "application/json;charset=utf-8",
|
||||
"Cache-Control": 'no-cache',
|
||||
}
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"Content-type": "application/json;charset=utf-8",
|
||||
"Cache-Control": 'no-cache',
|
||||
"UserId": 0,
|
||||
"Token": "",
|
||||
}
|
||||
})
|
||||
|
||||
// axios.post('192.168.1.60:8888/login', {username: "likun", password: "likun1"}).then((res)=>{
|
||||
@ -21,41 +24,57 @@ const service=axios.create({
|
||||
// console.log(err)
|
||||
// })
|
||||
|
||||
const reqInterceptor = (config)=>{
|
||||
config.headers=config.headers || {}
|
||||
// const token = ExpireCache.getCache('token')
|
||||
// if(token){
|
||||
// config.headers["x-token"]=token || ""
|
||||
// } else {
|
||||
// console.log("not found local storage token")
|
||||
// }
|
||||
return config
|
||||
const reqInterceptor = (config) => {
|
||||
|
||||
let user = LocalCache.getCache("user");
|
||||
let token = LocalCache.getCache("token");
|
||||
|
||||
let userId = user ? parseInt(user.user_id, 10) : 0;
|
||||
token = token ? token : "";
|
||||
|
||||
config.headers = config.headers || {}
|
||||
config.headers.UserId = userId;
|
||||
config.headers.Token = token;
|
||||
|
||||
// const token = ExpireCache.getCache('token')
|
||||
// if(token){
|
||||
// config.headers["x-token"]=token || ""
|
||||
// } else {
|
||||
// console.log("not found local storage token")
|
||||
// }
|
||||
return config
|
||||
}
|
||||
|
||||
const resInterceptor = (res)=>{
|
||||
console.log("res:", res.data)
|
||||
const code=res.data.code
|
||||
if(code!=200) {
|
||||
console.log("interceptor err code", res)
|
||||
ElMessageBox.alert("请求服务器成功,但是逻辑错误:" + res.data.message, "服务器错误码" + code, {
|
||||
type: "warning",
|
||||
confirmButtonText: '知道了',
|
||||
const resInterceptor = (res) => {
|
||||
console.log("res:", res.data)
|
||||
const code = res.data.code
|
||||
if (code != 200) {
|
||||
if (code === 5) {
|
||||
// token过期
|
||||
console.log("token无效,重新登录!")
|
||||
location.href = "/login"
|
||||
return Promise.reject()
|
||||
}
|
||||
console.log("interceptor err code", res)
|
||||
ElMessageBox.alert("请求服务器成功,但是逻辑错误:" + res.data.msg, "服务器错误码[" + code + "]", {
|
||||
type: "warning",
|
||||
confirmButtonText: '知道了',
|
||||
})
|
||||
return Promise.reject(res.data)
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
||||
const resErrorInterceptor = (err) => {
|
||||
console.log(err)
|
||||
const code = err.response && err.response.status || -1
|
||||
const message = err.response && err.response.data.message || err
|
||||
ElMessageBox.alert(message, "请求服务器返回http错误码-" + code, {
|
||||
type: "error",
|
||||
confirmButtonText: '知道了',
|
||||
})
|
||||
return Promise.reject(res.data)
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
|
||||
const resErrorInterceptor = (err)=>{
|
||||
console.log(err)
|
||||
const code = err.response && err.response.status || -1
|
||||
const message = err.response && err.response.data.message || err
|
||||
ElMessageBox.alert(message, "请求服务器返回http错误码-" + code, {
|
||||
type: "error",
|
||||
confirmButtonText: '知道了',
|
||||
})
|
||||
return Promise.reject(err)
|
||||
return Promise.reject(err)
|
||||
}
|
||||
|
||||
// 请求拦截
|
||||
|
@ -2,22 +2,59 @@
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {computed} from 'vue'
|
||||
import avatarUrl from '@/assets/icon/header.png'
|
||||
import {projectOpTreeRoutes} from '@/router/index.js'
|
||||
import LocalCache from "@/stores/project.js";
|
||||
import {constUserChildrenRoutes, projectOpTreeRoutes} from '@/router/index.js'
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
import welcome from '@/views/welcome.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const userInfo = LocalCache.getCache("user")
|
||||
const nickName = userInfo.nick_name
|
||||
|
||||
// 计算当前激活的菜单项
|
||||
const activeMenu = computed(() => route.path)
|
||||
|
||||
|
||||
const hasClickedMenu = ref(false)
|
||||
|
||||
router.push("/welcome")
|
||||
|
||||
const handleEnterIndex = () => {
|
||||
router.push("/welcome")
|
||||
}
|
||||
|
||||
// 处理菜单点击事件
|
||||
const handleMenuSelect = (clickResource) => {
|
||||
console.log("点击资源:", clickResource)
|
||||
LocalCache.setCache("resource", clickResource)
|
||||
router.push({path: clickResource.path})
|
||||
hasClickedMenu.value = true
|
||||
}
|
||||
|
||||
function handleCommand(command) {
|
||||
switch (command) {
|
||||
case "logout":
|
||||
logout();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
LocalCache.deleteCache("user")
|
||||
LocalCache.deleteCache("token")
|
||||
router.push('/login')
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -27,23 +64,35 @@ const handleMenuSelect = (clickResource) => {
|
||||
<el-aside class="el-aside-demo">
|
||||
<div class="sidebar-content">
|
||||
<!-- <el-avatar shape="square" :size="100" :src="avatarUrl"></el-avatar>-->
|
||||
<div class="sidebar-logo">
|
||||
<div class="sidebar-logo" @click="handleEnterIndex">
|
||||
<img src="@/assets/logo.svg" class="logo" alt="Logo">
|
||||
<span class="system-name">游戏后台管理系统</span>
|
||||
</div>
|
||||
|
||||
<el-menu :default-active="activeMenu" class="el-menu-vertical-demo" :collapse="false">
|
||||
|
||||
<!-- 静态菜单 -->
|
||||
<el-menu-item index="/user" @click="$router.push('/user')">
|
||||
<span>用户管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/project" @click="$router.push('/project')">
|
||||
<span>项目管理</span>
|
||||
</el-menu-item>
|
||||
<div style="border-bottom: 1px whitesmoke solid">
|
||||
<!-- 静态菜单 -->
|
||||
<el-sub-menu index="/user">
|
||||
<template #title>
|
||||
<span>用户管理</span>
|
||||
</template>
|
||||
<el-menu-item v-for="user in constUserChildrenRoutes" :index="user.path"
|
||||
@click="$router.push(user.path)">
|
||||
<el-icon>
|
||||
<component class="el-icon" :is="user.meta.icon"></component>
|
||||
</el-icon>
|
||||
<span>{{ user.meta.name }}</span>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item index="/project" @click="$router.push('/project')">
|
||||
<span>项目管理</span>
|
||||
</el-menu-item>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 分割条 -->
|
||||
<el-divider></el-divider>
|
||||
<!-- <el-divider></el-divider>-->
|
||||
|
||||
<!-- 动态菜单:每个项目操作 -->
|
||||
<template v-for="project in projectOpTreeRoutes">
|
||||
@ -69,11 +118,11 @@ const handleMenuSelect = (clickResource) => {
|
||||
<el-header class="el-header-demo">
|
||||
<div class="header-content">
|
||||
<div class="avatar-container">
|
||||
<el-dropdown class="right-menu-item hover-effect" trigger="click">
|
||||
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
|
||||
|
||||
<!-- 头像 -->
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatarUrl" class="user-avatar"/>
|
||||
<span style="font-size: 20px">{{ nickName }}</span>
|
||||
<el-icon color="black" size="20">
|
||||
<ArrowDownBold/>
|
||||
</el-icon>
|
||||
@ -83,8 +132,8 @@ const handleMenuSelect = (clickResource) => {
|
||||
<!-- 头像操作下拉菜单 -->
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||
<el-dropdown-item>退出登录</el-dropdown-item>
|
||||
<!-- <el-dropdown-item>个人中心</el-dropdown-item>-->
|
||||
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -158,6 +207,7 @@ h1 {
|
||||
background: transparent; /* 关键:透明背景 */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
|
||||
.logo {
|
||||
width: 50px;
|
||||
@ -187,6 +237,11 @@ h1 {
|
||||
background-color: cadetblue;
|
||||
}
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-sub-menu) {
|
||||
color: #fff;
|
||||
background-color: cadetblue;
|
||||
}
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-sub-menu__title) {
|
||||
color: #fff;
|
||||
}
|
||||
@ -220,13 +275,8 @@ h1 {
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
|
262
ui/src/views/Login.vue
Normal file
262
ui/src/views/Login.vue
Normal file
@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<div class="login-box">
|
||||
<div :class="{ active: currentModel, container: true,animate__animated :true, animate__flipInX :true}">
|
||||
<div class="form-container sign-in-container">
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:model="loginForm"
|
||||
status-icon
|
||||
:rules="loginRules"
|
||||
class="form"
|
||||
>
|
||||
<el-form-item class="form-item" prop="username">
|
||||
<el-input v-model="loginForm.user" placeholder="用户名" autocomplete="off"
|
||||
@keyup.enter="submitForm(loginForm)"/>
|
||||
</el-form-item>
|
||||
<el-form-item class="form-item" prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
placeholder="密码"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
@keyup.enter="submitForm(loginForm)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item>-->
|
||||
<el-button class="theme-button" type="primary" @click="submitForm(loginForm)" @keydown.enter="keyDown()">登
|
||||
陆
|
||||
</el-button>
|
||||
<p class="forget"
|
||||
@click="
|
||||
currentModel = !currentModel;
|
||||
isForget = true;
|
||||
">
|
||||
--- Forget Passwoed----
|
||||
</p>
|
||||
|
||||
<!-- </el-form-item>-->
|
||||
</el-form>
|
||||
</div>
|
||||
<!--覆盖容器-->
|
||||
<div class="overlay_container">
|
||||
<div class="overlay">
|
||||
<div class="overlay_panel overlay_right_container">
|
||||
<h2 class="container-title">hello friend!</h2>
|
||||
<p>输入您的个人信息,以便使用后台管理系统</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineComponent, reactive, toRefs, ref} from "vue";
|
||||
import LocalCache from "@/stores/localCache";
|
||||
import ExpireCache from "@/stores/expireCache";
|
||||
import {login} from "@/api/sys.js";
|
||||
import router from "@/router/index.js";
|
||||
|
||||
const redirect = ref(undefined);
|
||||
const {proxy} = getCurrentInstance();
|
||||
|
||||
const loginForm = ref({
|
||||
user: "",
|
||||
password: "",
|
||||
})
|
||||
|
||||
const loginRules = {
|
||||
user: [{required: true, trigger: "blur", message: "请输入您的账号"}],
|
||||
password: [{required: true, trigger: "blur", message: "请输入您的密码"}],
|
||||
// code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||
};
|
||||
|
||||
const submitForm = (formEl) => {
|
||||
if (!formEl) return
|
||||
// 对表单的内容进行验证
|
||||
proxy.$refs.ruleFormRef.validate((valid) => {
|
||||
if (valid) {
|
||||
login(loginForm.value).then((res) => {
|
||||
// 保存token,跳转
|
||||
// console.log('登陆成功,用户名:', res.payload.username)
|
||||
// console.log('登陆成功,token:', res.payload.token)
|
||||
LocalCache.setCache('token', res.data.token_info.token)
|
||||
LocalCache.setCache('user', res.data.user_info)
|
||||
ExpireCache.setCache("token", res.data.token_info.token, res.data.token_info.expire_at)
|
||||
console.log("登录成功,返回数据:", res.data, ",准备重定向到页面:", redirect.value)
|
||||
router.push({path: redirect.value || "/"})
|
||||
}, (res) => {
|
||||
console.log("login response error:")
|
||||
console.log(res)
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
loginForm.value.user = ""
|
||||
loginForm.value.password = ""
|
||||
}
|
||||
|
||||
const keyDown = (e) => {
|
||||
if (e.keyCode === 13 || e.keyCode === 100) {
|
||||
submitForm(loginForm.value)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #f6f5f7;
|
||||
background-size: cover;
|
||||
background-image: url("@/assets/img/login-background.svg");
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 768px;
|
||||
height: 480px;
|
||||
background-color: white;
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
/*border: solid;*/
|
||||
background-color: white;
|
||||
transition: all 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
text-align: -webkit-center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 50px;
|
||||
/*border: solid;*/
|
||||
}
|
||||
|
||||
.form-item {
|
||||
width: 100%;
|
||||
/*border: solid;*/
|
||||
:deep(.el-input-group__append) {
|
||||
:deep(.el-button) {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append) {
|
||||
border-radius: 20px;
|
||||
margin-left: 10px;
|
||||
background: white;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.forget {
|
||||
font-size: 12px;
|
||||
color: rgba(50, 50, 50, 0.6);
|
||||
position: relative;
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.container.active .sign-up-container {
|
||||
transform: translateX(100%);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.container.active .sign-in-container {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.container.active .overlay_container {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.container.active .overlay {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
|
||||
.theme-button {
|
||||
background: rgba(64, 158, 255, 0.7);
|
||||
padding: 10px 50px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 20px;
|
||||
text-transform: uppercase;
|
||||
color: white;
|
||||
margin-top: 10px;
|
||||
outline: none;
|
||||
transition: transform 80;
|
||||
}
|
||||
|
||||
.overlay_container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
z-index: 100;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
transition: all 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
left: -100%;
|
||||
background-color: #42b983;
|
||||
}
|
||||
|
||||
.overlay_panel {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
color: white;
|
||||
padding: 0 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.overlay_panel button {
|
||||
background-color: transparent;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.overlay_panel p {
|
||||
font-size: 12px;
|
||||
margin: 10px 0 15px 0;
|
||||
}
|
||||
|
||||
.overlay_right_container {
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
|
||||
import tableView from '@/components/restful/table.vue'
|
||||
import LocalCache from "@/stores/project.js";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
import {useRouter} from 'vue-router'
|
||||
import {constProjectResourceRoute} from "@/router/index.js";
|
||||
// import {Aim, ArrowRightBold} from '@element-plus/icons-vue'
|
||||
@ -10,7 +10,12 @@ LocalCache.setCache("project", {})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
LocalCache.setCache("resource", constProjectResourceRoute)
|
||||
let resourceCache = constProjectResourceRoute
|
||||
const userInfo = LocalCache.getCache("user")
|
||||
if (userInfo.character !== "admin") {
|
||||
resourceCache.meta.methods = {} // 先只有管理员有权限增删改查项目
|
||||
}
|
||||
LocalCache.setCache("resource", resourceCache)
|
||||
|
||||
</script>
|
||||
|
||||
|
34
ui/src/views/user/character.vue
Normal file
34
ui/src/views/user/character.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<script setup>
|
||||
import tableView from "@/components/restful/tableUser.vue";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
let resourceCache = {
|
||||
meta: {
|
||||
desc: 'character',
|
||||
resource: 'character',
|
||||
resource_url: '/resource/character',
|
||||
methods: {
|
||||
get: true,
|
||||
post: true,
|
||||
put: true,
|
||||
delete: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const userInfo = LocalCache.getCache("user")
|
||||
if (userInfo.character !== "admin") {
|
||||
resourceCache.meta.methods = {} // 先只有管理员有权限动用户模块
|
||||
}
|
||||
LocalCache.setCache("resource", resourceCache)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<component :is="tableView"></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,9 +1,32 @@
|
||||
<script setup>
|
||||
import tableView from "@/components/restful/tableUser.vue";
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
let resourceCache = {
|
||||
meta: {
|
||||
desc: 'user',
|
||||
resource: 'user',
|
||||
resource_url: '/resource/user',
|
||||
methods: {
|
||||
get: true,
|
||||
post: true,
|
||||
put: true,
|
||||
delete: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const userInfo = LocalCache.getCache("user")
|
||||
if (userInfo.character !== "admin") {
|
||||
resourceCache.meta.methods = {} // 先只有管理员有权限动用户模块
|
||||
}
|
||||
LocalCache.setCache("resource", resourceCache)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
用户管理
|
||||
<div>
|
||||
<component :is="tableView"></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
18
ui/src/views/welcome.vue
Normal file
18
ui/src/views/welcome.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<script setup>
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
|
||||
const userInfo = LocalCache.getCache("user")
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span style="font-size:40px">亲爱的<span style="color: darkslategrey;font-size: 50px"> {{
|
||||
userInfo.nick_name
|
||||
}} </span>!欢迎使用本公司后台管理系统!</span>
|
||||
<el-divider></el-divider>
|
||||
<span style="font-size:40px">硬盘有价,数据无价,操作不规范,亲人两行泪。</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user