package domain import ( "admin/apps/game/api" "admin/apps/game/domain/entity" "admin/apps/game/domain/projects" "admin/apps/game/domain/repo" "admin/apps/game/model" "admin/internal/consts" "admin/internal/errcode" "admin/internal/event" dto2 "admin/internal/model/dto" "admin/lib/xlog" "database/sql" "encoding/json" "fmt" "gorm.io/gorm" "reflect" "strconv" "strings" "time" ) type CommonResourceService struct { projectRepo repo.IProjectRepo serverRepo repo.IServerRepo noticeRepo repo.INoticeRepo } func (svc *CommonResourceService) initCommonResourcesRepo(db *gorm.DB) { r(consts.ResourcesName_Project, "项目管理", repo.NewCommonResourceRepo(db, &model.Project{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) { serverRepo := r(consts.ResourcesName_Server, "服务器管理", repo.NewCommonResourceRepo(db, &model.Server{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) serverRepo.GlobalBtns = []*ResourceBtnInfo{ {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Server_DownAll, Name: "一键停服", BtnColorType: "info"}, svc.handleServerUpOrDown}, {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Server_UpAll, Name: "一键起服", BtnColorType: "warning"}, svc.handleServerUpOrDown}, {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Server_ExportCdn, Name: "一键导出cdn", BtnColorType: "danger"}, svc.handleServerExportCdn}, {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Server_ExportCdn, Name: "预览导出cdn", BtnColorType: "default"}, svc.handleServerExportCdn}, } serverRepo.RowBtns = []*ResourceBtnInfo{ {&api.ResourceBtnInfo{Key: consts.BtnKeyRow_Server_Down, Name: "停服", BtnColorType: "info"}, svc.handleServerUpOrDown}, {&api.ResourceBtnInfo{Key: consts.BtnKeyRow_Server_Up, Name: "起服", BtnColorType: "warning"}, svc.handleServerUpOrDown}, } } { noticeRepo := r(consts.ResourcesName_Notice, "登陆前公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) noticeRepo.GlobalBtns = []*ResourceBtnInfo{ {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Notice_DisableAll, Name: "一键禁用", BtnColorType: "info"}, svc.handleNoticeDisable}, {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Notice_EnableAll, Name: "一键启用", BtnColorType: "warning"}, svc.handleNoticeEnable}, } noticeRepo.RowBtns = []*ResourceBtnInfo{ {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Notice_Disable, Name: "禁用", BtnColorType: "info"}, svc.handleNoticeDisable}, {&api.ResourceBtnInfo{Key: consts.BtnKeyGlobal_Notice_Enable, Name: "启用", BtnColorType: "warning"}, svc.handleNoticeEnable}, } } r(consts.ResourcesName_Account, "账号列表", repo.NewCommonResourceRepo(db, &model.Account{}), ShowMethod_Get) // 账号管理不需要在后台读写数据,都是通过项目api拉 r(consts.ResourcesName_SupportAccount, "扶持账号", repo.NewCommonResourceRepo(db, &model.SupportAccount{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete) // 扶持账号,只是标记哪些账号是扶持好 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) // 个人邮件发放就没法撤回? { globalMailRepo := r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post) globalMailRepo.HasDelayInvokeCreateHook = true } // 直接删除,别修改了,玩家收到的更乱 r(consts.ResourcesName_CDKey, "礼包码", repo.NewCommonResourceRepo(db, &model.CDKey{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) r(consts.ResourcesName_ItemBag, "道具礼包", repo.NewCommonResourceRepo(db, &model.ItemBag{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete) //r(consts.ResourcesName_DevicePush, "设备推送(暂无)", repo.NewCommonResourceRepo(db, &model.DevicePush{}), ShowMethod_Get) } func NewCommonResourceService(db *gorm.DB) *CommonResourceService { svc := &CommonResourceService{ projectRepo: repo.NewProjectRepo(db), serverRepo: repo.NewServerRepo(db), noticeRepo: repo.NewNoticeRepo(db), } svc.initCommonResourcesRepo(db) svc.startEventSubscriber() svc.startLoadAllDelayInvokeDbData() return svc } func (svc *CommonResourceService) startLoadAllDelayInvokeDbData() { for _, repo := range commResourcesRepo { if repo.HasDelayInvokeCreateHook { repo.Repo.ListPagination("`delay_invoke_create_hook` is not NULL and `delay_invoke_create_hook` > ?", []any{time.Now()}, func(po model.IModel) { var projectId int var resource string = repo.Resource var delayAt time.Time //xlog.Infof("过滤数据:%+v", po) switch repo.Resource { case consts.ResourcesName_MailGlobal: dbData := po.(*model.GlobalMail) projectId = dbData.ProjectId if !dbData.DelayInvokeCreateHook.Valid || dbData.DelayInvokeCreateHook.Time.Before(time.Now()) { return } delayAt = dbData.DelayInvokeCreateHook.Time default: return } // 延迟执行钩子调用 xlog.Infof("起服查看全局邮件:%+v延迟到%v发送,添加到全局定时器。。", po, delayAt.Format(time.DateTime)) objBin, _ := json.Marshal(po) payload := &event.EventPayload_DelayInvokeCreateHook{ ProjectId: projectId, Resource: resource, DelayAt: delayAt, Obj: objBin, } event.GetMgrInstance().Publish(event.EventTopic_DelayInvokeCreateHook, payload) return }) } } } func (svc *CommonResourceService) List(projectId int, resource string, listParams *dto2.CommonListReq) (int, []*dto2.CommonDtoFieldDesc, []dto2.CommonDtoValues, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return 0, nil, nil, err } if resource != consts.ResourcesName_Project && !find { return 0, nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } rRepo := findCommResourceRepo(resource) totalCount, fieldsDescInfo, etList, err := rRepo.Repo.List(projectEt, listParams) if err != nil { return 0, nil, nil, err } retList := make([]dto2.CommonDtoValues, 0, len(etList)) for _, v := range etList { retList = append(retList, v.ToCommonDto()) } // 执行各个项目特定的钩子方法 if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpListHook); ok { totalCount1, fieldsDescInfo1, retList1, err1 := hook.List(projectEt, resource, listParams, fieldsDescInfo, totalCount, retList) if err1 != nil { xlog.Warnf("list resource %v, hook return error:%v", resource, err1) return totalCount, fieldsDescInfo, retList, nil } return totalCount1, fieldsDescInfo1, retList1, nil } return totalCount, fieldsDescInfo, retList, nil } func (svc *CommonResourceService) GetById(projectId int, resource string, id int) ([]*dto2.CommonDtoFieldDesc, dto2.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).Repo.GetById(projectEt, id) if err != nil { return nil, nil, nil, false, err } return fieldsDescInfo, et.ToCommonDto(), et, find, nil } func (svc *CommonResourceService) Create(projectId int, resource string, dtoObj dto2.CommonDtoValues) (*entity.Project, dto2.CommonDtoValues, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return projectEt, nil, err } if resource != consts.ResourcesName_Project && !find { return projectEt, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } createOne := func(obj dto2.CommonDtoValues) (dto2.CommonDtoValues, error) { resourceRepo := findCommResourceRepo(resource) et, err := resourceRepo.Repo.Create(projectEt, resource, obj) if err != nil { return nil, err } // 返回新的实体,插入数据库之后会填入数据库id,所以要返给前端刷新id数据 // 这里转换一次新的数据传输对象,因为上一步走了创建,会给dto分配id newObj := et.ToCommonDto() // 执行各个项目特定的钩子方法 if resourceRepo.HasDelayInvokeCreateHook { field := reflect.ValueOf(et.Po).Elem().FieldByName("DelayInvokeCreateHook") delayAt := field.Interface().(sql.NullTime) if delayAt.Valid && delayAt.Time.After(time.Now()) { // 延迟执行钩子调用 objBin, _ := json.Marshal(et.Po) payload := &event.EventPayload_DelayInvokeCreateHook{ ProjectId: projectId, Resource: resource, DelayAt: delayAt.Time, Obj: objBin, } event.GetMgrInstance().Publish(event.EventTopic_DelayInvokeCreateHook, payload) return newObj, nil } } if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpCreateHook); ok { err = hook.Create(projectEt, resource, newObj) if err != nil { return nil, err } return newObj, nil } return newObj, nil } var newDtoObj dto2.CommonDtoValues if resource == consts.ResourcesName_SupportAccount { account, findAccount := dtoObj["Account"] if findAccount { for _, ac := range strings.Split(account.(string), ",") { dtoObj["Account"] = ac tmpObj, err := createOne(dtoObj) if err != nil { xlog.Errorf("create support account %v db error:%v", ac, err) err = nil // 忽略多个插入的报错 } else { newDtoObj = tmpObj } } } else { return projectEt, nil, errcode.New(errcode.ParamsInvalid, "account empty:%+v", dtoObj) } } else { newDtoObj, err = createOne(dtoObj) } return projectEt, newDtoObj, err } func (svc *CommonResourceService) Edit(projectId int, resource string, dtoObj dto2.CommonDtoValues) (*entity.Project, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return projectEt, err } if resource != consts.ResourcesName_Project && !find { return projectEt, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } err = findCommResourceRepo(resource).Repo.Edit(projectEt, dtoObj) if err != nil { return projectEt, err } // 执行各个项目特定的钩子方法 if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpEditHook); ok { err = hook.Edit(projectEt, resource, dtoObj) if err != nil { return projectEt, err } return projectEt, nil } return projectEt, nil } func (svc *CommonResourceService) Delete(projectId int, resource string, id int) (*entity.Project, *entity.CommonResource, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return projectEt, nil, err } if resource != consts.ResourcesName_Project && !find { return projectEt, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } oldEt, find, err := findCommResourceRepo(resource).Repo.Delete(projectEt, id) if err != nil { return projectEt, nil, err } // 执行各个项目特定的钩子方法 if find { if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpDeleteHook); ok { err = hook.Delete(projectEt, resource, oldEt.ToCommonDto()) if err != nil { return projectEt, oldEt, err } return projectEt, oldEt, nil } } return projectEt, oldEt, nil } func (svc *CommonResourceService) RowsSelection(projectId int, resourceName string, params *dto2.CommonRowsSelectionReq) (*entity.Project, *dto2.CommonRowsSelectionRsp, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return projectEt, nil, err } if resourceName != consts.ResourcesName_Project && !find { return projectEt, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } rRepo := findCommResourceRepo(resourceName) btnInfo, find := rRepo.findRowsSelectionBtn(params.BtnKey) if !find { return projectEt, nil, errcode.New(errcode.ServerError, "not found project %v resource %v rows section btn:%v", projectId, resourceName, params.BtnKey) } rsp, err := btnInfo.RowsSelectionHandler(projectEt, resourceName, params) return projectEt, rsp, err } func (svc *CommonResourceService) GetResourceKeyByDto(resource string, dtoObj []dto2.CommonDtoValues) (string, string) { resourceInfo := findCommResourceRepo(resource) desc := resourceInfo.Desc etList := resourceInfo.Repo.MakeEntitiesByDtoList(dtoObj) keyList := make([]string, 0, len(dtoObj)) for _, et := range etList { po := et.Po.(model.IModel) method := reflect.ValueOf(po).MethodByName("GetShowKey") var key string if !method.IsValid() { key = strconv.Itoa(po.GetId()) } else { rets := method.Call([]reflect.Value{}) key = rets[0].Interface().(string) } keyList = append(keyList, key) } if len(keyList) > 1 { return desc, "[" + strings.Join(keyList, ",") + "]" } return desc, strings.Join(keyList, ",") } func (svc *CommonResourceService) GetResourceSpecBtnKey(resource string, btnKey string) *ResourceBtnInfo { resourceInfo := findCommResourceRepo(resource) for _, v := range resourceInfo.GlobalBtns { if v.Key == btnKey { return v } } for _, v := range resourceInfo.RowBtns { if v.Key == btnKey { return v } } return nil } func (svc *CommonResourceService) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo { list := make([]*api.ResourceInitInfo, 0, len(commResourcesRepo)) for _, v := range commResourcesRepo { initInfo := &api.ResourceInitInfo{ Resource: v.Resource, Desc: v.Desc, ShowMethods: v.ShowMethods, } for _, v := range v.GlobalBtns { initInfo.GlobalBtns = append(initInfo.GlobalBtns, v.ResourceBtnInfo) } for _, v := range v.RowBtns { initInfo.RowBtns = append(initInfo.RowBtns, v.ResourceBtnInfo) } list = append(list, initInfo) } return list } const ( ShowMethod_Get = 1 ShowMethod_Post = 1 << 1 ShowMethod_Put = 1 << 2 ShowMethod_Delete = 1 << 3 ) type ResourceBtnInfo struct { *api.ResourceBtnInfo RowsSelectionHandler func(projectEt *entity.Project, resourceName string, params *dto2.CommonRowsSelectionReq) (*dto2.CommonRowsSelectionRsp, error) } type resourceRepoInfo struct { Resource string Desc string Repo repo.ICommonResourceRepo ShowMethods []string showMethods int GlobalBtns []*ResourceBtnInfo RowBtns []*ResourceBtnInfo HasDelayInvokeCreateHook bool } func (repoInfo *resourceRepoInfo) findRowsSelectionBtn(clickedBtn string) (*ResourceBtnInfo, bool) { for _, btn := range repoInfo.GlobalBtns { if btn.Key == clickedBtn { return btn, true } } for _, btn := range repoInfo.RowBtns { if btn.Key == clickedBtn { return btn, true } } return nil, false } var commResourcesRepo = make([]*resourceRepoInfo, 0) func r(resource, desc string, repo repo.ICommonResourceRepo, showMethods int) *resourceRepoInfo { curRepo := &resourceRepoInfo{ Resource: resource, Desc: desc, Repo: repo, ShowMethods: make([]string, 0), } 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) return curRepo } func findCommResourceRepo(resource string) *resourceRepoInfo { for _, v := range commResourcesRepo { if v.Resource == resource { return v } } panic(fmt.Errorf("not found Resource:%v", resource)) return nil }