添加条件搜索
This commit is contained in:
parent
16b648f3a2
commit
9d715d2f1f
@ -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
|
||||
|
@ -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" {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -10,15 +10,13 @@ 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"`
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -47,3 +47,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
|
||||
}
|
||||
|
@ -24,5 +24,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)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ import (
|
||||
"admin/apps/game/domain"
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/consts"
|
||||
"admin/internal/errcode"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -30,17 +32,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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
admin/cmd/all_in_one/all_in_one.exe
Normal file
BIN
admin/cmd/all_in_one/all_in_one.exe
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -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:"数据库用户名"`
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ const (
|
||||
TokenInvalid = 5
|
||||
UserOrPassInValid = 7 // 用户名或密码错误
|
||||
NoPermission = 8 // 没有权限
|
||||
ParamsInvalid = 9
|
||||
)
|
||||
|
BIN
admin/uniugm
Normal file
BIN
admin/uniugm
Normal file
Binary file not shown.
@ -1,2 +1,2 @@
|
||||
VITE_APP_BASE_API = '/api'
|
||||
VITE_APP_BASE_URL = 'http://192.168.78.128:8080/api'
|
||||
VITE_APP_BASE_URL = 'http://localhost:8080/api'
|
26
ui/README.md
26
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
|
||||
```
|
||||
|
@ -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',
|
||||
})
|
||||
}
|
@ -1,20 +1,24 @@
|
||||
<script setup>
|
||||
import {ElNotification} from "element-plus";
|
||||
import {resourceDelete, resourceList, resourcePost, resourcePut} from "@/api/resource.js";
|
||||
import {resourceDelete, resourceList, resourcePost, resourcePut, resourceGetAllItems} from "@/api/resource.js";
|
||||
import {ref, toRaw} from "vue";
|
||||
import {useRoute} from 'vue-router';
|
||||
import LocalCache from "@/stores/localCache.js";
|
||||
import empty from '@/components/restful/empty.vue';
|
||||
import {getWhereConditionDesc} from "@/utils/string.js";
|
||||
|
||||
const cachedResource = LocalCache.getCache("resource");
|
||||
|
||||
const listRsp = ref({fields_desc: [], rows: []})
|
||||
const listDataOK = ref(false)
|
||||
const projectId = cachedResource.meta.projectId
|
||||
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 whereFieldsDescInfo = ref([])
|
||||
const calcElColSpan = ref(0)
|
||||
const rows = ref([])
|
||||
const rules = ref({})
|
||||
|
||||
@ -22,7 +26,7 @@ const current_page = ref(0)
|
||||
const page_size = ref(0)
|
||||
|
||||
const item = ref({
|
||||
id: 0,
|
||||
id: '',
|
||||
number: 1,
|
||||
})
|
||||
|
||||
@ -33,7 +37,22 @@ const listData = async () => {
|
||||
let listParams = {
|
||||
page_no: 0,
|
||||
page_len: 100,
|
||||
where_conditions: "",
|
||||
}
|
||||
let whereReqConditions = {
|
||||
conditions: []
|
||||
}
|
||||
whereFieldsDescInfo.value.forEach((field) => {
|
||||
if (field.value1) {
|
||||
whereReqConditions.conditions.push({
|
||||
key: field.key,
|
||||
op: field.where,
|
||||
value1: field.value1,
|
||||
value2: field.value2,
|
||||
})
|
||||
}
|
||||
})
|
||||
listParams.where_conditions = JSON.stringify(whereReqConditions)
|
||||
// console.log("list params:", listParams)
|
||||
const rspData = await resourceList(resource_url, listParams);
|
||||
listRsp.value = rspData;
|
||||
@ -41,7 +60,6 @@ const listData = async () => {
|
||||
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] = ''
|
||||
@ -56,8 +74,41 @@ const listData = async () => {
|
||||
rows.value[j].jsonValue = JSON.stringify(rows.value[j][field.key])
|
||||
}
|
||||
}
|
||||
|
||||
if (field.where !== "") {
|
||||
field.value1 = ""
|
||||
field.value2 = ""
|
||||
field.whereDesc = getWhereConditionDesc(field.where)
|
||||
let find = false
|
||||
for (let i = 0; i < whereFieldsDescInfo.value.length; i++) {
|
||||
let whereField = whereFieldsDescInfo.value[i]
|
||||
if (whereField.key === field.key) {
|
||||
whereFieldsDescInfo.value[i].type = field.type
|
||||
whereFieldsDescInfo.value[i].where = field.where
|
||||
whereFieldsDescInfo.value[i].whereDesc = getWhereConditionDesc(field.where)
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!find) {
|
||||
whereFieldsDescInfo.value.push(field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calcElColSpan.value = 0
|
||||
// 计算el-col占用24格子的span数量
|
||||
let calcElColSpanTmp = 1
|
||||
whereFieldsDescInfo.value.forEach((field) => {
|
||||
if (field.where === "range") {
|
||||
calcElColSpanTmp += 2
|
||||
} else {
|
||||
calcElColSpanTmp += 1
|
||||
}
|
||||
})
|
||||
calcElColSpan.value = 24/calcElColSpanTmp
|
||||
|
||||
console.log("where fields:", whereFieldsDescInfo.value)
|
||||
// console.log('await list rsp:', listRsp.value, fieldsDescInfo.value, toRaw(rows.value), toRaw(rules.value))
|
||||
|
||||
listDataOK.value = true
|
||||
@ -208,6 +259,27 @@ const handleCloseDialog = () => {
|
||||
dialogEditForm.value = {}
|
||||
}
|
||||
|
||||
const loadingRemoteItems = ref(false)
|
||||
const itemChoices = ref({})
|
||||
const handleQueryItem = (itemQueryStr) => {
|
||||
if (!itemQueryStr) {
|
||||
itemChoices.value = []
|
||||
return
|
||||
}
|
||||
loadingRemoteItems.value = true
|
||||
itemQueryStr = itemQueryStr.replace(/[\s\u3000]/g, "")
|
||||
resourceGetAllItems(projectId).then((res) => {
|
||||
console.log("获取所有道具返回:", res.data)
|
||||
console.log("查询字符串:[" + itemQueryStr + "]")
|
||||
itemChoices.value = res.data.items.filter((item) => {
|
||||
return item.desc.includes(itemQueryStr)
|
||||
})
|
||||
loadingRemoteItems.value = false
|
||||
}, (err) => {
|
||||
itemChoices.value = []
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -217,10 +289,36 @@ const handleCloseDialog = () => {
|
||||
<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-row :gutter="20" v-if="(whereFieldsDescInfo.length !== 0)">
|
||||
<template v-for="fieldDescInfo in whereFieldsDescInfo">
|
||||
<template v-if="(fieldDescInfo.where === 'range')">
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-date-picker v-model="fieldDescInfo.value1" type="datetime"
|
||||
:placeholder="(fieldDescInfo.name + '起始')" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-col>
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-date-picker v-model="fieldDescInfo.value2" type="datetime"
|
||||
:placeholder="(fieldDescInfo.name + '结束')" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-input v-model="fieldDescInfo.value1"
|
||||
:placeholder="(fieldDescInfo.name + fieldDescInfo.whereDesc)" style="width: 150px"></el-input>
|
||||
</template>
|
||||
</template>
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-button @click="listData" type="primary">条件搜索</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row style="margin-top: 10px">
|
||||
<el-button @click="dialogAddVisible = true" size="large" type="primary"
|
||||
v-if="(resource_raw_node.meta.methods.post === true)">
|
||||
添加
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-table :data="rows" style="width: 100%" table-layout="auto" stripe>
|
||||
@ -273,9 +371,11 @@ const handleCloseDialog = () => {
|
||||
<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"
|
||||
<el-select v-model="item.id" placeholder="--搜索道具--" style="width: 150px"
|
||||
filterable remote
|
||||
:remote-method="handleQueryItem"
|
||||
:loading="loadingRemoteItems">
|
||||
<el-option v-for="info in itemChoices" :key="info.value" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
@ -354,9 +454,9 @@ const handleCloseDialog = () => {
|
||||
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"
|
||||
<el-select placeholder="--搜索道具--" v-model="item.id" style="width: 150px"
|
||||
filterable remote :remote-method="handleQueryItem" :loading="loadingRemoteItems">
|
||||
<el-option v-for="info in itemChoices" :key="info.value" :label="info.desc"
|
||||
:value="info.value"></el-option>
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
|
@ -115,6 +115,7 @@ export function setProjectOperationRoutes(projectList) {
|
||||
name: projectRoute.name + "_" + resource.resource,
|
||||
meta: {
|
||||
desc: resource.desc,
|
||||
projectId: project.project_id,
|
||||
resource: resource.resource,
|
||||
resource_url: routePath,
|
||||
methods: {},
|
||||
|
19
ui/src/utils/string.js
Normal file
19
ui/src/utils/string.js
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
export function getWhereConditionDesc(where) {
|
||||
switch (where) {
|
||||
case 'eq':
|
||||
return "等于"
|
||||
case 'gt':
|
||||
return "大于"
|
||||
case 'lt':
|
||||
return "小于"
|
||||
case 'ge':
|
||||
return "大于等于"
|
||||
case 'le':
|
||||
return "小于等于"
|
||||
case 'like':
|
||||
return "包含"
|
||||
case 'range':
|
||||
return ""
|
||||
}
|
||||
}
|
@ -236,12 +236,12 @@ h1 {
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-menu-item) {
|
||||
color: #fff;
|
||||
background-color: cadetblue;
|
||||
background-color: #4d4f52;
|
||||
}
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-sub-menu) {
|
||||
color: #fff;
|
||||
background-color: cadetblue;
|
||||
background-color: #4d4f52;
|
||||
}
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-sub-menu__title) {
|
||||
@ -255,7 +255,7 @@ h1 {
|
||||
|
||||
:deep(.el-menu-vertical-demo .el-menu-item.is-active),
|
||||
:deep(.el-menu-vertical-demo .el-sub-menu__title.is-active) {
|
||||
background-color: #ffd04b;
|
||||
background-color: #e91f63;
|
||||
color: #545c64;
|
||||
}
|
||||
|
||||
|
7
ui/todo.md
Normal file
7
ui/todo.md
Normal file
@ -0,0 +1,7 @@
|
||||
# todo列表
|
||||
- [x] 道具列表选择支持远程搜索(避免道具列表太长卡顿)
|
||||
- [ ] 表格各种项目支持分页(难点是账户、角色、订单如何分)
|
||||
- [ ] 表格字段排序
|
||||
- [ ] 表格字段搜索
|
||||
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁)
|
||||
- [ ]
|
Loading…
x
Reference in New Issue
Block a user