🎨 更新项目版本
This commit is contained in:
45
service/system/auto_code_mcp.go
Normal file
45
service/system/auto_code_mcp.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
"git.echol.cn/loser/lckt/utils"
|
||||
"git.echol.cn/loser/lckt/utils/autocode"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func (s *autoCodeTemplate) CreateMcp(ctx context.Context, info request.AutoMcpTool) (toolFilePath string, err error) {
|
||||
mcpTemplatePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "mcp", "tools.tpl")
|
||||
mcpToolPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "mcp")
|
||||
|
||||
var files *template.Template
|
||||
|
||||
templateName := filepath.Base(mcpTemplatePath)
|
||||
|
||||
files, err = template.New(templateName).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(mcpTemplatePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fileName := utils.HumpToUnderscore(info.Name)
|
||||
|
||||
toolFilePath = filepath.Join(mcpToolPath, fileName+".go")
|
||||
|
||||
f, err := os.Create(toolFilePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// 执行模板,将内容写入文件
|
||||
err = files.Execute(f, info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
@@ -3,20 +3,20 @@ package system
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
common "git.echol.cn/loser/lckt/model/common/request"
|
||||
model "git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
"git.echol.cn/loser/lckt/utils"
|
||||
"git.echol.cn/loser/lckt/utils/ast"
|
||||
"git.echol.cn/loser/lckt/utils/autocode"
|
||||
"github.com/pkg/errors"
|
||||
"go/token"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var AutoCodePackage = new(autoCodePackage)
|
||||
@@ -59,7 +59,7 @@ func (s *autoCodePackage) Create(ctx context.Context, info *request.SysAutoCodeP
|
||||
}
|
||||
for key, value := range creates { // key 为 模版绝对路径
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
files, err = template.New(filepath.Base(key)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
@@ -114,6 +114,20 @@ func (s *autoCodePackage) Delete(ctx context.Context, info common.GetById) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByNames
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) DeleteByNames(ctx context.Context, names []string) error {
|
||||
if len(names) == 0 {
|
||||
return nil
|
||||
}
|
||||
err := global.GVA_DB.WithContext(ctx).Where("package_name IN ?", names).Delete(&model.SysAutoCodePackage{}).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// All 获取所有包
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
@@ -233,6 +247,9 @@ func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) {
|
||||
if entries[i].Name() == "preview" {
|
||||
continue
|
||||
} // preview 为预览代码生成器的代码
|
||||
if entries[i].Name() == "mcp" {
|
||||
continue
|
||||
} // preview 为mcp生成器的代码
|
||||
templates = append(templates, entries[i].Name())
|
||||
}
|
||||
}
|
||||
@@ -267,7 +284,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
three := filepath.Join(second, secondDirs[j].Name())
|
||||
if !secondDirs[j].IsDir() {
|
||||
ext := filepath.Ext(secondDirs[j].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", three)
|
||||
}
|
||||
name := strings.TrimSuffix(secondDirs[j].Name(), ext)
|
||||
@@ -301,7 +318,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
@@ -473,7 +490,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
gen := strings.Index(threeDirs[k].Name(), "gen")
|
||||
@@ -557,7 +574,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", five)
|
||||
}
|
||||
ext := filepath.Ext(five)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", five)
|
||||
}
|
||||
hasRequest := strings.Index(fourDirs[l].Name(), "request")
|
||||
@@ -573,7 +590,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
continue
|
||||
}
|
||||
ext := filepath.Ext(threeDirs[k].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
hasModel := strings.Index(threeDirs[k].Name(), "model")
|
||||
@@ -634,7 +651,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
if ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
|
@@ -2,10 +2,11 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
model "git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
)
|
||||
|
||||
func Test_autoCodePackage_Create(t *testing.T) {
|
||||
@@ -53,9 +54,10 @@ func Test_autoCodePackage_Create(t *testing.T) {
|
||||
|
||||
func Test_autoCodePackage_templates(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
entity model.SysAutoCodePackage
|
||||
info request.AutoCode
|
||||
ctx context.Context
|
||||
entity model.SysAutoCodePackage
|
||||
info request.AutoCode
|
||||
isPackage bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -78,6 +80,7 @@ func Test_autoCodePackage_templates(t *testing.T) {
|
||||
Abbreviation: "user",
|
||||
HumpPackageName: "user",
|
||||
},
|
||||
isPackage: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -85,7 +88,7 @@ func Test_autoCodePackage_templates(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &autoCodePackage{}
|
||||
gotCode, gotEnter, gotCreates, err := s.templates(tt.args.ctx, tt.args.entity, tt.args.info)
|
||||
gotCode, gotEnter, gotCreates, err := s.templates(tt.args.ctx, tt.args.entity, tt.args.info, tt.args.isPackage)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("templates() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
"git.echol.cn/loser/lckt/utils"
|
||||
"git.echol.cn/loser/lckt/utils/ast"
|
||||
"github.com/mholt/archiver/v4"
|
||||
"github.com/mholt/archives"
|
||||
cp "github.com/otiai10/copy"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
@@ -154,7 +154,7 @@ func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) {
|
||||
|
||||
fileName := plugName + ".zip"
|
||||
// 创建一个新的zip文件
|
||||
files, err := archiver.FilesFromDisk(nil, map[string]string{
|
||||
files, err := archives.FilesFromDisk(context.Background(), nil, map[string]string{
|
||||
webPath: plugName + "/web/plugin/" + plugName,
|
||||
serverPath: plugName + "/server/plugin/" + plugName,
|
||||
})
|
||||
@@ -168,8 +168,9 @@ func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) {
|
||||
|
||||
// we can use the CompressedArchive type to gzip a tarball
|
||||
// (compression is not required; you could use Tar directly)
|
||||
format := archiver.Archive{
|
||||
Archival: archiver.Zip{},
|
||||
format := archives.CompressedArchive{
|
||||
//Compression: archives.Gz{},
|
||||
Archival: archives.Zip{},
|
||||
}
|
||||
|
||||
// create the archive
|
||||
@@ -207,7 +208,8 @@ func (s *autoCodePlugin) InitMenu(menuInfo request.InitMenu) (err error) {
|
||||
},
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Find(&menus, "id in (?)", menuInfo.Menus).Error
|
||||
// 查询菜单及其关联的参数和按钮
|
||||
err = global.GVA_DB.Preload("Parameters").Preload("MenuBtn").Find(&menus, "id in (?)", menuInfo.Menus).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/lckt/utils/autocode"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
@@ -224,7 +225,7 @@ func (s *autoCodeTemplate) generate(ctx context.Context, info request.AutoCode,
|
||||
code := make(map[string]strings.Builder)
|
||||
for key, create := range templates {
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
files, err = template.New(filepath.Base(key)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(key)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
@@ -322,7 +323,7 @@ func (s *autoCodeTemplate) GetApiAndServer(info request.AutoFunc) (map[string]st
|
||||
|
||||
func (s *autoCodeTemplate) getTemplateStr(t string, info request.AutoFunc) (string, error) {
|
||||
tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".tpl")
|
||||
files, err := template.ParseFiles(tempPath)
|
||||
files, err := template.New(filepath.Base(tempPath)).Funcs(autocode.GetTemplateFuncMap()).ParseFiles(tempPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath)
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ type ServiceGroup struct {
|
||||
AuthorityBtnService
|
||||
SysExportTemplateService
|
||||
SysParamsService
|
||||
SysVersionService
|
||||
AutoCodePlugin autoCodePlugin
|
||||
AutoCodePackage autoCodePackage
|
||||
AutoCodeHistory autoCodeHistory
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
"git.echol.cn/loser/lckt/utils"
|
||||
)
|
||||
|
||||
type JwtService struct{}
|
||||
@@ -29,20 +28,6 @@ func (jwtService *JwtService) JsonInBlacklist(jwtList system.JwtBlacklist) (err
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: IsBlacklist
|
||||
//@description: 判断JWT是否在黑名单内部
|
||||
//@param: jwt string
|
||||
//@return: bool
|
||||
|
||||
func (jwtService *JwtService) IsBlacklist(jwt string) bool {
|
||||
_, ok := global.BlackCache.Get(jwt)
|
||||
return ok
|
||||
// err := global.GVA_DB.Where("jwt = ?", jwt).First(&system.JwtBlacklist{}).Error
|
||||
// isNotFound := errors.Is(err, gorm.ErrRecordNotFound)
|
||||
// return !isNotFound
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetRedisJWT
|
||||
//@description: 从redis取jwt
|
||||
@@ -54,23 +39,6 @@ func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err
|
||||
return redisJWT, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetRedisJWT
|
||||
//@description: jwt存入redis并设置过期时间
|
||||
//@param: jwt string, userName string
|
||||
//@return: err error
|
||||
|
||||
func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) {
|
||||
// 此处过期时间等于jwt过期时间
|
||||
dr, err := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timer := dr
|
||||
err = global.GVA_REDIS.Set(context.Background(), userName, jwt, timer).Err()
|
||||
return err
|
||||
}
|
||||
|
||||
func LoadAll() {
|
||||
var data []string
|
||||
err := global.GVA_DB.Model(&system.JwtBlacklist{}).Select("jwt").Find(&data).Error
|
||||
|
@@ -326,5 +326,8 @@ func (authorityService *AuthorityService) findChildrenAuthority(authority *syste
|
||||
func (authorityService *AuthorityService) GetParentAuthorityID(authorityID uint) (parentID uint, err error) {
|
||||
var authority system.SysAuthority
|
||||
err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error
|
||||
return *authority.ParentId, err
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return *authority.ParentId, nil
|
||||
}
|
||||
|
@@ -64,7 +64,7 @@ WHERE
|
||||
lower(a.table_name) = ?
|
||||
AND lower(a.OWNER) = ?
|
||||
ORDER BY
|
||||
a.COLUMN_ID;
|
||||
a.COLUMN_ID
|
||||
`
|
||||
|
||||
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error
|
||||
|
@@ -72,6 +72,7 @@ func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.SysBaseMenu)
|
||||
var oldMenu system.SysBaseMenu
|
||||
upDateMap := make(map[string]interface{})
|
||||
upDateMap["keep_alive"] = menu.KeepAlive
|
||||
upDateMap["transition_type"] = menu.TransitionType
|
||||
upDateMap["close_tab"] = menu.CloseTab
|
||||
upDateMap["default_menu"] = menu.DefaultMenu
|
||||
upDateMap["parent_id"] = menu.ParentId
|
||||
|
@@ -3,17 +3,14 @@ package system
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system/request"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
"git.echol.cn/loser/lckt/utils"
|
||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
@@ -68,7 +65,7 @@ func (casbinService *CasbinService) UpdateCasbin(adminAuthorityID, AuthorityID u
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
} // 设置空权限无需调用 AddPolicies 方法
|
||||
e := casbinService.Casbin()
|
||||
e := utils.GetCasbin()
|
||||
success, _ := e.AddPolicies(rules)
|
||||
if !success {
|
||||
return errors.New("存在相同api,添加失败,请联系管理员")
|
||||
@@ -91,7 +88,7 @@ func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath stri
|
||||
return err
|
||||
}
|
||||
|
||||
e := casbinService.Casbin()
|
||||
e := utils.GetCasbin()
|
||||
return e.LoadPolicy()
|
||||
}
|
||||
|
||||
@@ -102,7 +99,7 @@ func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath stri
|
||||
//@return: pathMaps []request.CasbinInfo
|
||||
|
||||
func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []request.CasbinInfo) {
|
||||
e := casbinService.Casbin()
|
||||
e := utils.GetCasbin()
|
||||
authorityId := strconv.Itoa(int(AuthorityID))
|
||||
list, _ := e.GetFilteredPolicy(0, authorityId)
|
||||
for _, v := range list {
|
||||
@@ -121,7 +118,7 @@ func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint)
|
||||
//@return: bool
|
||||
|
||||
func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
|
||||
e := casbinService.Casbin()
|
||||
e := utils.GetCasbin()
|
||||
success, _ := e.RemoveFilteredPolicy(v, p...)
|
||||
return success
|
||||
}
|
||||
@@ -170,52 +167,7 @@ func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) e
|
||||
}
|
||||
|
||||
func (casbinService *CasbinService) FreshCasbin() (err error) {
|
||||
e := casbinService.Casbin()
|
||||
e := utils.GetCasbin()
|
||||
err = e.LoadPolicy()
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: Casbin
|
||||
//@description: 持久化到数据库 引入自定义规则
|
||||
//@return: *casbin.Enforcer
|
||||
|
||||
var (
|
||||
syncedCachedEnforcer *casbin.SyncedCachedEnforcer
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func (casbinService *CasbinService) Casbin() *casbin.SyncedCachedEnforcer {
|
||||
once.Do(func() {
|
||||
a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
|
||||
if err != nil {
|
||||
zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
|
||||
return
|
||||
}
|
||||
text := `
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
|
||||
`
|
||||
m, err := model.NewModelFromString(text)
|
||||
if err != nil {
|
||||
zap.L().Error("字符串加载模型失败!", zap.Error(err))
|
||||
return
|
||||
}
|
||||
syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
|
||||
syncedCachedEnforcer.SetExpireTime(60 * 60)
|
||||
_ = syncedCachedEnforcer.LoadPolicy()
|
||||
})
|
||||
return syncedCachedEnforcer
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package system
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
@@ -127,6 +128,11 @@ func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateIn
|
||||
// ExportExcel 导出Excel
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) {
|
||||
var params = values.Get("params")
|
||||
paramsValues, err := url.ParseQuery(params)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("解析 params 参数失败: %v", err)
|
||||
}
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
@@ -175,10 +181,38 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
|
||||
|
||||
db = db.Select(selects).Table(template.TableName)
|
||||
|
||||
filterDeleted := false
|
||||
|
||||
filterParam := paramsValues.Get("filterDeleted")
|
||||
if filterParam == "true" {
|
||||
filterDeleted = true
|
||||
}
|
||||
|
||||
if filterDeleted {
|
||||
// 自动过滤主表的软删除
|
||||
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
||||
|
||||
// 过滤关联表的软删除(如果有)
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
// 检查关联表是否有deleted_at字段
|
||||
hasDeletedAt := sysExportTemplateService.hasDeletedAtColumn(join.Table)
|
||||
if hasDeletedAt {
|
||||
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(template.Conditions) > 0 {
|
||||
for _, condition := range template.Conditions {
|
||||
sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator)
|
||||
value := values.Get(condition.From)
|
||||
value := paramsValues.Get(condition.From)
|
||||
|
||||
if condition.Operator == "IN" || condition.Operator == "NOT IN" {
|
||||
sql = fmt.Sprintf("%s %s (?)", condition.Column, condition.Operator)
|
||||
}
|
||||
|
||||
if value != "" {
|
||||
if condition.Operator == "LIKE" {
|
||||
value = "%" + value + "%"
|
||||
@@ -188,7 +222,7 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
|
||||
}
|
||||
}
|
||||
// 通过参数传入limit
|
||||
limit := values.Get("limit")
|
||||
limit := paramsValues.Get("limit")
|
||||
if limit != "" {
|
||||
l, e := strconv.Atoi(limit)
|
||||
if e == nil {
|
||||
@@ -201,7 +235,7 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
|
||||
}
|
||||
|
||||
// 通过参数传入offset
|
||||
offset := values.Get("offset")
|
||||
offset := paramsValues.Get("offset")
|
||||
if offset != "" {
|
||||
o, e := strconv.Atoi(offset)
|
||||
if e == nil {
|
||||
@@ -224,7 +258,7 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
|
||||
}
|
||||
|
||||
// 通过参数传入order
|
||||
order := values.Get("order")
|
||||
order := paramsValues.Get("order")
|
||||
|
||||
if order == "" && template.Order != "" {
|
||||
// 如果没有order入参,这里会使用模板的默认排序
|
||||
@@ -281,7 +315,17 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
|
||||
}
|
||||
for i, row := range rows {
|
||||
for j, colCell := range row {
|
||||
sErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(j+1), i+1), colCell)
|
||||
cell := fmt.Sprintf("%s%d", getColumnName(j+1), i+1)
|
||||
|
||||
var sErr error
|
||||
if v, err := strconv.ParseFloat(colCell, 64); err == nil {
|
||||
sErr = f.SetCellValue("Sheet1", cell, v)
|
||||
} else if v, err := strconv.ParseInt(colCell, 10, 64); err == nil {
|
||||
sErr = f.SetCellValue("Sheet1", cell, v)
|
||||
} else {
|
||||
sErr = f.SetCellValue("Sheet1", cell, colCell)
|
||||
}
|
||||
|
||||
if sErr != nil {
|
||||
return nil, "", sErr
|
||||
}
|
||||
@@ -344,6 +388,13 @@ func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templat
|
||||
return file, template.Name, nil
|
||||
}
|
||||
|
||||
// 辅助函数:检查表是否有deleted_at列
|
||||
func (s *SysExportTemplateService) hasDeletedAtColumn(tableName string) bool {
|
||||
var count int64
|
||||
global.GVA_DB.Raw("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = 'deleted_at'", tableName).Count(&count)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// ImportExcel 导入Excel
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) {
|
||||
@@ -368,6 +419,9 @@ func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
return errors.New("Excel data is not enough.\nIt should contain title row and data")
|
||||
}
|
||||
|
||||
var templateInfoMap = make(map[string]string)
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
@@ -387,11 +441,17 @@ func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID
|
||||
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
excelTitle := rows[0]
|
||||
for i, str := range excelTitle {
|
||||
excelTitle[i] = strings.TrimSpace(str)
|
||||
}
|
||||
values := rows[1:]
|
||||
items := make([]map[string]interface{}, 0, len(values))
|
||||
for _, row := range values {
|
||||
var item = make(map[string]interface{})
|
||||
for ii, value := range row {
|
||||
if _, ok := titleKeyMap[excelTitle[ii]]; !ok {
|
||||
continue // excel中多余的标题,在模板信息中没有对应的字段,因此key为空,必须跳过
|
||||
}
|
||||
key := titleKeyMap[excelTitle[ii]]
|
||||
item[key] = value
|
||||
}
|
||||
|
@@ -134,10 +134,52 @@ func (menuService *MenuService) getBaseChildrenList(menu *system.SysBaseMenu, tr
|
||||
//@return: error
|
||||
|
||||
func (menuService *MenuService) AddBaseMenu(menu system.SysBaseMenu) error {
|
||||
if !errors.Is(global.GVA_DB.Where("name = ?", menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在重复name,请修改name")
|
||||
}
|
||||
return global.GVA_DB.Create(&menu).Error
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 检查name是否重复
|
||||
if !errors.Is(tx.Where("name = ?", menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在重复name,请修改name")
|
||||
}
|
||||
|
||||
if menu.ParentId != 0 {
|
||||
// 检查父菜单是否存在
|
||||
var parentMenu system.SysBaseMenu
|
||||
if err := tx.First(&parentMenu, menu.ParentId).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("父菜单不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查父菜单下现有子菜单数量
|
||||
var existingChildrenCount int64
|
||||
err := tx.Model(&system.SysBaseMenu{}).Where("parent_id = ?", menu.ParentId).Count(&existingChildrenCount).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果父菜单原本是叶子菜单(没有子菜单),现在要变成枝干菜单,需要清空其权限分配
|
||||
if existingChildrenCount == 0 {
|
||||
// 检查父菜单是否被其他角色设置为首页
|
||||
var defaultRouterCount int64
|
||||
err := tx.Model(&system.SysAuthority{}).Where("default_router = ?", parentMenu.Name).Count(&defaultRouterCount).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if defaultRouterCount > 0 {
|
||||
return errors.New("父菜单已被其他角色的首页占用,请先释放父菜单的首页权限")
|
||||
}
|
||||
|
||||
// 清空父菜单的所有权限分配
|
||||
err = tx.Where("sys_base_menu_id = ?", menu.ParentId).Delete(&system.SysAuthorityMenu{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
return tx.Create(&menu).Error
|
||||
})
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
|
@@ -17,11 +17,6 @@ type OperationRecordService struct{}
|
||||
|
||||
var OperationRecordServiceApp = new(OperationRecordService)
|
||||
|
||||
func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) {
|
||||
err = global.GVA_DB.Create(&sysOperationRecord).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [granty1](https://github.com/granty1)
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteSysOperationRecordByIds
|
||||
|
@@ -3,9 +3,10 @@ package system
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/lckt/model/common"
|
||||
systemReq "git.echol.cn/loser/lckt/model/system/request"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
@@ -63,20 +64,20 @@ func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysU
|
||||
//@function: ChangePassword
|
||||
//@description: 修改用户密码
|
||||
//@param: u *model.SysUser, newPassword string
|
||||
//@return: userInter *model.SysUser,err error
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (userInter *system.SysUser, err error) {
|
||||
func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (err error) {
|
||||
var user system.SysUser
|
||||
if err = global.GVA_DB.Where("id = ?", u.ID).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
err = global.GVA_DB.Select("id, password").Where("id = ?", u.ID).First(&user).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
|
||||
return nil, errors.New("原密码错误")
|
||||
return errors.New("原密码错误")
|
||||
}
|
||||
user.Password = utils.BcryptHash(newPassword)
|
||||
err = global.GVA_DB.Save(&user).Error
|
||||
return &user, err
|
||||
|
||||
pwd := utils.BcryptHash(newPassword)
|
||||
err = global.GVA_DB.Model(&user).Update("password", pwd).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
@@ -311,7 +312,7 @@ func (userService *UserService) FindUserByUuid(uuid string) (user *system.SysUse
|
||||
//@param: ID uint
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) ResetPassword(ID uint) (err error) {
|
||||
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", ID).Update("password", utils.BcryptHash("123456")).Error
|
||||
func (userService *UserService) ResetPassword(ID uint, password string) (err error) {
|
||||
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", ID).Update("password", utils.BcryptHash(password)).Error
|
||||
return err
|
||||
}
|
||||
|
230
service/system/sys_version.go
Normal file
230
service/system/sys_version.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.echol.cn/loser/lckt/global"
|
||||
"git.echol.cn/loser/lckt/model/system"
|
||||
systemReq "git.echol.cn/loser/lckt/model/system/request"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SysVersionService struct{}
|
||||
|
||||
// CreateSysVersion 创建版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) CreateSysVersion(ctx context.Context, sysVersion *system.SysVersion) (err error) {
|
||||
err = global.GVA_DB.Create(sysVersion).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysVersion 删除版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) DeleteSysVersion(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.Delete(&system.SysVersion{}, "id = ?", ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysVersionByIds 批量删除版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) DeleteSysVersionByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.Where("id in ?", IDs).Delete(&system.SysVersion{}).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSysVersion 根据ID获取版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) GetSysVersion(ctx context.Context, ID string) (sysVersion system.SysVersion, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", ID).First(&sysVersion).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetSysVersionInfoList 分页获取版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) GetSysVersionInfoList(ctx context.Context, info systemReq.SysVersionSearch) (list []system.SysVersion, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysVersion{})
|
||||
var sysVersions []system.SysVersion
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if len(info.CreatedAtRange) == 2 {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1])
|
||||
}
|
||||
|
||||
if info.VersionName != nil && *info.VersionName != "" {
|
||||
db = db.Where("version_name LIKE ?", "%"+*info.VersionName+"%")
|
||||
}
|
||||
if info.VersionCode != nil && *info.VersionCode != "" {
|
||||
db = db.Where("version_code = ?", *info.VersionCode)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Find(&sysVersions).Error
|
||||
return sysVersions, total, err
|
||||
}
|
||||
func (sysVersionService *SysVersionService) GetSysVersionPublic(ctx context.Context) {
|
||||
// 此方法为获取数据源定义的数据
|
||||
// 请自行实现
|
||||
}
|
||||
|
||||
// GetMenusByIds 根据ID列表获取菜单数据
|
||||
func (sysVersionService *SysVersionService) GetMenusByIds(ctx context.Context, ids []uint) (menus []system.SysBaseMenu, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Preload("Parameters").Preload("MenuBtn").Find(&menus).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetApisByIds 根据ID列表获取API数据
|
||||
func (sysVersionService *SysVersionService) GetApisByIds(ctx context.Context, ids []uint) (apis []system.SysApi, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Find(&apis).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetDictionariesByIds 根据ID列表获取字典数据
|
||||
func (sysVersionService *SysVersionService) GetDictionariesByIds(ctx context.Context, ids []uint) (dictionaries []system.SysDictionary, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Preload("SysDictionaryDetails").Find(&dictionaries).Error
|
||||
return
|
||||
}
|
||||
|
||||
// ImportMenus 导入菜单数据
|
||||
func (sysVersionService *SysVersionService) ImportMenus(ctx context.Context, menus []system.SysBaseMenu) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 递归创建菜单
|
||||
return sysVersionService.createMenusRecursively(tx, menus, 0)
|
||||
})
|
||||
}
|
||||
|
||||
// createMenusRecursively 递归创建菜单
|
||||
func (sysVersionService *SysVersionService) createMenusRecursively(tx *gorm.DB, menus []system.SysBaseMenu, parentId uint) error {
|
||||
for _, menu := range menus {
|
||||
// 检查菜单是否已存在
|
||||
var existingMenu system.SysBaseMenu
|
||||
if err := tx.Where("name = ? AND path = ?", menu.Name, menu.Path).First(&existingMenu).Error; err == nil {
|
||||
// 菜单已存在,使用现有菜单ID继续处理子菜单
|
||||
if len(menu.Children) > 0 {
|
||||
if err := sysVersionService.createMenusRecursively(tx, menu.Children, existingMenu.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 保存参数和按钮数据,稍后处理
|
||||
parameters := menu.Parameters
|
||||
menuBtns := menu.MenuBtn
|
||||
children := menu.Children
|
||||
|
||||
// 创建新菜单(不包含关联数据)
|
||||
newMenu := system.SysBaseMenu{
|
||||
ParentId: parentId,
|
||||
Path: menu.Path,
|
||||
Name: menu.Name,
|
||||
Hidden: menu.Hidden,
|
||||
Component: menu.Component,
|
||||
Sort: menu.Sort,
|
||||
Meta: menu.Meta,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newMenu).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建参数
|
||||
if len(parameters) > 0 {
|
||||
for _, param := range parameters {
|
||||
newParam := system.SysBaseMenuParameter{
|
||||
SysBaseMenuID: newMenu.ID,
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
}
|
||||
if err := tx.Create(&newParam).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建菜单按钮
|
||||
if len(menuBtns) > 0 {
|
||||
for _, btn := range menuBtns {
|
||||
newBtn := system.SysBaseMenuBtn{
|
||||
SysBaseMenuID: newMenu.ID,
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
}
|
||||
if err := tx.Create(&newBtn).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子菜单
|
||||
if len(children) > 0 {
|
||||
if err := sysVersionService.createMenusRecursively(tx, children, newMenu.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportApis 导入API数据
|
||||
func (sysVersionService *SysVersionService) ImportApis(apis []system.SysApi) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, api := range apis {
|
||||
// 检查API是否已存在
|
||||
var existingApi system.SysApi
|
||||
if err := tx.Where("path = ? AND method = ?", api.Path, api.Method).First(&existingApi).Error; err == nil {
|
||||
// API已存在,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建新API
|
||||
newApi := system.SysApi{
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newApi).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ImportDictionaries 导入字典数据
|
||||
func (sysVersionService *SysVersionService) ImportDictionaries(dictionaries []system.SysDictionary) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, dict := range dictionaries {
|
||||
// 检查字典是否已存在
|
||||
var existingDict system.SysDictionary
|
||||
if err := tx.Where("type = ?", dict.Type).First(&existingDict).Error; err == nil {
|
||||
// 字典已存在,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建新字典
|
||||
newDict := system.SysDictionary{
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
SysDictionaryDetails: dict.SysDictionaryDetails,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newDict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user