uniugm/admin/apps/game/domain/comm_resource.go
2025-06-06 18:30:12 +08:00

445 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 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 = []*api.ResourceBtnInfo{
{Key: consts.BtnKeyGlobal_Server_DownAll, Name: "一键停服", BtnColorType: "info"},
{Key: consts.BtnKeyGlobal_Server_UpAll, Name: "一键起服", BtnColorType: "warning"},
{Key: consts.BtnKeyGlobal_Server_ExportCdn, Name: "一键导出cdn", BtnColorType: "danger"},
{Key: consts.BtnKeyGlobal_Server_ExportCdn, Name: "预览导出cdn", BtnColorType: "default"},
}
serverRepo.RowBtns = []*api.ResourceBtnInfo{
{Key: consts.BtnKeyRow_Server_Down, Name: "停服", BtnColorType: "info"},
{Key: consts.BtnKeyRow_Server_Up, Name: "起服", BtnColorType: "warning"},
}
}
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)
{
noticeRepo := r(consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
noticeRepo.GlobalBtns = []*api.ResourceBtnInfo{
{Key: consts.BtnKeyGlobal_Server_DownAll, Name: "一键禁用所有", BtnColorType: "info"},
}
noticeRepo.RowBtns = []*api.ResourceBtnInfo{
{Key: consts.BtnKeyRow_Server_Down, Name: "禁用", BtnColorType: "info"},
{Key: consts.BtnKeyRow_Server_Up, Name: "启用", BtnColorType: "warning"},
}
}
r(consts.ResourcesName_DevicePush, "设备推送(暂无)", repo.NewCommonResourceRepo(db, &model.DevicePush{}), ShowMethod_Get)
}
func NewCommonResourceService(db *gorm.DB) *CommonResourceService {
initCommonResourcesRepo(db)
svc := &CommonResourceService{
projectRepo: repo.NewProjectRepo(db),
serverRepo: repo.NewServerRepo(db),
noticeRepo: repo.NewNoticeRepo(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)
}
if params.BtnKey == consts.BtnKeyGlobal_Server_ExportCdn {
// 导出cdn聚合公告信息一起导
serverList, err := svc.serverRepo.List(projectId)
if err != nil {
return projectEt, nil, err
}
noticeList, err := svc.noticeRepo.List(projectId)
if err != nil {
return projectEt, nil, err
}
content, err := genCdnServerListContent(serverList, noticeList)
if err != nil {
return projectEt, nil, err
}
return projectEt, &dto2.CommonRowsSelectionRsp{
Msg: content,
NeedRefresh: false,
FileName: projectEt.GetProjectPo().Name + "-区服公告信息.txt",
}, nil
} else {
if hook, ok := projects.GetProjectResourceHook(projectEt, resourceName).(projects.IPostResourceOpRowsHook); ok {
rsp, err := hook.RowsSelection(projectEt, resourceName, params.BtnKey, params.Rows)
return projectEt, rsp, err
}
}
return projectEt, nil, nil
}
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) *api.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 {
list = append(list, &api.ResourceInitInfo{
Resource: v.Resource,
Desc: v.Desc,
ShowMethods: v.ShowMethods,
GlobalBtns: v.GlobalBtns,
RowBtns: v.RowBtns,
})
}
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
GlobalBtns []*api.ResourceBtnInfo
RowBtns []*api.ResourceBtnInfo
HasDelayInvokeCreateHook bool
}
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
}