🎨 更新项目版本

This commit is contained in:
2025-09-03 01:45:01 +08:00
parent f928348284
commit 5496bdaa94
130 changed files with 9397 additions and 1816 deletions

View 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
}

View File

@@ -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")

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -17,6 +17,7 @@ type ServiceGroup struct {
AuthorityBtnService
SysExportTemplateService
SysParamsService
SysVersionService
AutoCodePlugin autoCodePlugin
AutoCodePackage autoCodePackage
AutoCodeHistory autoCodeHistory

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View 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
})
}