diff --git a/admin/apps/game/domain/comm_resource.go b/admin/apps/game/domain/comm_resource.go index 3a333e6..2c430a5 100644 --- a/admin/apps/game/domain/comm_resource.go +++ b/admin/apps/game/domain/comm_resource.go @@ -39,7 +39,7 @@ func NewCommonResourceService(db *gorm.DB) *CommonResourceService { return svc } -func (svc *CommonResourceService) List(projectId int, resource string, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { +func (svc *CommonResourceService) List(projectId int, resource string, listParams *dto.CommonListReq) ([]*dto.CommonDtoFieldDesc, []dto.CommonDtoValues, error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return nil, nil, err @@ -48,7 +48,8 @@ func (svc *CommonResourceService) List(projectId int, resource string, pageNo, p return nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } - fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectEt, pageNo, pageLen, extraQuery, args...) + fieldsDescInfo, etList, err := findCommResourceRepo(resource).List(projectEt, + listParams.PageNo, listParams.PageLen, listParams.ParsedWhereConditions.Conditions) if err != nil { return nil, nil, err } @@ -59,7 +60,7 @@ func (svc *CommonResourceService) List(projectId int, resource string, pageNo, p // 执行各个项目特定的钩子方法 if hook, ok := projects.GetProjectResourceHook(projectEt, resource).(projects.IPostResourceOpListHook); ok { - return hook.List(projectEt, resource, pageNo, pageLen, fieldsDescInfo, retList, extraQuery, args...) + return hook.List(projectEt, resource, listParams.PageNo, listParams.PageLen, fieldsDescInfo, retList, "") } return fieldsDescInfo, retList, nil diff --git a/admin/apps/game/domain/entity/utils.go b/admin/apps/game/domain/entity/utils.go index 004f5c0..ea3e3e0 100644 --- a/admin/apps/game/domain/entity/utils.go +++ b/admin/apps/game/domain/entity/utils.go @@ -52,6 +52,7 @@ func getFieldTypeDtoDescInfo(project *Project, poValue reflect.Value, fieldType Choices: make([]*dto.CommonDtoFieldChoice, 0), MultiChoice: fieldType.Tag.Get("multi_choice") == "true", Uneditable: fieldType.Tag.Get("uneditable") == "true", + Where: fieldType.Tag.Get("where"), } if f1.Key == "CreatedAt" { diff --git a/admin/apps/game/domain/project.go b/admin/apps/game/domain/project.go index d5e2e8f..7132f07 100644 --- a/admin/apps/game/domain/project.go +++ b/admin/apps/game/domain/project.go @@ -2,8 +2,10 @@ package domain import ( "admin/apps/game/domain/entity" + "admin/apps/game/domain/projects" "admin/apps/game/domain/repo" "admin/apps/game/model/dto" + "admin/internal/errcode" "gorm.io/gorm" ) @@ -68,3 +70,20 @@ func (svc *ProjectService) GetProjectInvokeApiAddr(projectId int, serverIds []in } return []string{et.Po.ApiAddr}, nil } + +func (svc *ProjectService) GetAllItems(projectId int) ([]*dto.CommonDtoFieldChoice, error) { + _, projectEt, find, err := svc.repo.GetById(projectId) + if err != nil { + return nil, err + } + if !find { + return nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) + } + + handler := projects.GetProjectValueChoicesGetHook(projectEt.Po.ProjectType) + if handler == nil { + return nil, errcode.New(errcode.ServerError, "not found project %v items handler", projectEt.Po.ProjectType) + } + + return handler.GetItems(projectEt) +} diff --git a/admin/apps/game/domain/projects/projects.go b/admin/apps/game/domain/projects/projects.go index 1749158..adc22c5 100644 --- a/admin/apps/game/domain/projects/projects.go +++ b/admin/apps/game/domain/projects/projects.go @@ -25,6 +25,7 @@ var projectsValueChoicesGetHook = map[string]IGetAllValueChoicesHook{ } func GetProjectResourceHook(project *entity.Project, resource string) any { + return nil projectResourceHooks, find := projectsResourceHookMgr[project.Po.ProjectType] if !find { return nil diff --git a/admin/apps/game/domain/projects/smdl/items.go b/admin/apps/game/domain/projects/smdl/items.go index 51b3b66..a8e4cdb 100644 --- a/admin/apps/game/domain/projects/smdl/items.go +++ b/admin/apps/game/domain/projects/smdl/items.go @@ -11,6 +11,12 @@ type Items struct { } func (items *Items) GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error) { + return []*dto.CommonDtoFieldChoice{ + {Desc: "黄金战甲", Value: 123, Type: 1}, + {Desc: "黄金头盔", Value: 234, Type: 1}, + {Desc: "黄金大刀", Value: 346, Type: 1}, + {Desc: "白银大刀", Value: 346, Type: 1}, + }, nil alisrvAddr := projectInfo.GetApiAddr() if alisrvAddr == "" { return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) diff --git a/admin/apps/game/domain/repo/comm_resource.go b/admin/apps/game/domain/repo/comm_resource.go index 3ddb3b3..c0c80ac 100644 --- a/admin/apps/game/domain/repo/comm_resource.go +++ b/admin/apps/game/domain/repo/comm_resource.go @@ -5,13 +5,18 @@ import ( "admin/apps/game/model" "admin/apps/game/model/dto" "admin/internal/errcode" + "admin/lib/xlog" "errors" + "fmt" "gorm.io/gorm" + "gorm.io/gorm/schema" "reflect" + "strings" + "time" ) type ICommonResourceRepo interface { - List(project *entity.Project, pageNo, pageLen int, extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) + List(project *entity.Project, pageNo, pageLen int, whereConditions []*dto.GetWhereCondition) ([]*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 @@ -34,15 +39,28 @@ func newCommonResourceRepoImpl(db *gorm.DB, poTemplate model.IModel) *commonReso } func (repo *commonResourceRepoImpl) List(projectEt *entity.Project, pageNo, pageLen int, - extraQuery string, args ...any) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { + whereConditions []*dto.GetWhereCondition) ([]*dto.CommonDtoFieldDesc, []*entity.CommonResource, error) { + + if pageNo < 0 || pageLen <= 0 { + return nil, nil, errcode.New(errcode.ParamsInvalid, "page no or page len invalid:%v,%v", pageNo, pageLen) + } + + limitStart := pageNo * pageLen + limitLen := pageLen listType := reflect.New(reflect.SliceOf(reflect.TypeOf(repo.poTemplate))) + + var tx *gorm.DB var err error - if extraQuery == "" { - err = repo.db.Find(listType.Interface()).Error + if len(whereConditions) <= 0 { + tx = repo.db.Offset(limitStart).Limit(limitLen) } else { - err = repo.db.Where(extraQuery, args...).Find(listType.Interface()).Error + whereSql, whereArgs := repo.parseWhereConditions2Sql(whereConditions) + xlog.Debugf("list resource %v where sql:%v, args:%+v", + repo.poTemplate.TableName(), whereSql, whereArgs) + tx = repo.db.Where(whereSql, whereArgs...).Offset(limitStart).Limit(limitLen) } + err = tx.Find(listType.Interface()).Error if err != nil { return nil, nil, errcode.New(errcode.DBError, "list resource %v error:%v", repo.poTemplate.TableName(), err) } @@ -110,3 +128,52 @@ func (repo *commonResourceRepoImpl) Delete(projectEt *entity.Project, id int) (* func (repo *commonResourceRepoImpl) newEmptyPo() model.IModel { return reflect.New(reflect.TypeOf(repo.poTemplate).Elem()).Interface().(model.IModel) } + +func (repo *commonResourceRepoImpl) parseWhereConditions2Sql(conditions []*dto.GetWhereCondition) (whereSql string, args []any) { + namer := new(schema.NamingStrategy) + to := reflect.TypeOf(repo.poTemplate).Elem() + whereClause := make([]string, 0, len(conditions)) + whereArgs := make([]interface{}, 0, len(conditions)) + for _, cond := range conditions { + for i := 0; i < to.NumField(); i++ { + field := to.Field(i) + if field.Name != cond.Key { + continue + } + dbFieldName := namer.ColumnName("", field.Name) + if field.Type.Name() == "Time" { + cond.Value1, _ = time.ParseInLocation("2006/01/02 15:04:05", cond.Value1.(string), time.Local) + cond.Value2, _ = time.ParseInLocation("2006/01/02 15:04:05", cond.Value2.(string), time.Local) + } + switch field.Tag.Get("where") { + case "eq": + whereClause = append(whereClause, fmt.Sprintf("`%v` = ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "gt": + whereClause = append(whereClause, fmt.Sprintf("`%v` > ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "lt": + whereClause = append(whereClause, fmt.Sprintf("`%v` < ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "le": + whereClause = append(whereClause, fmt.Sprintf("`%v` <= ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "ge": + whereClause = append(whereClause, fmt.Sprintf("`%v` >= ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "like": + whereClause = append(whereClause, fmt.Sprintf("`%v` like ?", dbFieldName)) + whereArgs = append(whereArgs, cond.Value1) + case "range": + whereClause = append(whereClause, fmt.Sprintf("`%v` >= ? and `%v` <= ?", dbFieldName, dbFieldName)) + whereArgs = append(whereArgs, cond.Value1, cond.Value2) + case "": + default: + panic(fmt.Errorf("unsupport where tag %v", field.Tag)) + } + + } + } + whereSql = strings.Join(whereClause, " AND ") + return whereSql, whereArgs +} diff --git a/admin/apps/game/model/account.go b/admin/apps/game/model/account.go index 9aab85a..b34312e 100644 --- a/admin/apps/game/model/account.go +++ b/admin/apps/game/model/account.go @@ -12,8 +12,8 @@ func init() { // Account 空表,就是用来兼容资源增删改查公共操作的,实际列举账号等都是走各个项目api拉取 type Account struct { ProjectId int - Account string `name:"账号" json:"account"` - ServerConfId string `name:"区服id" json:"serveId"` + Account string `name:"账号" json:"account" where:"eq"` + ServerConfId string `name:"区服id" json:"serveId" where:"eq"` RoleIds []string `gorm:"type:json;serializer:json" name:"角色id列表" json:"roleIds"` LatestLoginTime time.Time `name:"最近登录时间" json:"latest_login_time"` CreateTime time.Time `name:"创建时间" json:"create_time"` diff --git a/admin/apps/game/model/dto/common.go b/admin/apps/game/model/dto/common.go index 4b68a30..088f177 100644 --- a/admin/apps/game/model/dto/common.go +++ b/admin/apps/game/model/dto/common.go @@ -25,6 +25,7 @@ type CommonDtoFieldDesc struct { Choices []*CommonDtoFieldChoice `json:"choices"` // 可选项,用于字段做下拉框 MultiChoice bool `json:"multi_choice"` // 是否多选 Uneditable bool `json:"uneditable"` // 不可编辑,某些数据一旦新增之后不能修改,例如封禁的值、服务器的id等 + Where string `json:"where"` // sql list的where条件,用于表格页面查询条件编写,值:eq gt lt ge lt range like } //type CommonDtoValue struct { @@ -43,3 +44,10 @@ type PathInfo struct { Path string `json:"path"` Method string `json:"method"` } + +type GetWhereCondition struct { + Key string `json:"key"` + Op string `json:"op"` // eq,gt,lt,range + Value1 any `json:"value1"` + Value2 any `json:"value2"` +} diff --git a/admin/apps/game/model/dto/msg.go b/admin/apps/game/model/dto/msg.go index 8868fdf..dd8bc54 100644 --- a/admin/apps/game/model/dto/msg.go +++ b/admin/apps/game/model/dto/msg.go @@ -5,12 +5,16 @@ type NilReq struct { type NilRsp = NilReq +type ListWhereConditionInfo struct { + Conditions []*GetWhereCondition `json:"conditions"` +} + 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"` + PageNo int `json:"page_no"` + PageLen int `json:"page_len"` + //WhereConditions []*GetWhereCondition `json:"where_conditions"` + WhereConditions string `json:"where_conditions"` // json序列化数据,内容是{"conditions": []*GetWhereCondition} + ParsedWhereConditions *ListWhereConditionInfo `json:"-"` } type CommonPostReq struct { @@ -46,3 +50,7 @@ type CommandListReq struct { type CommandListRsp struct { List []*PathInfo `json:"list"` } + +type GetProjectAllItemsRsp struct { + Items []*CommonDtoFieldChoice `json:"items"` +} diff --git a/admin/apps/game/model/globalmail.go b/admin/apps/game/model/globalmail.go index 50e92da..cdc05d2 100644 --- a/admin/apps/game/model/globalmail.go +++ b/admin/apps/game/model/globalmail.go @@ -10,17 +10,15 @@ func init() { db.RegisterTableModels(GlobalMail{}) } -var ProjectsAllItems = make(map[string][]*dto.CommonDtoFieldChoice) - type GlobalMail struct { ID int `gorm:"primarykey" readonly:"true"` 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"` - Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"` + Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" desc:"搜索道具并点击添加"` - CreatedAt time.Time `readonly:"true"` + CreatedAt time.Time `readonly:"true" where:"range"` } func (lm *GlobalMail) TableName() string { @@ -34,7 +32,3 @@ func (m *GlobalMail) GetId() int { func (m *GlobalMail) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { return getChoiceServers(project) } - -func (m *GlobalMail) GetChoiceItems(project *Project) []*dto.CommonDtoFieldChoice { - return ProjectsAllItems[project.ProjectType] -} diff --git a/admin/apps/game/model/role.go b/admin/apps/game/model/role.go index a303ea1..156c54c 100644 --- a/admin/apps/game/model/role.go +++ b/admin/apps/game/model/role.go @@ -12,10 +12,10 @@ 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"` - Name string `name:"名称" json:"roleName"` + RoleId string `name:"角色ID" json:"roleId" where:"eq"` + Account string `name:"账号" json:"account" where:"eq"` + ServerConfId string `name:"区服id" json:"serverId" where:"eq"` + Name string `name:"名称" json:"roleName" where:"eq"` Status string `name:"状态" desc:"离线|在线" json:"status"` Level int `name:"等级" json:"roleLevel"` Profession string `name:"职业" json:"profession"` diff --git a/admin/apps/game/model/rolemail.go b/admin/apps/game/model/rolemail.go index c890145..3a6ae88 100644 --- a/admin/apps/game/model/rolemail.go +++ b/admin/apps/game/model/rolemail.go @@ -20,12 +20,12 @@ type RoleMail struct { ID int `gorm:"primarykey" readonly:"true"` ProjectId int RoleIDs []string `gorm:"type:json;serializer:json" name:"生效的角色id" desc:"生效的角色id,逗号分隔多个" required:"true"` - ServerID string `name:"所属区服" choices:"GetChoiceServers" required:"true"` + ServerID string `name:"所属区服" choices:"GetChoiceServers" required:"true" where:"eq"` Title string `name:"邮件标题" required:"true"` Content string `name:"邮件内容" required:"true"` - Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"` + Attach []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" desc:"搜索道具并点击添加"` - CreatedAt time.Time `readonly:"true"` + CreatedAt time.Time `readonly:"true" where:"range"` } func (lm *RoleMail) TableName() string { @@ -39,7 +39,3 @@ func (m *RoleMail) GetId() int { func (m *RoleMail) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice { return getChoiceServers(project) } - -func (m *RoleMail) GetChoiceItems(project *Project) []*dto.CommonDtoFieldChoice { - return ProjectsAllItems[project.ProjectType] -} diff --git a/admin/apps/game/model/whitelist.go b/admin/apps/game/model/whitelist.go index d60c851..27d8fd9 100644 --- a/admin/apps/game/model/whitelist.go +++ b/admin/apps/game/model/whitelist.go @@ -13,11 +13,11 @@ func init() { type WhiteList struct { ID int `gorm:"primarykey" readonly:"true"` ProjectId int `gorm:"uniqueIndex:idx_whitelist"` - ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers"` + ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"` Account string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"` Desc string `name:"描述"` - CreatedAt time.Time `readonly:"true"` + CreatedAt time.Time `readonly:"true" where:"range"` } func (lm *WhiteList) TableName() string { diff --git a/admin/apps/game/server/ctl_common_rest.go b/admin/apps/game/server/ctl_common_rest.go index 3ba4b8e..f3185a4 100644 --- a/admin/apps/game/server/ctl_common_rest.go +++ b/admin/apps/game/server/ctl_common_rest.go @@ -3,6 +3,7 @@ package server import ( "admin/apps/game/model/dto" "admin/internal/context" + stdContext "context" ) func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error { @@ -17,7 +18,11 @@ func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonLis func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPostReq, rsp *dto.CommonPostRsp) error { projectId, resource := getCtxURIProjectIdAndResource(ctx) - newObj, err := ctl.svc.CommonPost(ctx, projectId, resource, *params.Dto) + + ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId) + ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserId) + + newObj, err := ctl.svc.CommonPost(ctx1, projectId, resource, *params.Dto) if err != nil { return err } @@ -47,3 +52,13 @@ func (ctl *controller) CommonDelete(ctx *context.WebContext, params *dto.CommonD func (ctl *controller) OnClickCustomButton(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error { return nil } + +func (ctl *controller) GetProjectAllItems(ctx *context.WebContext, params *dto.NilReq, rsp *dto.GetProjectAllItemsRsp) error { + projectId := getCtxURIProjectId(ctx) + items, err := ctl.svc.GetAllItems(projectId) + if err != nil { + return err + } + rsp.Items = items + return nil +} diff --git a/admin/apps/game/server/middleware.go b/admin/apps/game/server/middleware.go index 1e06e27..7a14c6c 100644 --- a/admin/apps/game/server/middleware.go +++ b/admin/apps/game/server/middleware.go @@ -23,6 +23,8 @@ func (srv *Server) CheckToken(ctx *context.WebContext) { return } + ctx.Header.UserName = authRsp.User.NickName + ctx.GinCtx().Set("userInfo", authRsp) return } diff --git a/admin/apps/game/server/route.go b/admin/apps/game/server/route.go index b379aae..3096018 100644 --- a/admin/apps/game/server/route.go +++ b/admin/apps/game/server/route.go @@ -6,6 +6,8 @@ import ( ) func (srv *Server) Route(engine *web.Engine) { + engine.Use(srv.CheckToken) + apiGroup := engine.Group("/api", "") { @@ -24,5 +26,7 @@ func (srv *Server) Route(engine *web.Engine) { resourceUnderProjectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) } + + projectGroup.Get("/:projectId/items", "获取项目所有道具列表", consts.WebPathPermit_Read, srv.ctl.GetProjectAllItems) } } diff --git a/admin/apps/game/service/service.go b/admin/apps/game/service/service.go index 4a42237..39f60e4 100644 --- a/admin/apps/game/service/service.go +++ b/admin/apps/game/service/service.go @@ -5,7 +5,10 @@ import ( "admin/apps/game/domain" "admin/apps/game/model/dto" "admin/internal/consts" + "admin/internal/errcode" + "admin/internal/event" "context" + "encoding/json" "gorm.io/gorm" ) @@ -30,17 +33,26 @@ func New(db *gorm.DB) (*Service, error) { } func (svc *Service) CommonList(ctx context.Context, projectId int, resourceName string, params *dto.CommonListReq) (*dto.CommonDtoList, error) { - var ( - query string - args []any - ) + + params.ParsedWhereConditions = &dto.ListWhereConditionInfo{} + if params.WhereConditions != "" { + err := json.Unmarshal([]byte(params.WhereConditions), params.ParsedWhereConditions) + if err != nil { + return nil, errcode.New(errcode.ParamsInvalid, "unmarshal list condition:%v error:%v", + params.WhereConditions, err) + } + } + switch resourceName { case consts.ResourcesName_Project: default: - query = "project_id = ?" - args = append(args, projectId) + params.ParsedWhereConditions.Conditions = append([]*dto.GetWhereCondition{&dto.GetWhereCondition{ + Key: "ProjectId", + Op: "eq", + Value1: projectId, + }}, params.ParsedWhereConditions.Conditions...) } - fieldsDescInfo, rows, err := svc.resourceSvc.List(projectId, resourceName, params.PageNo, params.PageLen, query, args) + fieldsDescInfo, rows, err := svc.resourceSvc.List(projectId, resourceName, params) return &dto.CommonDtoList{FieldsDesc: fieldsDescInfo, Rows: rows}, err } @@ -48,18 +60,61 @@ func (svc *Service) CommonPost(ctx context.Context, projectId int, resourceName if resourceName != consts.ResourcesName_Project { params["ProjectId"] = projectId } - return svc.resourceSvc.Create(projectId, resourceName, params) + + values, err := svc.resourceSvc.Create(projectId, resourceName, params) + + userId := ctx.Value("user_id").(int) + userName := ctx.Value("user_name").(string) + + event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{ + UserId: userId, + UserName: userName, + ProjectId: projectId, + Resource: resourceName, + Method: "新增", + NewData: params, + }) + + return values, err } 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) + err := svc.resourceSvc.Edit(projectId, resourceName, params) + + userId := ctx.Value("user_id").(int) + userName := ctx.Value("user_name").(string) + + event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{ + UserId: userId, + UserName: userName, + ProjectId: projectId, + Resource: resourceName, + Method: "编辑", + NewData: params, + }) + + return err } func (svc *Service) CommonDelete(ctx context.Context, projectId int, resourceName string, id int) error { - return svc.resourceSvc.Delete(projectId, resourceName, id) + err := svc.resourceSvc.Delete(projectId, resourceName, id) + + userId := ctx.Value("user_id").(int) + userName := ctx.Value("user_name").(string) + + event.GetMgrInstance().Publish(event.EventTopic_UserExecute, &event.EventPayload_UserExecute{ + UserId: userId, + UserName: userName, + ProjectId: projectId, + Resource: resourceName, + Method: "删除", + NewData: id, + }) + + return err } func (svc *Service) GetSupportResourcesList(permissions []string) []*api.ResourceInitInfo { diff --git a/admin/apps/game/service/service_project.go b/admin/apps/game/service/service_project.go index a4cfa85..c1ff756 100644 --- a/admin/apps/game/service/service_project.go +++ b/admin/apps/game/service/service_project.go @@ -3,6 +3,7 @@ package service import ( "admin/apps/game/api" "admin/apps/game/domain/entity" + "admin/apps/game/model/dto" "admin/internal/consts" "admin/internal/permission" ) @@ -66,3 +67,7 @@ func (svc *Service) GetProjectList() ([]*entity.Project, error) { func (svc *Service) GetProjectInvokeApiAddr(projectId int, serverIds []int) ([]string, error) { return svc.projectSvc.GetProjectInvokeApiAddr(projectId, serverIds) } + +func (svc *Service) GetAllItems(projectId int) ([]*dto.CommonDtoFieldChoice, error) { + return svc.projectSvc.GetAllItems(projectId) +} diff --git a/admin/apps/user/api/api_user.go b/admin/apps/user/api/api_user.go index fc16cf9..46b1556 100644 --- a/admin/apps/user/api/api_user.go +++ b/admin/apps/user/api/api_user.go @@ -24,6 +24,7 @@ type AuthReq struct { type UserInfo struct { UserId int `json:"user_id"` + UserName string `json:"user_name"` NickName string `json:"nick_name"` Icon string `json:"icon"` Character string `json:"character"` diff --git a/admin/apps/user/domain/repo/user.go b/admin/apps/user/domain/repo/user.go index f3b0c8a..3f3bfa5 100644 --- a/admin/apps/user/domain/repo/user.go +++ b/admin/apps/user/domain/repo/user.go @@ -38,14 +38,22 @@ func (impl *userRepoImpl) CreateAdminUsers() error { } if err := impl.db.Create(adminCharacter).Error; err != nil { - if !strings.Contains(err.Error(), "Duplicate entry") { + if strings.Contains(err.Error(), "Duplicate entry") { + + } else if strings.Contains(err.Error(), "UNIQUE constraint") { + + } else { 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) + if strings.Contains(err.Error(), "Duplicate entry") { + + } else if strings.Contains(err.Error(), "UNIQUE constraint") { + + } else { + return errcode.New(errcode.DBError, "create admin character fail:%v", err) } } diff --git a/admin/apps/user/model/history.go b/admin/apps/user/model/history.go new file mode 100644 index 0000000..6436e4a --- /dev/null +++ b/admin/apps/user/model/history.go @@ -0,0 +1,35 @@ +package model + +import ( + "admin/internal/db" + "admin/internal/global" + "time" +) + +func init() { + db.RegisterTableModels(History{}) +} + +// History 用户执行历史 +type History struct { + ID int `gorm:"primarykey" readonly:"true"` + UserId int + UserName string + ProjectId int + Resource string + Method string + Data string `gorm:"type:longtext"` + CreateTime time.Time +} + +func (m *History) TableName() string { + return "history" +} + +func (m *History) GetId() int { + return m.ID +} + +func (m *History) Create() error { + return global.GLOB_DB.Create(m).Error +} diff --git a/admin/apps/user/server/server.go b/admin/apps/user/server/server.go index 5280a6f..2adad5c 100644 --- a/admin/apps/user/server/server.go +++ b/admin/apps/user/server/server.go @@ -1,6 +1,12 @@ package server -import "admin/apps/user/service" +import ( + "admin/apps/user/model" + "admin/apps/user/service" + "admin/internal/event" + "admin/lib/xlog" + "encoding/json" +) type Server struct { svc *service.Service @@ -13,3 +19,28 @@ func New(svc *service.Service) *Server { ctl: newController(svc), } } + +func (srv *Server) jobsSubscribe() { + event.GetMgrInstance().Subscribe("user", event.EventTopic_UserExecute, srv.subscriberHandlerUserExecute) +} + +func (srv *Server) subscriberHandlerUserExecute(msg *event.Msg) { + po := new(model.History) + msgHistory := &event.EventPayload_UserExecute{} + err := json.Unmarshal(msg.Payload, msgHistory) + if err != nil { + xlog.Errorf("unmarshal msg(%+v) err:%v", string(msg.Payload), err) + } + + po.UserId = msgHistory.UserId + po.UserName = msgHistory.UserName + po.ProjectId = msgHistory.ProjectId + po.Resource = msgHistory.Resource + po.Method = msgHistory.Method + po.Data = string(msg.Payload) + + err = po.Create() + if err != nil { + xlog.Errorf("create user execute(%+v) err:%v", string(msg.Payload), err) + } +} diff --git a/admin/apps/user/service/service_user.go b/admin/apps/user/service/service_user.go index 80bfd80..2b194b9 100644 --- a/admin/apps/user/service/service_user.go +++ b/admin/apps/user/service/service_user.go @@ -40,6 +40,7 @@ func (svc *Service) Auth(ctx context.Context, req *apiUser.AuthReq) (*apiUser.Au } rsp.User = &apiUser.UserInfo{ UserId: info.User.UserId, + UserName: info.User.NickName, NickName: info.User.NickName, Icon: info.User.Icon, Character: info.User.Character, diff --git a/admin/cmd/all_in_one/all_in_one.exe b/admin/cmd/all_in_one/all_in_one.exe new file mode 100644 index 0000000..43b8d35 Binary files /dev/null and b/admin/cmd/all_in_one/all_in_one.exe differ diff --git a/admin/go.mod b/admin/go.mod index cd296ee..d37e64b 100644 --- a/admin/go.mod +++ b/admin/go.mod @@ -9,6 +9,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.2 github.com/prometheus/client_golang v1.22.0 github.com/rs/zerolog v1.34.0 + golang.org/x/crypto v0.37.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 gorm.io/driver/sqlite v1.5.7 @@ -46,7 +47,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.16.0 // indirect - golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect diff --git a/admin/internal/config/flags.go b/admin/internal/config/flags.go index a8b5796..387d39a 100644 --- a/admin/internal/config/flags.go +++ b/admin/internal/config/flags.go @@ -2,7 +2,7 @@ package config type CommonBootFlags struct { ApiPort string `env:"api_port" default:"8080" desc:"api端口,客户端请求的地址端口"` - DBType string `env:"db_type" default:"sqlite3" desc:"数据库类型,默认sqlite,可选:sqlite|mysql|pg"` + DBType string `env:"db_type" default:"sqlite" desc:"数据库类型,默认sqlite,可选:sqlite|mysql|pg"` DBAddr string `env:"db_addr" default:"localhost" desc:"数据库地址"` DBName string `env:"db_name" default:"uniugm" desc:"数据库名字"` DBUser string `env:"db_user" default:"root" desc:"数据库用户名"` diff --git a/admin/internal/context/ctx_web.go b/admin/internal/context/ctx_web.go index 6970e54..8bf413f 100644 --- a/admin/internal/context/ctx_web.go +++ b/admin/internal/context/ctx_web.go @@ -9,9 +9,10 @@ import ( ) type WebHeader struct { - UserId int `json:"UserId"` // 用户id,会与下面token解析出用户id做匹配校验 - Token string `json:"Token"` // jwt token,内置一些信息 - Ip string `json:"IP"` + UserId int `json:"UserId"` // 用户id,会与下面token解析出用户id做匹配校验 + Token string `json:"Token"` // jwt token,内置一些信息 + Ip string `json:"IP"` + UserName string `json:"-"` } type WebContext struct { diff --git a/admin/internal/db/db.go b/admin/internal/db/db.go index 2c6e94f..0597be7 100644 --- a/admin/internal/db/db.go +++ b/admin/internal/db/db.go @@ -9,6 +9,7 @@ import ( "gorm.io/driver/mysql" "gorm.io/driver/sqlite" "gorm.io/gorm" + "strings" "sync" "time" ) @@ -25,39 +26,54 @@ func RegisterTableModels(models ...any) { } func NewDB(dbType, dbAddr, dbName, dbUser, dbPass string) (db *gorm.DB, err error) { - switch dbType { - case "sqlite": - db, err = gorm.Open(sqlite.Open(dbName+".db"), &gorm.Config{}) - if err != nil { - return nil, err - } - case "mysql": - dsn := fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbAddr, dbName) - dsnWithoutDB := fmt.Sprintf("%v:%v@tcp(%v)/?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbAddr) - db, err = createDBAndGuaranteeMigrate(dsnWithoutDB, dsn, globalTables) + dsn := fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbAddr, dbName) + dsnWithoutDB := fmt.Sprintf("%v:%v@tcp(%v)/?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPass, dbAddr) + db, err = createDBAndGuaranteeMigrate(dbType, dsnWithoutDB, dsn, globalTables) + if err != nil { + return nil, err } global.GLOB_DB = db return db, nil } -func createDBAndGuaranteeMigrate(dsnWithoutDb, dsn string, tables []any) (*gorm.DB, error) { +func createDBAndGuaranteeMigrate(dbType string, dsnWithoutDb, dsn string, tables []any) (*gorm.DB, error) { mysqlDriverConf, err := mysqlDriver.ParseDSN(dsn) if err != nil { return nil, fmt.Errorf("parse dsn:%v error:%v", dsn, err) } dbName := mysqlDriverConf.DBName - _, err = tryCreateDB(dbName, dsnWithoutDb) + + var dialector gorm.Dialector + switch dbType { + case "sqlite": + dialector = sqlite.Open(dbName) + case "mysql": + dialector = mysql.Open(dsnWithoutDb) + default: + panic(fmt.Errorf("unsupported db type: %v", dbType)) + } + + _, err = tryCreateDB(dbType, dialector, dbName) if err != nil { xlog.Fatalf(err) return nil, err } - driverConf := mysql.Config{ - DSN: dsn, - DontSupportRenameColumn: true, - //SkipInitializeWithVersion: false, // 根据数据库版本自动配置 + switch dbType { + case "sqlite": + dialector = sqlite.Open(dbName) + case "mysql": + dialector = mysql.Open(dsn) + default: + panic(fmt.Errorf("unsupported db type: %v", dbType)) } - dialector := mysql.New(driverConf) + + //driverConf := mysql.Config{ + // DSN: dsn, + // DontSupportRenameColumn: true, + // //SkipInitializeWithVersion: false, // 根据数据库版本自动配置 + //} + //dialector = mysql.New(driverConf) //slowLogger := logger.New( // syslog.New(xlog.GetGlobalWriter(), "\n", syslog.LstdFlags), @@ -97,13 +113,7 @@ func createDBAndGuaranteeMigrate(dsnWithoutDb, dsn string, tables []any) (*gorm. return db, nil } -func tryCreateDB(dbName, dsn string) (string, error) { - driverConf := mysql.Config{ - DSN: dsn, - DontSupportRenameColumn: true, - //SkipInitializeWithVersion: false, // 根据数据库版本自动配置 - } - dialector := mysql.New(driverConf) +func tryCreateDB(dbType string, dialector gorm.Dialector, dbName string) (string, error) { db, err := gorm.Open(dialector, &gorm.Config{ PrepareStmt: false, // 关闭缓存sql语句功能,因为后续use db会报错,这个缓存会无限存储可能导致内存泄露 @@ -114,14 +124,16 @@ func tryCreateDB(dbName, dsn string) (string, error) { } // 检查数据库是否存在 - var count int - db.Raw("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?", dbName).Scan(&count) - if count == 0 { - // 数据库不存在,创建它 - sql := fmt.Sprintf("create database if not exists `%s` default charset utf8mb4 collate utf8mb4_unicode_ci", - dbName) - if e := db.Exec(sql).Error; e != nil { - return "", fmt.Errorf("failed to create database:%v", e) + if dbType != "sqlite" { + var count int + db.Raw("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = ?", dbName).Scan(&count) + if count == 0 { + // 数据库不存在,创建它 + sql := fmt.Sprintf("create database if not exists `%s` default charset utf8mb4 collate utf8mb4_unicode_ci", + dbName) + if e := db.Exec(sql).Error; e != nil { + return "", fmt.Errorf("failed to create database:%v", e) + } } } @@ -135,8 +147,17 @@ func autoMigrate(db *gorm.DB, tables ...interface{}) error { // 这个函数是在InitConn之后调用的 // 初始化表 - if err := db.AutoMigrate(tables...); err != nil { - return errcode.New(errcode.DBError, "failed to init tables:%v", err) + for _, table := range tables { + if err := db.AutoMigrate(table); err != nil { + if strings.Contains(err.Error(), "there is already a table named") { + continue + } + if strings.Contains(err.Error(), "already exists") { + continue + } + return errcode.New(errcode.DBError, "failed to init tables:%v", err) + } } + return nil } diff --git a/admin/internal/errcode/code.go b/admin/internal/errcode/code.go index 1101ac9..ad91540 100644 --- a/admin/internal/errcode/code.go +++ b/admin/internal/errcode/code.go @@ -9,4 +9,5 @@ const ( TokenInvalid = 5 UserOrPassInValid = 7 // 用户名或密码错误 NoPermission = 8 // 没有权限 + ParamsInvalid = 9 ) diff --git a/admin/internal/event/event.go b/admin/internal/event/event.go new file mode 100644 index 0000000..a51390d --- /dev/null +++ b/admin/internal/event/event.go @@ -0,0 +1,15 @@ +package event + +import "admin/lib/eventmgr" + +type Msg = eventmgr.Msg + +var manager *eventmgr.Mgr + +func init() { + manager = eventmgr.NewMgr() +} + +func GetMgrInstance() *eventmgr.Mgr { + return manager +} diff --git a/admin/internal/event/topic.go b/admin/internal/event/topic.go new file mode 100644 index 0000000..9b7f16c --- /dev/null +++ b/admin/internal/event/topic.go @@ -0,0 +1,15 @@ +package event + +const ( + EventTopic_UserExecute = "user.execute" +) + +type EventPayload_UserExecute struct { + UserId int `json:"user_id"` + UserName string `json:"user_name"` + ProjectId int `json:"project_id"` + Resource string `json:"resource"` + Method string `json:"method"` + OldData any `json:"old_data"` + NewData any `json:"new_data"` +} diff --git a/admin/lib/eventmgr/event.go b/admin/lib/eventmgr/event.go new file mode 100644 index 0000000..938ae11 --- /dev/null +++ b/admin/lib/eventmgr/event.go @@ -0,0 +1,66 @@ +package eventmgr + +import ( + "admin/lib/xlog" + "encoding/json" + "runtime" +) + +type Msg struct { + Event string + Payload []byte +} + +type subscriber struct { + name string + topic string + handler func(*Msg) +} + +type Mgr struct { + subscribers []*subscriber + broker chan *Msg +} + +func NewMgr() *Mgr { + mgr := &Mgr{ + subscribers: make([]*subscriber, 0), + broker: make(chan *Msg, 1024), + } + mgr.start() + return mgr +} + +func (m *Mgr) start() { + for i := 0; i < runtime.NumCPU(); i++ { + go func() { + for { + select { + case msg := <-m.broker: + for _, sub := range m.subscribers { + if sub.topic == msg.Event { + sub.handler(msg) + } + } + } + } + }() + } +} + +func (m *Mgr) Subscribe(name string, topic string, handler func(*Msg)) { + m.subscribers = append(m.subscribers, &subscriber{name: name, topic: topic, handler: handler}) +} + +func (m *Mgr) Publish(event string, payload any) { + payloadBin, err := json.Marshal(payload) + if err != nil { + panic(err) + } + msg := &Msg{Event: event, Payload: payloadBin} + select { + case m.broker <- msg: + default: + xlog.Errorf("publish event:%v broker is full", event) + } +} diff --git a/admin/uniugm b/admin/uniugm new file mode 100644 index 0000000..1ac481a Binary files /dev/null and b/admin/uniugm differ diff --git a/ui/.env.development b/ui/.env.development index ff54252..1683d70 100644 --- a/ui/.env.development +++ b/ui/.env.development @@ -1,2 +1,2 @@ VITE_APP_BASE_API = '/api' -VITE_APP_BASE_URL = 'http://192.168.78.128:8080/api' \ No newline at end of file +VITE_APP_BASE_URL = 'http://localhost:8080/api' \ No newline at end of file diff --git a/ui/README.md b/ui/README.md index 82d7ccd..1a66c21 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,29 +1,5 @@ # ui -This template should help get you started developing with Vue 3 in Vite. - -## Recommended IDE Setup - -[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). - -## Customize configuration - -See [Vite Configuration Reference](https://vite.dev/config/). - -## Project Setup - -```sh -npm install -``` - -### Compile and Hot-Reload for Development - -```sh +npm install --registry=https://registry.npmmirror.com npm run dev -``` - -### Compile and Minify for Production - -```sh npm run build -``` diff --git a/ui/src/api/resource.js b/ui/src/api/resource.js index 295e85c..4df3332 100644 --- a/ui/src/api/resource.js +++ b/ui/src/api/resource.js @@ -38,4 +38,11 @@ export function resourceDelete(url, params) { method: 'delete', data: params }) +} + +export function resourceGetAllItems(projectId) { + return request({ + url: '/project/' + projectId.toString() + "/items", + method: 'get', + }) } \ No newline at end of file diff --git a/ui/src/components/restful/table.vue b/ui/src/components/restful/table.vue index c96c6fe..7804e0d 100644 --- a/ui/src/components/restful/table.vue +++ b/ui/src/components/restful/table.vue @@ -1,20 +1,24 @@