✨ init project
This commit is contained in:
13
service/enter.go
Normal file
13
service/enter.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/service/example"
|
||||
"git.echol.cn/loser/xiecheng_server/service/system"
|
||||
)
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
|
||||
type ServiceGroup struct {
|
||||
SystemServiceGroup system.ServiceGroup
|
||||
ExampleServiceGroup example.ServiceGroup
|
||||
}
|
7
service/example/enter.go
Normal file
7
service/example/enter.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package example
|
||||
|
||||
type ServiceGroup struct {
|
||||
CustomerService
|
||||
FileUploadAndDownloadService
|
||||
AttachmentCategoryService
|
||||
}
|
66
service/example/exa_attachment_category.go
Normal file
66
service/example/exa_attachment_category.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/example"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AttachmentCategoryService struct{}
|
||||
|
||||
// AddCategory 创建/更新的分类
|
||||
func (a *AttachmentCategoryService) AddCategory(req *example.ExaAttachmentCategory) (err error) {
|
||||
// 检查是否已存在相同名称的分类
|
||||
if (!errors.Is(global.GVA_DB.Take(&example.ExaAttachmentCategory{}, "name = ? and pid = ?", req.Name, req.Pid).Error, gorm.ErrRecordNotFound)) {
|
||||
return errors.New("分类名称已存在")
|
||||
}
|
||||
if req.ID > 0 {
|
||||
if err = global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Where("id = ?", req.ID).Updates(&example.ExaAttachmentCategory{
|
||||
Name: req.Name,
|
||||
Pid: req.Pid,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = global.GVA_DB.Create(&example.ExaAttachmentCategory{
|
||||
Name: req.Name,
|
||||
Pid: req.Pid,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCategory 删除分类
|
||||
func (a *AttachmentCategoryService) DeleteCategory(id *int) error {
|
||||
var childCount int64
|
||||
global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Where("pid = ?", id).Count(&childCount)
|
||||
if childCount > 0 {
|
||||
return errors.New("请先删除子级")
|
||||
}
|
||||
return global.GVA_DB.Where("id = ?", id).Unscoped().Delete(&example.ExaAttachmentCategory{}).Error
|
||||
}
|
||||
|
||||
// GetCategoryList 分类列表
|
||||
func (a *AttachmentCategoryService) GetCategoryList() (res []*example.ExaAttachmentCategory, err error) {
|
||||
var fileLists []example.ExaAttachmentCategory
|
||||
err = global.GVA_DB.Model(&example.ExaAttachmentCategory{}).Find(&fileLists).Error
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return a.getChildrenList(fileLists, 0), nil
|
||||
}
|
||||
|
||||
// getChildrenList 子类
|
||||
func (a *AttachmentCategoryService) getChildrenList(categories []example.ExaAttachmentCategory, parentID uint) []*example.ExaAttachmentCategory {
|
||||
var tree []*example.ExaAttachmentCategory
|
||||
for _, category := range categories {
|
||||
if category.Pid == parentID {
|
||||
category.Children = a.getChildrenList(categories, category.ID)
|
||||
tree = append(tree, &category)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
71
service/example/exa_breakpoint_continue.go
Normal file
71
service/example/exa_breakpoint_continue.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/example"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FileUploadAndDownloadService struct{}
|
||||
|
||||
var FileUploadAndDownloadServiceApp = new(FileUploadAndDownloadService)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: FindOrCreateFile
|
||||
//@description: 上传文件时检测当前文件属性,如果没有文件则创建,有则返回文件的当前切片
|
||||
//@param: fileMd5 string, fileName string, chunkTotal int
|
||||
//@return: file model.ExaFile, err error
|
||||
|
||||
func (e *FileUploadAndDownloadService) FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (file example.ExaFile, err error) {
|
||||
var cfile example.ExaFile
|
||||
cfile.FileMd5 = fileMd5
|
||||
cfile.FileName = fileName
|
||||
cfile.ChunkTotal = chunkTotal
|
||||
|
||||
if errors.Is(global.GVA_DB.Where("file_md5 = ? AND is_finish = ?", fileMd5, true).First(&file).Error, gorm.ErrRecordNotFound) {
|
||||
err = global.GVA_DB.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).Preload("ExaFileChunk").FirstOrCreate(&file, cfile).Error
|
||||
return file, err
|
||||
}
|
||||
cfile.IsFinish = true
|
||||
cfile.FilePath = file.FilePath
|
||||
err = global.GVA_DB.Create(&cfile).Error
|
||||
return cfile, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateFileChunk
|
||||
//@description: 创建文件切片记录
|
||||
//@param: id uint, fileChunkPath string, fileChunkNumber int
|
||||
//@return: error
|
||||
|
||||
func (e *FileUploadAndDownloadService) CreateFileChunk(id uint, fileChunkPath string, fileChunkNumber int) error {
|
||||
var chunk example.ExaFileChunk
|
||||
chunk.FileChunkPath = fileChunkPath
|
||||
chunk.ExaFileID = id
|
||||
chunk.FileChunkNumber = fileChunkNumber
|
||||
err := global.GVA_DB.Create(&chunk).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteFileChunk
|
||||
//@description: 删除文件切片记录
|
||||
//@param: fileMd5 string, fileName string, filePath string
|
||||
//@return: error
|
||||
|
||||
func (e *FileUploadAndDownloadService) DeleteFileChunk(fileMd5 string, filePath string) error {
|
||||
var chunks []example.ExaFileChunk
|
||||
var file example.ExaFile
|
||||
err := global.GVA_DB.Where("file_md5 = ?", fileMd5).First(&file).
|
||||
Updates(map[string]interface{}{
|
||||
"IsFinish": true,
|
||||
"file_path": filePath,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = global.GVA_DB.Where("exa_file_id = ?", file.ID).Delete(&chunks).Unscoped().Error
|
||||
return err
|
||||
}
|
87
service/example/exa_customer.go
Normal file
87
service/example/exa_customer.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/example"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
systemService "git.echol.cn/loser/xiecheng_server/service/system"
|
||||
)
|
||||
|
||||
type CustomerService struct{}
|
||||
|
||||
var CustomerServiceApp = new(CustomerService)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateExaCustomer
|
||||
//@description: 创建客户
|
||||
//@param: e model.ExaCustomer
|
||||
//@return: err error
|
||||
|
||||
func (exa *CustomerService) CreateExaCustomer(e example.ExaCustomer) (err error) {
|
||||
err = global.GVA_DB.Create(&e).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteFileChunk
|
||||
//@description: 删除客户
|
||||
//@param: e model.ExaCustomer
|
||||
//@return: err error
|
||||
|
||||
func (exa *CustomerService) DeleteExaCustomer(e example.ExaCustomer) (err error) {
|
||||
err = global.GVA_DB.Delete(&e).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateExaCustomer
|
||||
//@description: 更新客户
|
||||
//@param: e *model.ExaCustomer
|
||||
//@return: err error
|
||||
|
||||
func (exa *CustomerService) UpdateExaCustomer(e *example.ExaCustomer) (err error) {
|
||||
err = global.GVA_DB.Save(e).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetExaCustomer
|
||||
//@description: 获取客户信息
|
||||
//@param: id uint
|
||||
//@return: customer model.ExaCustomer, err error
|
||||
|
||||
func (exa *CustomerService) GetExaCustomer(id uint) (customer example.ExaCustomer, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", id).First(&customer).Error
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetCustomerInfoList
|
||||
//@description: 分页获取客户列表
|
||||
//@param: sysUserAuthorityID string, info request.PageInfo
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (exa *CustomerService) GetCustomerInfoList(sysUserAuthorityID uint, info request.PageInfo) (list interface{}, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
db := global.GVA_DB.Model(&example.ExaCustomer{})
|
||||
var a system.SysAuthority
|
||||
a.AuthorityId = sysUserAuthorityID
|
||||
auth, err := systemService.AuthorityServiceApp.GetAuthorityInfo(a)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var dataId []uint
|
||||
for _, v := range auth.DataAuthorityId {
|
||||
dataId = append(dataId, v.AuthorityId)
|
||||
}
|
||||
var CustomerList []example.ExaCustomer
|
||||
err = db.Where("sys_user_authority_id in ?", dataId).Count(&total).Error
|
||||
if err != nil {
|
||||
return CustomerList, total, err
|
||||
} else {
|
||||
err = db.Limit(limit).Offset(offset).Preload("SysUser").Where("sys_user_authority_id in ?", dataId).Find(&CustomerList).Error
|
||||
}
|
||||
return CustomerList, total, err
|
||||
}
|
123
service/example/exa_file_upload_download.go
Normal file
123
service/example/exa_file_upload_download.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/example"
|
||||
"git.echol.cn/loser/xiecheng_server/model/example/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils/upload"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: Upload
|
||||
//@description: 创建文件上传记录
|
||||
//@param: file model.ExaFileUploadAndDownload
|
||||
//@return: error
|
||||
|
||||
func (e *FileUploadAndDownloadService) Upload(file example.ExaFileUploadAndDownload) error {
|
||||
return global.GVA_DB.Create(&file).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: FindFile
|
||||
//@description: 查询文件记录
|
||||
//@param: id uint
|
||||
//@return: model.ExaFileUploadAndDownload, error
|
||||
|
||||
func (e *FileUploadAndDownloadService) FindFile(id uint) (example.ExaFileUploadAndDownload, error) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
err := global.GVA_DB.Where("id = ?", id).First(&file).Error
|
||||
return file, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteFile
|
||||
//@description: 删除文件记录
|
||||
//@param: file model.ExaFileUploadAndDownload
|
||||
//@return: err error
|
||||
|
||||
func (e *FileUploadAndDownloadService) DeleteFile(file example.ExaFileUploadAndDownload) (err error) {
|
||||
var fileFromDb example.ExaFileUploadAndDownload
|
||||
fileFromDb, err = e.FindFile(file.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
oss := upload.NewOss()
|
||||
if err = oss.DeleteFile(fileFromDb.Key); err != nil {
|
||||
return errors.New("文件删除失败")
|
||||
}
|
||||
err = global.GVA_DB.Where("id = ?", file.ID).Unscoped().Delete(&file).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// EditFileName 编辑文件名或者备注
|
||||
func (e *FileUploadAndDownloadService) EditFileName(file example.ExaFileUploadAndDownload) (err error) {
|
||||
var fileFromDb example.ExaFileUploadAndDownload
|
||||
return global.GVA_DB.Where("id = ?", file.ID).First(&fileFromDb).Update("name", file.Name).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetFileRecordInfoList
|
||||
//@description: 分页获取数据
|
||||
//@param: info request.ExaAttachmentCategorySearch
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.ExaAttachmentCategorySearch) (list []example.ExaFileUploadAndDownload, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
db := global.GVA_DB.Model(&example.ExaFileUploadAndDownload{})
|
||||
|
||||
if len(info.Keyword) > 0 {
|
||||
db = db.Where("name LIKE ?", "%"+info.Keyword+"%")
|
||||
}
|
||||
|
||||
if info.ClassId > 0 {
|
||||
db = db.Where("class_id = ?", info.ClassId)
|
||||
}
|
||||
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Order("id desc").Find(&list).Error
|
||||
return list, total, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UploadFile
|
||||
//@description: 根据配置文件判断是文件上传到本地或者七牛云
|
||||
//@param: header *multipart.FileHeader, noSave string
|
||||
//@return: file model.ExaFileUploadAndDownload, err error
|
||||
|
||||
func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string, classId int) (file example.ExaFileUploadAndDownload, err error) {
|
||||
oss := upload.NewOss()
|
||||
filePath, key, uploadErr := oss.UploadFile(header)
|
||||
if uploadErr != nil {
|
||||
return file, uploadErr
|
||||
}
|
||||
s := strings.Split(header.Filename, ".")
|
||||
f := example.ExaFileUploadAndDownload{
|
||||
Url: filePath,
|
||||
Name: header.Filename,
|
||||
ClassId: classId,
|
||||
Tag: s[len(s)-1],
|
||||
Key: key,
|
||||
}
|
||||
if noSave == "0" {
|
||||
return f, e.Upload(f)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: ImportURL
|
||||
//@description: 导入URL
|
||||
//@param: file model.ExaFileUploadAndDownload
|
||||
//@return: error
|
||||
|
||||
func (e *FileUploadAndDownloadService) ImportURL(file *[]example.ExaFileUploadAndDownload) error {
|
||||
return global.GVA_DB.Create(&file).Error
|
||||
}
|
217
service/system/auto_code_history.go
Normal file
217
service/system/auto_code_history.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
common "git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
model "git.echol.cn/loser/xiecheng_server/model/system"
|
||||
request "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var AutocodeHistory = new(autoCodeHistory)
|
||||
|
||||
type autoCodeHistory struct{}
|
||||
|
||||
// Create 创建代码生成器历史记录
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Create(ctx context.Context, info request.SysAutoHistoryCreate) error {
|
||||
create := info.Create()
|
||||
err := global.GVA_DB.WithContext(ctx).Create(&create).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// First 根据id获取代码生成器历史的数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) First(ctx context.Context, info common.GetById) (string, error) {
|
||||
var meta string
|
||||
err := global.GVA_DB.WithContext(ctx).Model(model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Pluck("request", &meta).Error
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "获取失败!")
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// Repeat 检测重复
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Repeat(businessDB, structName, abbreviation, Package string) bool {
|
||||
var count int64
|
||||
global.GVA_DB.Model(&model.SysAutoCodeHistory{}).Where("business_db = ? and (struct_name = ? OR abbreviation = ?) and package = ? and flag = ?", businessDB, structName, abbreviation, Package, 0).Count(&count).Debug()
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// RollBack 回滚
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) RollBack(ctx context.Context, info request.SysAutoHistoryRollBack) error {
|
||||
var history model.SysAutoCodeHistory
|
||||
err := global.GVA_DB.Where("id = ?", info.ID).First(&history).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if history.ExportTemplateID != 0 {
|
||||
err = global.GVA_DB.Delete(&model.SysExportTemplate{}, "id = ?", history.ExportTemplateID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if info.DeleteApi {
|
||||
ids := info.ApiIds(history)
|
||||
err = ApiServiceApp.DeleteApisByIds(ids)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
|
||||
}
|
||||
} // 清除API表
|
||||
if info.DeleteMenu {
|
||||
err = BaseMenuServiceApp.DeleteBaseMenu(int(history.MenuID))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除菜单失败!")
|
||||
}
|
||||
} // 清除菜单表
|
||||
if info.DeleteTable {
|
||||
err = s.DropTable(history.BusinessDB, history.Table)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除表失败!")
|
||||
}
|
||||
} // 删除表
|
||||
templates := make(map[string]string, len(history.Templates))
|
||||
for key, template := range history.Templates {
|
||||
{
|
||||
server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
keys := strings.Split(key, "/")
|
||||
key = filepath.Join(keys...)
|
||||
key = strings.TrimPrefix(key, server)
|
||||
} // key
|
||||
{
|
||||
web := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot())
|
||||
server := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)
|
||||
slices := strings.Split(template, "/")
|
||||
template = filepath.Join(slices...)
|
||||
ext := path.Ext(template)
|
||||
switch ext {
|
||||
case ".js", ".vue":
|
||||
template = filepath.Join(web, template)
|
||||
case ".go":
|
||||
template = filepath.Join(server, template)
|
||||
}
|
||||
} // value
|
||||
templates[key] = template
|
||||
}
|
||||
history.Templates = templates
|
||||
for key, value := range history.Injections {
|
||||
var injection ast.Ast
|
||||
switch key {
|
||||
case ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter:
|
||||
|
||||
case ast.TypePackageApiModuleEnter, ast.TypePackageRouterModuleEnter, ast.TypePackageServiceModuleEnter:
|
||||
var entity ast.PackageModuleEnter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePackageInitializeGorm:
|
||||
var entity ast.PackageInitializeGorm
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePackageInitializeRouter:
|
||||
var entity ast.PackageInitializeRouter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginGen:
|
||||
var entity ast.PluginGen
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginApiEnter, ast.TypePluginRouterEnter, ast.TypePluginServiceEnter:
|
||||
var entity ast.PluginEnter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginInitializeGorm:
|
||||
var entity ast.PluginInitializeGorm
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
case ast.TypePluginInitializeRouter:
|
||||
var entity ast.PluginInitializeRouter
|
||||
_ = json.Unmarshal([]byte(value), &entity)
|
||||
injection = &entity
|
||||
}
|
||||
if injection == nil {
|
||||
continue
|
||||
}
|
||||
file, _ := injection.Parse("", nil)
|
||||
if file != nil {
|
||||
_ = injection.Rollback(file)
|
||||
err = injection.Format("", nil, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("[filepath:%s]回滚注入代码成功!\n", key)
|
||||
}
|
||||
} // 清除注入代码
|
||||
removeBasePath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, "rm_file", strconv.FormatInt(int64(time.Now().Nanosecond()), 10))
|
||||
for _, value := range history.Templates {
|
||||
if !filepath.IsAbs(value) {
|
||||
continue
|
||||
}
|
||||
removePath := filepath.Join(removeBasePath, strings.TrimPrefix(value, global.GVA_CONFIG.AutoCode.Root))
|
||||
err = utils.FileMove(value, removePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[src:%s][dst:%s]文件移动失败!", value, removePath)
|
||||
}
|
||||
} // 移动文件
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{}).Where("id = ?", info.ID).Update("flag", 1).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "更新失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) Delete(ctx context.Context, info common.GetById) error {
|
||||
err := global.GVA_DB.WithContext(ctx).Where("id = ?", info.Uint()).Delete(&model.SysAutoCodeHistory{}).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetList 获取系统历史数据
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
// Author [songzhibin97](https://github.com/songzhibin97)
|
||||
func (s *autoCodeHistory) GetList(ctx context.Context, info common.PageInfo) (list []model.SysAutoCodeHistory, total int64, err error) {
|
||||
var entities []model.SysAutoCodeHistory
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&model.SysAutoCodeHistory{})
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
err = db.Scopes(info.Paginate()).Order("updated_at desc").Find(&entities).Error
|
||||
return entities, total, err
|
||||
}
|
||||
|
||||
// DropTable 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
func (s *autoCodeHistory) DropTable(BusinessDb, tableName string) error {
|
||||
if BusinessDb != "" {
|
||||
return global.MustGetGlobalDBByDBName(BusinessDb).Exec("DROP TABLE " + tableName).Error
|
||||
} else {
|
||||
return global.GVA_DB.Exec("DROP TABLE " + tableName).Error
|
||||
}
|
||||
}
|
680
service/system/auto_code_package.go
Normal file
680
service/system/auto_code_package.go
Normal file
@@ -0,0 +1,680 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
common "git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
model "git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"git.echol.cn/loser/xiecheng_server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var AutoCodePackage = new(autoCodePackage)
|
||||
|
||||
type autoCodePackage struct{}
|
||||
|
||||
// Create 创建包信息
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Create(ctx context.Context, info *request.SysAutoCodePackageCreate) error {
|
||||
switch {
|
||||
case info.Template == "":
|
||||
return errors.New("模板不能为空!")
|
||||
case info.Template == "page":
|
||||
return errors.New("page为表单生成器!")
|
||||
case info.PackageName == "":
|
||||
return errors.New("PackageName不能为空!")
|
||||
case token.IsKeyword(info.PackageName):
|
||||
return errors.Errorf("%s为go的关键字!", info.PackageName)
|
||||
case info.Template == "package":
|
||||
if info.PackageName == "system" || info.PackageName == "example" {
|
||||
return errors.New("不能使用已保留的package name")
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
if !errors.Is(global.GVA_DB.Where("package_name = ? and template = ?", info.PackageName, info.Template).First(&model.SysAutoCodePackage{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同PackageName")
|
||||
}
|
||||
create := info.Create()
|
||||
return global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Create(&create).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建失败!")
|
||||
}
|
||||
code := info.AutoCode()
|
||||
_, asts, creates, err := s.templates(ctx, create, code, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range creates { // key 为 模版绝对路径
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
err = os.MkdirAll(filepath.Dir(value), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value)
|
||||
}
|
||||
var file *os.File
|
||||
file, err = os.Create(value)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", value)
|
||||
}
|
||||
err = files.Execute(file, code)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]生成失败!", value)
|
||||
}
|
||||
fmt.Printf("[template:%s][filepath:%s]生成成功!\n", key, value)
|
||||
}
|
||||
for key, value := range asts {
|
||||
keys := strings.Split(key, "=>")
|
||||
if len(keys) == 2 {
|
||||
switch keys[1] {
|
||||
case ast.TypePluginInitializeV2, ast.TypePackageApiEnter, ast.TypePackageRouterEnter, ast.TypePackageServiceEnter:
|
||||
file, _ := value.Parse("", nil)
|
||||
if file != nil {
|
||||
err = value.Injection(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = value.Format("", nil, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Printf("[type:%s]注入成功!\n", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除包记录
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Delete(ctx context.Context, info common.GetById) error {
|
||||
err := global.GVA_DB.WithContext(ctx).Delete(&model.SysAutoCodePackage{}, info.Uint()).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "删除失败!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// All 获取所有包
|
||||
// @author: [piexlmax](https://github.com/piexlmax)
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) All(ctx context.Context) (entities []model.SysAutoCodePackage, err error) {
|
||||
server := make([]model.SysAutoCodePackage, 0)
|
||||
plugin := make([]model.SysAutoCodePackage, 0)
|
||||
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service")
|
||||
pluginPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin")
|
||||
serverDir, err := os.ReadDir(serverPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "读取service文件夹失败!")
|
||||
}
|
||||
pluginDir, err := os.ReadDir(pluginPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "读取plugin文件夹失败!")
|
||||
}
|
||||
for i := 0; i < len(serverDir); i++ {
|
||||
if serverDir[i].IsDir() {
|
||||
serverPackage := model.SysAutoCodePackage{
|
||||
PackageName: serverDir[i].Name(),
|
||||
Template: "package",
|
||||
Label: serverDir[i].Name() + "包",
|
||||
Desc: "系统自动读取" + serverDir[i].Name() + "包",
|
||||
Module: global.GVA_CONFIG.AutoCode.Module,
|
||||
}
|
||||
server = append(server, serverPackage)
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(pluginDir); i++ {
|
||||
if pluginDir[i].IsDir() {
|
||||
dirNameMap := map[string]bool{
|
||||
"api": true,
|
||||
"config": true,
|
||||
"initialize": true,
|
||||
"model": true,
|
||||
"plugin": true,
|
||||
"router": true,
|
||||
"service": true,
|
||||
}
|
||||
dir, e := os.ReadDir(filepath.Join(pluginPath, pluginDir[i].Name()))
|
||||
if e != nil {
|
||||
return nil, errors.Wrap(err, "读取plugin文件夹失败!")
|
||||
}
|
||||
//dir目录需要包含所有的dirNameMap
|
||||
for k := 0; k < len(dir); k++ {
|
||||
if dir[k].IsDir() {
|
||||
if ok := dirNameMap[dir[k].Name()]; ok {
|
||||
delete(dirNameMap, dir[k].Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(dirNameMap) != 0 {
|
||||
continue
|
||||
}
|
||||
pluginPackage := model.SysAutoCodePackage{
|
||||
PackageName: pluginDir[i].Name(),
|
||||
Template: "plugin",
|
||||
Label: pluginDir[i].Name() + "插件",
|
||||
Desc: "系统自动读取" + pluginDir[i].Name() + "插件,使用前请确认是否为v2版本插件",
|
||||
Module: global.GVA_CONFIG.AutoCode.Module,
|
||||
}
|
||||
plugin = append(plugin, pluginPackage)
|
||||
}
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Find(&entities).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "获取所有包失败!")
|
||||
}
|
||||
entitiesMap := make(map[string]model.SysAutoCodePackage)
|
||||
for i := 0; i < len(entities); i++ {
|
||||
entitiesMap[entities[i].PackageName] = entities[i]
|
||||
}
|
||||
createEntity := []model.SysAutoCodePackage{}
|
||||
for i := 0; i < len(server); i++ {
|
||||
if _, ok := entitiesMap[server[i].PackageName]; !ok {
|
||||
if server[i].Template == "package" {
|
||||
createEntity = append(createEntity, server[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(plugin); i++ {
|
||||
if _, ok := entitiesMap[plugin[i].PackageName]; !ok {
|
||||
if plugin[i].Template == "plugin" {
|
||||
createEntity = append(createEntity, plugin[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(createEntity) > 0 {
|
||||
err = global.GVA_DB.WithContext(ctx).Create(&createEntity).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "同步失败!")
|
||||
}
|
||||
entities = append(entities, createEntity...)
|
||||
}
|
||||
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
// Templates 获取所有模版文件夹
|
||||
// @author: [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodePackage) Templates(ctx context.Context) ([]string, error) {
|
||||
templates := make([]string, 0)
|
||||
entries, err := os.ReadDir("resource")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "读取模版文件夹失败!")
|
||||
}
|
||||
for i := 0; i < len(entries); i++ {
|
||||
if entries[i].IsDir() {
|
||||
if entries[i].Name() == "page" {
|
||||
continue
|
||||
} // page 为表单生成器
|
||||
if entries[i].Name() == "function" {
|
||||
continue
|
||||
} // function 为函数生成器
|
||||
if entries[i].Name() == "preview" {
|
||||
continue
|
||||
} // preview 为预览代码生成器的代码
|
||||
templates = append(templates, entries[i].Name())
|
||||
}
|
||||
}
|
||||
return templates, nil
|
||||
}
|
||||
|
||||
func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCodePackage, info request.AutoCode, isPackage bool) (code map[string]string, asts map[string]ast.Ast, creates map[string]string, err error) {
|
||||
code = make(map[string]string)
|
||||
asts = make(map[string]ast.Ast)
|
||||
creates = make(map[string]string)
|
||||
templateDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", entity.Template)
|
||||
templateDirs, err := os.ReadDir(templateDir)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", templateDir)
|
||||
}
|
||||
for i := 0; i < len(templateDirs); i++ {
|
||||
second := filepath.Join(templateDir, templateDirs[i].Name())
|
||||
switch templateDirs[i].Name() {
|
||||
case "server":
|
||||
if !info.GenerateServer && !isPackage {
|
||||
break
|
||||
}
|
||||
var secondDirs []os.DirEntry
|
||||
secondDirs, err = os.ReadDir(second)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second)
|
||||
}
|
||||
for j := 0; j < len(secondDirs); j++ {
|
||||
if secondDirs[j].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
three := filepath.Join(second, secondDirs[j].Name())
|
||||
if !secondDirs[j].IsDir() {
|
||||
ext := filepath.Ext(secondDirs[j].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", three)
|
||||
}
|
||||
name := strings.TrimSuffix(secondDirs[j].Name(), ext)
|
||||
if name == "main.go" || name == "plugin.go" {
|
||||
pluginInitialize := &ast.PluginInitializeV2{
|
||||
Type: ast.TypePluginInitializeV2,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, name),
|
||||
PluginPath: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "plugin_biz_v2.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
PackageName: entity.PackageName,
|
||||
}
|
||||
asts[pluginInitialize.PluginPath+"=>"+pluginInitialize.Type.String()] = pluginInitialize
|
||||
creates[three] = pluginInitialize.Path
|
||||
continue
|
||||
}
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three)
|
||||
}
|
||||
switch secondDirs[j].Name() {
|
||||
case "api", "router", "service":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
hasEnter := strings.Index(threeDirs[k].Name(), "enter")
|
||||
router := strings.Index(threeDirs[k].Name(), "router")
|
||||
service := strings.Index(threeDirs[k].Name(), "service")
|
||||
if router == -1 && api == -1 && service == -1 && hasEnter == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if entity.Template == "package" {
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go")
|
||||
if api != -1 {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, info.HumpPackageName+".go")
|
||||
}
|
||||
if hasEnter != -1 {
|
||||
isApi := strings.Index(secondDirs[j].Name(), "api")
|
||||
isRouter := strings.Index(secondDirs[j].Name(), "router")
|
||||
isService := strings.Index(secondDirs[j].Name(), "service")
|
||||
if isApi != -1 {
|
||||
packageApiEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, "api", "v1", entity.PackageName),
|
||||
StructName: utils.FirstUpper(entity.PackageName) + "ApiGroup",
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "ApiGroup",
|
||||
}
|
||||
asts[packageApiEnter.Path+"=>"+packageApiEnter.Type.String()] = packageApiEnter
|
||||
packageApiModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageApiModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "v1", entity.PackageName, "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/service"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
StructName: info.StructName + "Api",
|
||||
AppName: "ServiceGroupApp",
|
||||
GroupName: utils.FirstUpper(entity.PackageName) + "ServiceGroup",
|
||||
ModuleName: info.Abbreviation + "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: info.StructName + "Service",
|
||||
}
|
||||
asts[packageApiModuleEnter.Path+"=>"+packageApiModuleEnter.Type.String()] = packageApiModuleEnter
|
||||
creates[four] = packageApiModuleEnter.Path
|
||||
}
|
||||
if isRouter != -1 {
|
||||
packageRouterEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/%s/%s"`, global.GVA_CONFIG.AutoCode.Module, secondDirs[j].Name(), entity.PackageName),
|
||||
StructName: utils.FirstUpper(entity.PackageName),
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "RouterGroup",
|
||||
}
|
||||
asts[packageRouterEnter.Path+"=>"+packageRouterEnter.Type.String()] = packageRouterEnter
|
||||
packageRouterModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageRouterModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"),
|
||||
ImportPath: fmt.Sprintf(`api "%s/api/v1"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
StructName: info.StructName + "Router",
|
||||
AppName: "ApiGroupApp",
|
||||
GroupName: utils.FirstUpper(entity.PackageName) + "ApiGroup",
|
||||
ModuleName: info.Abbreviation + "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: info.StructName + "Api",
|
||||
}
|
||||
creates[four] = packageRouterModuleEnter.Path
|
||||
asts[packageRouterModuleEnter.Path+"=>"+packageRouterModuleEnter.Type.String()] = packageRouterModuleEnter
|
||||
packageInitializeRouter := &ast.PackageInitializeRouter{
|
||||
Type: ast.TypePackageInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "router_biz.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/router"`, global.GVA_CONFIG.AutoCode.Module),
|
||||
AppName: "RouterGroupApp",
|
||||
GroupName: utils.FirstUpper(entity.PackageName),
|
||||
ModuleName: entity.PackageName + "Router",
|
||||
PackageName: "router",
|
||||
FunctionName: "Init" + info.StructName + "Router",
|
||||
LeftRouterGroupName: "privateGroup",
|
||||
RightRouterGroupName: "publicGroup",
|
||||
}
|
||||
asts[packageInitializeRouter.Path+"=>"+packageInitializeRouter.Type.String()] = packageInitializeRouter
|
||||
}
|
||||
if isService != -1 {
|
||||
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext))
|
||||
importPath := fmt.Sprintf(`"%s/service/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName)
|
||||
packageServiceEnter := &ast.PackageEnter{
|
||||
Type: ast.TypePackageServiceEnter,
|
||||
Path: path,
|
||||
ImportPath: importPath,
|
||||
StructName: utils.FirstUpper(entity.PackageName) + "ServiceGroup",
|
||||
PackageName: entity.PackageName,
|
||||
PackageStructName: "ServiceGroup",
|
||||
}
|
||||
asts[packageServiceEnter.Path+"=>"+packageServiceEnter.Type.String()] = packageServiceEnter
|
||||
packageServiceModuleEnter := &ast.PackageModuleEnter{
|
||||
Type: ast.TypePackageServiceModuleEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, "enter.go"),
|
||||
StructName: info.StructName + "Service",
|
||||
}
|
||||
asts[packageServiceModuleEnter.Path+"=>"+packageServiceModuleEnter.Type.String()] = packageServiceModuleEnter
|
||||
creates[four] = packageServiceModuleEnter.Path
|
||||
}
|
||||
continue
|
||||
}
|
||||
code[four] = create
|
||||
continue
|
||||
}
|
||||
if hasEnter != -1 {
|
||||
isApi := strings.Index(secondDirs[j].Name(), "api")
|
||||
isRouter := strings.Index(secondDirs[j].Name(), "router")
|
||||
isService := strings.Index(secondDirs[j].Name(), "service")
|
||||
if isRouter != -1 {
|
||||
pluginRouterEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginRouterEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/api"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
ModuleName: "api" + info.StructName,
|
||||
GroupName: "Api",
|
||||
PackageName: "api",
|
||||
ServiceName: info.StructName,
|
||||
}
|
||||
asts[pluginRouterEnter.Path+"=>"+pluginRouterEnter.Type.String()] = pluginRouterEnter
|
||||
creates[four] = pluginRouterEnter.Path
|
||||
}
|
||||
if isApi != -1 {
|
||||
pluginApiEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginApiEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/service"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
ModuleName: "service" + info.StructName,
|
||||
GroupName: "Service",
|
||||
PackageName: "service",
|
||||
ServiceName: info.StructName,
|
||||
}
|
||||
asts[pluginApiEnter.Path+"=>"+pluginApiEnter.Type.String()] = pluginApiEnter
|
||||
creates[four] = pluginApiEnter.Path
|
||||
}
|
||||
if isService != -1 {
|
||||
pluginServiceEnter := &ast.PluginEnter{
|
||||
Type: ast.TypePluginServiceEnter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
StructName: info.StructName,
|
||||
StructCamelName: info.Abbreviation,
|
||||
}
|
||||
asts[pluginServiceEnter.Path+"=>"+pluginServiceEnter.Type.String()] = pluginServiceEnter
|
||||
creates[four] = pluginServiceEnter.Path
|
||||
}
|
||||
continue
|
||||
} // enter.go
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go")
|
||||
code[four] = create
|
||||
}
|
||||
case "gen", "config", "initialize", "plugin", "response":
|
||||
if entity.Template == "package" {
|
||||
continue
|
||||
} // package模板不需要生成gen, config, initialize
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
gen := strings.Index(threeDirs[k].Name(), "gen")
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
menu := strings.Index(threeDirs[k].Name(), "menu")
|
||||
viper := strings.Index(threeDirs[k].Name(), "viper")
|
||||
plugin := strings.Index(threeDirs[k].Name(), "plugin")
|
||||
config := strings.Index(threeDirs[k].Name(), "config")
|
||||
router := strings.Index(threeDirs[k].Name(), "router")
|
||||
hasGorm := strings.Index(threeDirs[k].Name(), "gorm")
|
||||
response := strings.Index(threeDirs[k].Name(), "response")
|
||||
if gen != -1 && api != -1 && menu != -1 && viper != -1 && plugin != -1 && config != -1 && router != -1 && hasGorm != -1 && response != -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if api != -1 || menu != -1 || viper != -1 || response != -1 || plugin != -1 || config != -1 {
|
||||
creates[four] = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext))
|
||||
}
|
||||
if gen != -1 {
|
||||
pluginGen := &ast.PluginGen{
|
||||
Type: ast.TypePluginGen,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
}
|
||||
asts[pluginGen.Path+"=>"+pluginGen.Type.String()] = pluginGen
|
||||
creates[four] = pluginGen.Path
|
||||
}
|
||||
if hasGorm != -1 {
|
||||
pluginInitializeGorm := &ast.PluginInitializeGorm{
|
||||
Type: ast.TypePluginInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/model"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
StructName: info.StructName,
|
||||
PackageName: "model",
|
||||
IsNew: true,
|
||||
}
|
||||
asts[pluginInitializeGorm.Path+"=>"+pluginInitializeGorm.Type.String()] = pluginInitializeGorm
|
||||
creates[four] = pluginInitializeGorm.Path
|
||||
}
|
||||
if router != -1 {
|
||||
pluginInitializeRouter := &ast.PluginInitializeRouter{
|
||||
Type: ast.TypePluginInitializeRouter,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), strings.TrimSuffix(threeDirs[k].Name(), ext)),
|
||||
ImportPath: fmt.Sprintf(`"%s/plugin/%s/router"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
AppName: "Router",
|
||||
GroupName: info.StructName,
|
||||
PackageName: "router",
|
||||
FunctionName: "Init",
|
||||
LeftRouterGroupName: "public",
|
||||
RightRouterGroupName: "private",
|
||||
}
|
||||
asts[pluginInitializeRouter.Path+"=>"+pluginInitializeRouter.Type.String()] = pluginInitializeRouter
|
||||
creates[four] = pluginInitializeRouter.Path
|
||||
}
|
||||
}
|
||||
case "model":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
var fourDirs []os.DirEntry
|
||||
fourDirs, err = os.ReadDir(four)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", four)
|
||||
}
|
||||
for l := 0; l < len(fourDirs); l++ {
|
||||
if fourDirs[l].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
five := filepath.Join(four, fourDirs[l].Name())
|
||||
if fourDirs[l].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", five)
|
||||
}
|
||||
ext := filepath.Ext(five)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", five)
|
||||
}
|
||||
hasRequest := strings.Index(fourDirs[l].Name(), "request")
|
||||
if hasRequest == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", five)
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), threeDirs[k].Name(), info.HumpPackageName+".go")
|
||||
if entity.Template == "package" {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, threeDirs[k].Name(), info.HumpPackageName+".go")
|
||||
}
|
||||
code[five] = create
|
||||
}
|
||||
continue
|
||||
}
|
||||
ext := filepath.Ext(threeDirs[k].Name())
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
hasModel := strings.Index(threeDirs[k].Name(), "model")
|
||||
if hasModel == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", entity.PackageName, secondDirs[j].Name(), info.HumpPackageName+".go")
|
||||
if entity.Template == "package" {
|
||||
packageInitializeGorm := &ast.PackageInitializeGorm{
|
||||
Type: ast.TypePackageInitializeGorm,
|
||||
Path: filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "initialize", "gorm_biz.go"),
|
||||
ImportPath: fmt.Sprintf(`"%s/model/%s"`, global.GVA_CONFIG.AutoCode.Module, entity.PackageName),
|
||||
Business: info.BusinessDB,
|
||||
StructName: info.StructName,
|
||||
PackageName: entity.PackageName,
|
||||
IsNew: true,
|
||||
}
|
||||
code[four] = packageInitializeGorm.Path
|
||||
asts[packageInitializeGorm.Path+"=>"+packageInitializeGorm.Type.String()] = packageInitializeGorm
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, secondDirs[j].Name(), entity.PackageName, info.HumpPackageName+".go")
|
||||
}
|
||||
code[four] = create
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three)
|
||||
}
|
||||
}
|
||||
case "web":
|
||||
if !info.GenerateWeb && !isPackage {
|
||||
break
|
||||
}
|
||||
var secondDirs []os.DirEntry
|
||||
secondDirs, err = os.ReadDir(second)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", second)
|
||||
}
|
||||
for j := 0; j < len(secondDirs); j++ {
|
||||
if secondDirs[j].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
three := filepath.Join(second, secondDirs[j].Name())
|
||||
if !secondDirs[j].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", three)
|
||||
}
|
||||
switch secondDirs[j].Name() {
|
||||
case "api", "form", "view", "table":
|
||||
var threeDirs []os.DirEntry
|
||||
threeDirs, err = os.ReadDir(three)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "读取模版文件夹[%s]失败!", three)
|
||||
}
|
||||
for k := 0; k < len(threeDirs); k++ {
|
||||
if threeDirs[k].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
four := filepath.Join(three, threeDirs[k].Name())
|
||||
if threeDirs[k].IsDir() {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", four)
|
||||
}
|
||||
ext := filepath.Ext(four)
|
||||
if ext != ".template" && ext != ".tpl" {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版后缀!", four)
|
||||
}
|
||||
api := strings.Index(threeDirs[k].Name(), "api")
|
||||
form := strings.Index(threeDirs[k].Name(), "form")
|
||||
view := strings.Index(threeDirs[k].Name(), "view")
|
||||
table := strings.Index(threeDirs[k].Name(), "table")
|
||||
if api == -1 && form == -1 && view == -1 && table == -1 {
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", four)
|
||||
}
|
||||
if entity.Template == "package" {
|
||||
if view != -1 || table != -1 {
|
||||
formPath := filepath.Join(three, "form.vue"+ext)
|
||||
value, ok := code[formPath]
|
||||
if ok {
|
||||
value = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+"Form"+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
code[formPath] = value
|
||||
}
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
if api != -1 {
|
||||
create = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), secondDirs[j].Name(), entity.PackageName, info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
}
|
||||
code[four] = create
|
||||
continue
|
||||
}
|
||||
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
|
||||
code[four] = create
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件夹!", three)
|
||||
}
|
||||
}
|
||||
case "readme.txt.tpl", "readme.txt.template":
|
||||
continue
|
||||
default:
|
||||
if templateDirs[i].Name() == ".DS_Store" {
|
||||
continue
|
||||
}
|
||||
return nil, nil, nil, errors.Errorf("[filpath:%s]非法模版文件!", second)
|
||||
}
|
||||
}
|
||||
return code, asts, creates, nil
|
||||
}
|
105
service/system/auto_code_package_test.go
Normal file
105
service/system/auto_code_package_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
model "git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_autoCodePackage_Create(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info *request.SysAutoCodePackageCreate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 package",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: &request.SysAutoCodePackageCreate{
|
||||
Template: "package",
|
||||
PackageName: "gva",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 plugin",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: &request.SysAutoCodePackageCreate{
|
||||
Template: "plugin",
|
||||
PackageName: "gva",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &autoCodePackage{}
|
||||
if err := a.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_autoCodePackage_templates(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
entity model.SysAutoCodePackage
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantCode map[string]string
|
||||
wantEnter map[string]map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试1",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
entity: model.SysAutoCodePackage{
|
||||
Desc: "描述",
|
||||
Label: "展示名",
|
||||
Template: "plugin",
|
||||
PackageName: "preview",
|
||||
},
|
||||
info: request.AutoCode{
|
||||
Abbreviation: "user",
|
||||
HumpPackageName: "user",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
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)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("templates() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
for key, value := range gotCode {
|
||||
t.Logf("\n")
|
||||
t.Logf(key)
|
||||
t.Logf(value)
|
||||
t.Logf("\n")
|
||||
}
|
||||
t.Log(gotCreates)
|
||||
if !reflect.DeepEqual(gotEnter, tt.wantEnter) {
|
||||
t.Errorf("templates() gotEnter = %v, want %v", gotEnter, tt.wantEnter)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
249
service/system/auto_code_plugin.go
Normal file
249
service/system/auto_code_plugin.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"git.echol.cn/loser/xiecheng_server/utils/ast"
|
||||
"github.com/mholt/archiver/v4"
|
||||
cp "github.com/otiai10/copy"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var AutoCodePlugin = new(autoCodePlugin)
|
||||
|
||||
type autoCodePlugin struct{}
|
||||
|
||||
// Install 插件安装
|
||||
func (s *autoCodePlugin) Install(file *multipart.FileHeader) (web, server int, err error) {
|
||||
const GVAPLUGPINATH = "./gva-plug-temp/"
|
||||
defer os.RemoveAll(GVAPLUGPINATH)
|
||||
_, err = os.Stat(GVAPLUGPINATH)
|
||||
if os.IsNotExist(err) {
|
||||
os.Mkdir(GVAPLUGPINATH, os.ModePerm)
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
out, err := os.Create(GVAPLUGPINATH + file.Filename)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, src)
|
||||
|
||||
paths, err := utils.Unzip(GVAPLUGPINATH+file.Filename, GVAPLUGPINATH)
|
||||
paths = filterFile(paths)
|
||||
var webIndex = -1
|
||||
var serverIndex = -1
|
||||
webPlugin := ""
|
||||
serverPlugin := ""
|
||||
|
||||
for i := range paths {
|
||||
paths[i] = filepath.ToSlash(paths[i])
|
||||
pathArr := strings.Split(paths[i], "/")
|
||||
ln := len(pathArr)
|
||||
|
||||
if ln < 4 {
|
||||
continue
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `server/plugin` && len(serverPlugin) == 0 {
|
||||
serverPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
if pathArr[2]+"/"+pathArr[3] == `web/plugin` && len(webPlugin) == 0 {
|
||||
webPlugin = filepath.Join(pathArr[0], pathArr[1], pathArr[2], pathArr[3])
|
||||
}
|
||||
}
|
||||
if len(serverPlugin) == 0 && len(webPlugin) == 0 {
|
||||
zap.L().Error("非标准插件,请按照文档自动迁移使用")
|
||||
return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用")
|
||||
}
|
||||
|
||||
if len(serverPlugin) != 0 {
|
||||
err = installation(serverPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(webPlugin) != 0 {
|
||||
err = installation(webPlugin, global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web)
|
||||
if err != nil {
|
||||
return webIndex, serverIndex, err
|
||||
}
|
||||
}
|
||||
|
||||
return 1, 1, err
|
||||
}
|
||||
|
||||
func installation(path string, formPath string, toPath string) error {
|
||||
arr := strings.Split(filepath.ToSlash(path), "/")
|
||||
ln := len(arr)
|
||||
if ln < 3 {
|
||||
return errors.New("arr")
|
||||
}
|
||||
name := arr[ln-3]
|
||||
|
||||
var form = filepath.Join(global.GVA_CONFIG.AutoCode.Root, formPath, path)
|
||||
var to = filepath.Join(global.GVA_CONFIG.AutoCode.Root, toPath, "plugin")
|
||||
_, err := os.Stat(to + name)
|
||||
if err == nil {
|
||||
zap.L().Error("autoPath 已存在同名插件,请自行手动安装", zap.String("to", to))
|
||||
return errors.New(toPath + "已存在同名插件,请自行手动安装")
|
||||
}
|
||||
return cp.Copy(form, to, cp.Options{Skip: skipMacSpecialDocument})
|
||||
}
|
||||
|
||||
func filterFile(paths []string) []string {
|
||||
np := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
if ok, _ := skipMacSpecialDocument(nil, path, ""); ok {
|
||||
continue
|
||||
}
|
||||
np = append(np, path)
|
||||
}
|
||||
return np
|
||||
}
|
||||
|
||||
func skipMacSpecialDocument(_ os.FileInfo, src, _ string) (bool, error) {
|
||||
if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) {
|
||||
if plugName == "" {
|
||||
return "", errors.New("插件名称不能为空")
|
||||
}
|
||||
|
||||
// 防止路径穿越
|
||||
plugName = filepath.Clean(plugName)
|
||||
|
||||
webPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", plugName)
|
||||
serverPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", plugName)
|
||||
// 创建一个新的zip文件
|
||||
|
||||
// 判断目录是否存在
|
||||
_, err = os.Stat(webPath)
|
||||
if err != nil {
|
||||
return "", errors.New("web路径不存在")
|
||||
}
|
||||
_, err = os.Stat(serverPath)
|
||||
if err != nil {
|
||||
return "", errors.New("server路径不存在")
|
||||
}
|
||||
|
||||
fileName := plugName + ".zip"
|
||||
// 创建一个新的zip文件
|
||||
files, err := archiver.FilesFromDisk(nil, map[string]string{
|
||||
webPath: plugName + "/web/plugin/" + plugName,
|
||||
serverPath: plugName + "/server/plugin/" + plugName,
|
||||
})
|
||||
|
||||
// create the output file we'll write to
|
||||
out, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// 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{},
|
||||
}
|
||||
|
||||
// create the archive
|
||||
err = format.Archive(context.Background(), out, files)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
|
||||
}
|
||||
|
||||
func (s *autoCodePlugin) InitMenu(menuInfo request.InitMenu) (err error) {
|
||||
menuPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", menuInfo.PlugName, "initialize", "menu.go")
|
||||
src, err := os.ReadFile(menuPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||||
arrayAst := ast.FindArray(astFile, "model", "SysBaseMenu")
|
||||
var menus []system.SysBaseMenu
|
||||
|
||||
parentMenu := []system.SysBaseMenu{
|
||||
{
|
||||
ParentId: 0,
|
||||
Path: menuInfo.PlugName + "Menu",
|
||||
Name: menuInfo.PlugName + "Menu",
|
||||
Hidden: false,
|
||||
Component: "view/routerHolder.vue",
|
||||
Sort: 0,
|
||||
Meta: system.Meta{
|
||||
Title: menuInfo.ParentMenu,
|
||||
Icon: "school",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Find(&menus, "id in (?)", menuInfo.Menus).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
menus = append(parentMenu, menus...)
|
||||
menuExpr := ast.CreateMenuStructAst(menus)
|
||||
arrayAst.Elts = *menuExpr
|
||||
|
||||
var out []byte
|
||||
bf := bytes.NewBuffer(out)
|
||||
printer.Fprint(bf, fileSet, astFile)
|
||||
|
||||
os.WriteFile(menuPath, bf.Bytes(), 0666)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *autoCodePlugin) InitAPI(apiInfo request.InitApi) (err error) {
|
||||
apiPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", apiInfo.PlugName, "initialize", "api.go")
|
||||
src, err := os.ReadFile(apiPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||||
arrayAst := ast.FindArray(astFile, "model", "SysApi")
|
||||
var apis []system.SysApi
|
||||
err = global.GVA_DB.Find(&apis, "id in (?)", apiInfo.APIs).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apisExpr := ast.CreateApiStructAst(apis)
|
||||
arrayAst.Elts = *apisExpr
|
||||
|
||||
var out []byte
|
||||
bf := bytes.NewBuffer(out)
|
||||
printer.Fprint(bf, fileSet, astFile)
|
||||
|
||||
os.WriteFile(apiPath, bf.Bytes(), 0666)
|
||||
return nil
|
||||
}
|
452
service/system/auto_code_template.go
Normal file
452
service/system/auto_code_template.go
Normal file
@@ -0,0 +1,452 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
model "git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
utilsAst "git.echol.cn/loser/xiecheng_server/utils/ast"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var AutoCodeTemplate = new(autoCodeTemplate)
|
||||
|
||||
type autoCodeTemplate struct{}
|
||||
|
||||
func (s *autoCodeTemplate) checkPackage(Pkg string, template string) (err error) {
|
||||
switch template {
|
||||
case "package":
|
||||
apiEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", Pkg, "enter.go")
|
||||
_, err = os.Stat(apiEnter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("package结构异常,缺少api/v1/%s/enter.go", Pkg)
|
||||
}
|
||||
serviceEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", Pkg, "enter.go")
|
||||
_, err = os.Stat(serviceEnter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("package结构异常,缺少service/%s/enter.go", Pkg)
|
||||
}
|
||||
routerEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", Pkg, "enter.go")
|
||||
_, err = os.Stat(routerEnter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("package结构异常,缺少router/%s/enter.go", Pkg)
|
||||
}
|
||||
case "plugin":
|
||||
pluginEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", Pkg, "plugin.go")
|
||||
_, err = os.Stat(pluginEnter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("plugin结构异常,缺少plugin/%s/plugin.go", Pkg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create 创建生成自动化代码
|
||||
func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error {
|
||||
history := info.History()
|
||||
var autoPkg model.SysAutoCodePackage
|
||||
err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&autoPkg).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "查询包失败!")
|
||||
}
|
||||
err = s.checkPackage(info.Package, autoPkg.Template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 增加判断: 重复创建struct 或者重复的简称
|
||||
if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Abbreviation, info.Package) {
|
||||
return errors.New("已经创建过此数据结构,请勿重复创建!")
|
||||
}
|
||||
|
||||
generate, templates, injections, err := s.generate(ctx, info, autoPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, builder := range generate {
|
||||
err = os.MkdirAll(filepath.Dir(key), os.ModePerm)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]创建文件夹失败!", key)
|
||||
}
|
||||
err = os.WriteFile(key, []byte(builder.String()), 0666)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]写入文件失败!", key)
|
||||
}
|
||||
}
|
||||
|
||||
// 自动创建api
|
||||
if info.AutoCreateApiToSql && !info.OnlyTemplate {
|
||||
apis := info.Apis()
|
||||
err := global.GVA_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for _, v := range apis {
|
||||
var api model.SysApi
|
||||
var id uint
|
||||
err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
|
||||
return err
|
||||
}
|
||||
id = v.ID
|
||||
} else {
|
||||
id = api.ID
|
||||
}
|
||||
history.ApiIDs = append(history.ApiIDs, id)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 自动创建menu
|
||||
if info.AutoCreateMenuToSql {
|
||||
var entity model.SysBaseMenu
|
||||
var id uint
|
||||
err := global.GVA_DB.WithContext(ctx).First(&entity, "name = ?", info.Abbreviation).Error
|
||||
if err == nil {
|
||||
id = entity.ID
|
||||
} else {
|
||||
entity = info.Menu(autoPkg.Template)
|
||||
if info.AutoCreateBtnAuth && !info.OnlyTemplate {
|
||||
entity.MenuBtn = []model.SysBaseMenuBtn{
|
||||
{SysBaseMenuID: entity.ID, Name: "add", Desc: "新增"},
|
||||
{SysBaseMenuID: entity.ID, Name: "batchDelete", Desc: "批量删除"},
|
||||
{SysBaseMenuID: entity.ID, Name: "delete", Desc: "删除"},
|
||||
{SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"},
|
||||
{SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"},
|
||||
}
|
||||
if info.HasExcel {
|
||||
excelBtn := []model.SysBaseMenuBtn{
|
||||
{SysBaseMenuID: entity.ID, Name: "exportTemplate", Desc: "导出模板"},
|
||||
{SysBaseMenuID: entity.ID, Name: "exportExcel", Desc: "导出Excel"},
|
||||
{SysBaseMenuID: entity.ID, Name: "importExcel", Desc: "导入Excel"},
|
||||
}
|
||||
entity.MenuBtn = append(entity.MenuBtn, excelBtn...)
|
||||
}
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Create(&entity).Error
|
||||
id = entity.ID
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "创建菜单失败!")
|
||||
}
|
||||
}
|
||||
history.MenuID = id
|
||||
}
|
||||
|
||||
if info.HasExcel {
|
||||
dbName := info.BusinessDB
|
||||
name := info.Package + "_" + info.StructName
|
||||
tableName := info.TableName
|
||||
fieldsMap := make(map[string]string, len(info.Fields))
|
||||
for _, field := range info.Fields {
|
||||
if field.Excel {
|
||||
fieldsMap[field.ColumnName] = field.FieldDesc
|
||||
}
|
||||
}
|
||||
templateInfo, _ := json.Marshal(fieldsMap)
|
||||
sysExportTemplate := model.SysExportTemplate{
|
||||
DBName: dbName,
|
||||
Name: name,
|
||||
TableName: tableName,
|
||||
TemplateID: name,
|
||||
TemplateInfo: string(templateInfo),
|
||||
}
|
||||
err = SysExportTemplateServiceApp.CreateSysExportTemplate(&sysExportTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
history.ExportTemplateID = sysExportTemplate.ID
|
||||
}
|
||||
|
||||
// 创建历史记录
|
||||
history.Templates = templates
|
||||
history.Injections = make(map[string]string, len(injections))
|
||||
for key, value := range injections {
|
||||
bytes, _ := json.Marshal(value)
|
||||
history.Injections[key] = string(bytes)
|
||||
}
|
||||
err = AutocodeHistory.Create(ctx, history)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preview 预览自动化代码
|
||||
func (s *autoCodeTemplate) Preview(ctx context.Context, info request.AutoCode) (map[string]string, error) {
|
||||
var entity model.SysAutoCodePackage
|
||||
err := global.GVA_DB.WithContext(ctx).Where("package_name = ?", info.Package).First(&entity).Error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "查询包失败!")
|
||||
}
|
||||
// 增加判断: 重复创建struct 或者重复的简称
|
||||
if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Abbreviation, info.Package) && !info.IsAdd {
|
||||
return nil, errors.New("已经创建过此数据结构或重复简称,请勿重复创建!")
|
||||
}
|
||||
|
||||
preview := make(map[string]string)
|
||||
codes, _, _, err := s.generate(ctx, info, entity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, writer := range codes {
|
||||
if len(key) > len(global.GVA_CONFIG.AutoCode.Root) {
|
||||
key, _ = filepath.Rel(global.GVA_CONFIG.AutoCode.Root, key)
|
||||
}
|
||||
// 获取key的后缀 取消.
|
||||
suffix := filepath.Ext(key)[1:]
|
||||
var builder strings.Builder
|
||||
builder.WriteString("```" + suffix + "\n\n")
|
||||
builder.WriteString(writer.String())
|
||||
builder.WriteString("\n\n```")
|
||||
preview[key] = builder.String()
|
||||
}
|
||||
return preview, nil
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) generate(ctx context.Context, info request.AutoCode, entity model.SysAutoCodePackage) (map[string]strings.Builder, map[string]string, map[string]utilsAst.Ast, error) {
|
||||
templates, asts, _, err := AutoCodePackage.templates(ctx, entity, info, false)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
code := make(map[string]strings.Builder)
|
||||
for key, create := range templates {
|
||||
var files *template.Template
|
||||
files, err = template.ParseFiles(key)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]读取模版文件失败!", key)
|
||||
}
|
||||
var builder strings.Builder
|
||||
err = files.Execute(&builder, info)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "[filpath:%s]生成文件失败!", create)
|
||||
}
|
||||
code[create] = builder
|
||||
} // 生成文件
|
||||
injections := make(map[string]utilsAst.Ast, len(asts))
|
||||
for key, value := range asts {
|
||||
keys := strings.Split(key, "=>")
|
||||
if len(keys) == 2 {
|
||||
if keys[1] == utilsAst.TypePluginInitializeV2 {
|
||||
continue
|
||||
}
|
||||
if info.OnlyTemplate {
|
||||
if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !info.AutoMigrate {
|
||||
if keys[1] == utilsAst.TypePackageInitializeGorm || keys[1] == utilsAst.TypePluginInitializeGorm {
|
||||
continue
|
||||
}
|
||||
}
|
||||
var builder strings.Builder
|
||||
parse, _ := value.Parse("", &builder)
|
||||
if parse != nil {
|
||||
_ = value.Injection(parse)
|
||||
err = value.Format("", &builder, parse)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
code[keys[0]] = builder
|
||||
injections[keys[1]] = value
|
||||
fmt.Println(keys[0], "注入成功!")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 注入代码
|
||||
return code, templates, injections, nil
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error {
|
||||
autoPkg := model.SysAutoCodePackage{}
|
||||
err := global.GVA_DB.First(&autoPkg, "package_name = ?", info.Package).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if autoPkg.Template != "package" {
|
||||
info.IsPlugin = true
|
||||
}
|
||||
err = s.addTemplateToFile("api.go", info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.addTemplateToFile("server.go", info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.addTemplateToFile("api.js", info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.addTemplateToAst("router", info)
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) GetApiAndServer(info request.AutoFunc) (map[string]string, error) {
|
||||
autoPkg := model.SysAutoCodePackage{}
|
||||
err := global.GVA_DB.First(&autoPkg, "package_name = ?", info.Package).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if autoPkg.Template != "package" {
|
||||
info.IsPlugin = true
|
||||
}
|
||||
|
||||
apiStr, err := s.getTemplateStr("api.go", info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverStr, err := s.getTemplateStr("server.go", info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsStr, err := s.getTemplateStr("api.js", info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]string{"api": apiStr, "server": serverStr, "js": jsStr}, nil
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath)
|
||||
}
|
||||
var builder strings.Builder
|
||||
err = files.Execute(&builder, info)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return "", errors.Wrapf(err, "[filpath:%s]生成文件失败!", tempPath)
|
||||
}
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) addTemplateToAst(t string, info request.AutoFunc) error {
|
||||
tPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", info.Package, info.HumpPackageName+".go")
|
||||
funcName := fmt.Sprintf("Init%sRouter", info.StructName)
|
||||
|
||||
routerStr := "RouterWithoutAuth"
|
||||
if info.IsAuth {
|
||||
routerStr = "Router"
|
||||
}
|
||||
|
||||
stmtStr := fmt.Sprintf("%s%s.%s(\"%s\", %sApi.%s)", info.Abbreviation, routerStr, info.Method, info.Router, info.Abbreviation, info.FuncName)
|
||||
if info.IsPlugin {
|
||||
tPath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "router", info.HumpPackageName+".go")
|
||||
stmtStr = fmt.Sprintf("group.%s(\"%s\", api%s.%s)", info.Method, info.Router, info.StructName, info.FuncName)
|
||||
funcName = "Init"
|
||||
}
|
||||
|
||||
src, err := os.ReadFile(tPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
funcDecl := utilsAst.FindFunction(astFile, funcName)
|
||||
stmtNode := utilsAst.CreateStmt(stmtStr)
|
||||
|
||||
if info.IsAuth {
|
||||
for i := 0; i < len(funcDecl.Body.List); i++ {
|
||||
st := funcDecl.Body.List[i]
|
||||
// 使用类型断言来检查stmt是否是一个块语句
|
||||
if blockStmt, ok := st.(*ast.BlockStmt); ok {
|
||||
// 如果是,插入代码 跳出
|
||||
blockStmt.List = append(blockStmt.List, stmtNode)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := len(funcDecl.Body.List) - 1; i >= 0; i-- {
|
||||
st := funcDecl.Body.List[i]
|
||||
// 使用类型断言来检查stmt是否是一个块语句
|
||||
if blockStmt, ok := st.(*ast.BlockStmt); ok {
|
||||
// 如果是,插入代码 跳出
|
||||
blockStmt.List = append(blockStmt.List, stmtNode)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建一个新的文件
|
||||
f, err := os.Create(tPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := format.Node(f, fileSet, astFile); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *autoCodeTemplate) addTemplateToFile(t string, info request.AutoFunc) error {
|
||||
getTemplateStr, err := s.getTemplateStr(t, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var target string
|
||||
|
||||
switch t {
|
||||
case "api.go":
|
||||
if info.IsAi && info.ApiFunc != "" {
|
||||
getTemplateStr = info.ApiFunc
|
||||
}
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", info.Package, info.HumpPackageName+".go")
|
||||
case "server.go":
|
||||
if info.IsAi && info.ServerFunc != "" {
|
||||
getTemplateStr = info.ServerFunc
|
||||
}
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", info.Package, info.HumpPackageName+".go")
|
||||
case "api.js":
|
||||
if info.IsAi && info.JsFunc != "" {
|
||||
getTemplateStr = info.JsFunc
|
||||
}
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "api", info.Package, info.PackageName+".js")
|
||||
}
|
||||
if info.IsPlugin {
|
||||
switch t {
|
||||
case "api.go":
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "api", info.HumpPackageName+".go")
|
||||
case "server.go":
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "service", info.HumpPackageName+".go")
|
||||
case "api.js":
|
||||
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", info.Package, "api", info.PackageName+".js")
|
||||
}
|
||||
}
|
||||
|
||||
// 打开文件,如果不存在则返回错误
|
||||
file, err := os.OpenFile(target, os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 写入内容
|
||||
_, err = fmt.Fprintln(file, getTemplateStr)
|
||||
if err != nil {
|
||||
fmt.Printf("写入文件失败: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
84
service/system/auto_code_template_test.go
Normal file
84
service/system/auto_code_template_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_autoCodeTemplate_Create(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &autoCodeTemplate{}
|
||||
if err := s.Create(tt.args.ctx, tt.args.info); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_autoCodeTemplate_Preview(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
info request.AutoCode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "测试 package",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: request.AutoCode{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "测试 plugin",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
info: request.AutoCode{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testJson := `{"structName":"SysUser","tableName":"sys_users","packageName":"sysUsers","package":"gva","abbreviation":"sysUsers","description":"sysUsers表","businessDB":"","autoCreateApiToSql":true,"autoCreateMenuToSql":true,"autoMigrate":true,"gvaModel":true,"autoCreateResource":false,"fields":[{"fieldName":"Uuid","fieldDesc":"用户UUID","fieldType":"string","dataType":"varchar","fieldJson":"uuid","primaryKey":false,"dataTypeLong":"191","columnName":"uuid","comment":"用户UUID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Username","fieldDesc":"用户登录名","fieldType":"string","dataType":"varchar","fieldJson":"username","primaryKey":false,"dataTypeLong":"191","columnName":"username","comment":"用户登录名","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Password","fieldDesc":"用户登录密码","fieldType":"string","dataType":"varchar","fieldJson":"password","primaryKey":false,"dataTypeLong":"191","columnName":"password","comment":"用户登录密码","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"NickName","fieldDesc":"用户昵称","fieldType":"string","dataType":"varchar","fieldJson":"nickName","primaryKey":false,"dataTypeLong":"191","columnName":"nick_name","comment":"用户昵称","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"SideMode","fieldDesc":"用户侧边主题","fieldType":"string","dataType":"varchar","fieldJson":"sideMode","primaryKey":false,"dataTypeLong":"191","columnName":"side_mode","comment":"用户侧边主题","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"HeaderImg","fieldDesc":"用户头像","fieldType":"string","dataType":"varchar","fieldJson":"headerImg","primaryKey":false,"dataTypeLong":"191","columnName":"header_img","comment":"用户头像","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"BaseColor","fieldDesc":"基础颜色","fieldType":"string","dataType":"varchar","fieldJson":"baseColor","primaryKey":false,"dataTypeLong":"191","columnName":"base_color","comment":"基础颜色","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"AuthorityId","fieldDesc":"用户角色ID","fieldType":"int","dataType":"bigint","fieldJson":"authorityId","primaryKey":false,"dataTypeLong":"20","columnName":"authority_id","comment":"用户角色ID","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Phone","fieldDesc":"用户手机号","fieldType":"string","dataType":"varchar","fieldJson":"phone","primaryKey":false,"dataTypeLong":"191","columnName":"phone","comment":"用户手机号","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Email","fieldDesc":"用户邮箱","fieldType":"string","dataType":"varchar","fieldJson":"email","primaryKey":false,"dataTypeLong":"191","columnName":"email","comment":"用户邮箱","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}},{"fieldName":"Enable","fieldDesc":"用户是否被冻结 1正常 2冻结","fieldType":"int","dataType":"bigint","fieldJson":"enable","primaryKey":false,"dataTypeLong":"19","columnName":"enable","comment":"用户是否被冻结 1正常 2冻结","require":false,"errorText":"","clearable":true,"fieldSearchType":"","fieldIndexType":"","dictType":"","front":true,"dataSource":{"association":1,"table":"","label":"","value":""}}],"humpPackageName":"sys_users"}`
|
||||
err := json.Unmarshal([]byte(testJson), &tt.args.info)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = tt.args.info.Pretreatment()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
got, err := AutoCodeTemplate.Preview(tt.args.ctx, tt.args.info)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Preview() error = %+v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Preview() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
24
service/system/enter.go
Normal file
24
service/system/enter.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package system
|
||||
|
||||
type ServiceGroup struct {
|
||||
JwtService
|
||||
ApiService
|
||||
MenuService
|
||||
UserService
|
||||
CasbinService
|
||||
InitDBService
|
||||
AutoCodeService
|
||||
BaseMenuService
|
||||
AuthorityService
|
||||
DictionaryService
|
||||
SystemConfigService
|
||||
OperationRecordService
|
||||
DictionaryDetailService
|
||||
AuthorityBtnService
|
||||
SysExportTemplateService
|
||||
SysParamsService
|
||||
AutoCodePlugin autoCodePlugin
|
||||
AutoCodePackage autoCodePackage
|
||||
AutoCodeHistory autoCodeHistory
|
||||
AutoCodeTemplate autoCodeTemplate
|
||||
}
|
84
service/system/jwt_black_list.go
Normal file
84
service/system/jwt_black_list.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
)
|
||||
|
||||
type JwtService struct{}
|
||||
|
||||
var JwtServiceApp = new(JwtService)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: JsonInBlacklist
|
||||
//@description: 拉黑jwt
|
||||
//@param: jwtList model.JwtBlacklist
|
||||
//@return: err error
|
||||
|
||||
func (jwtService *JwtService) JsonInBlacklist(jwtList system.JwtBlacklist) (err error) {
|
||||
err = global.GVA_DB.Create(&jwtList).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
global.BlackCache.SetDefault(jwtList.Jwt, struct{}{})
|
||||
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
|
||||
//@param: userName string
|
||||
//@return: redisJWT string, err error
|
||||
|
||||
func (jwtService *JwtService) GetRedisJWT(userName string) (redisJWT string, err error) {
|
||||
redisJWT, err = global.GVA_REDIS.Get(context.Background(), userName).Result()
|
||||
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
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("加载数据库jwt黑名单失败!", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(data); i++ {
|
||||
global.BlackCache.SetDefault(data[i], struct{}{})
|
||||
} // jwt黑名单 加入 BlackCache 中
|
||||
}
|
326
service/system/sys_api.go
Normal file
326
service/system/sys_api.go
Normal file
@@ -0,0 +1,326 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
systemRes "git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateApi
|
||||
//@description: 新增基础api
|
||||
//@param: api model.SysApi
|
||||
//@return: err error
|
||||
|
||||
type ApiService struct{}
|
||||
|
||||
var ApiServiceApp = new(ApiService)
|
||||
|
||||
func (apiService *ApiService) CreateApi(api system.SysApi) (err error) {
|
||||
if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.SysApi{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同api")
|
||||
}
|
||||
return global.GVA_DB.Create(&api).Error
|
||||
}
|
||||
|
||||
func (apiService *ApiService) GetApiGroups() (groups []string, groupApiMap map[string]string, err error) {
|
||||
var apis []system.SysApi
|
||||
err = global.GVA_DB.Find(&apis).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
groupApiMap = make(map[string]string, 0)
|
||||
for i := range apis {
|
||||
pathArr := strings.Split(apis[i].Path, "/")
|
||||
newGroup := true
|
||||
for i2 := range groups {
|
||||
if groups[i2] == apis[i].ApiGroup {
|
||||
newGroup = false
|
||||
}
|
||||
}
|
||||
if newGroup {
|
||||
groups = append(groups, apis[i].ApiGroup)
|
||||
}
|
||||
groupApiMap[pathArr[1]] = apis[i].ApiGroup
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (apiService *ApiService) SyncApi() (newApis, deleteApis, ignoreApis []system.SysApi, err error) {
|
||||
newApis = make([]system.SysApi, 0)
|
||||
deleteApis = make([]system.SysApi, 0)
|
||||
ignoreApis = make([]system.SysApi, 0)
|
||||
var apis []system.SysApi
|
||||
err = global.GVA_DB.Find(&apis).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ignores []system.SysIgnoreApi
|
||||
err = global.GVA_DB.Find(&ignores).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range ignores {
|
||||
ignoreApis = append(ignoreApis, system.SysApi{
|
||||
Path: ignores[i].Path,
|
||||
Description: "",
|
||||
ApiGroup: "",
|
||||
Method: ignores[i].Method,
|
||||
})
|
||||
}
|
||||
|
||||
var cacheApis []system.SysApi
|
||||
for i := range global.GVA_ROUTERS {
|
||||
ignoresFlag := false
|
||||
for j := range ignores {
|
||||
if ignores[j].Path == global.GVA_ROUTERS[i].Path && ignores[j].Method == global.GVA_ROUTERS[i].Method {
|
||||
ignoresFlag = true
|
||||
}
|
||||
}
|
||||
if !ignoresFlag {
|
||||
cacheApis = append(cacheApis, system.SysApi{
|
||||
Path: global.GVA_ROUTERS[i].Path,
|
||||
Method: global.GVA_ROUTERS[i].Method,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//对比数据库中的api和内存中的api,如果数据库中的api不存在于内存中,则把api放入删除数组,如果内存中的api不存在于数据库中,则把api放入新增数组
|
||||
for i := range cacheApis {
|
||||
var flag bool
|
||||
// 如果存在于内存不存在于api数组中
|
||||
for j := range apis {
|
||||
if cacheApis[i].Path == apis[j].Path && cacheApis[i].Method == apis[j].Method {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
newApis = append(newApis, system.SysApi{
|
||||
Path: cacheApis[i].Path,
|
||||
Description: "",
|
||||
ApiGroup: "",
|
||||
Method: cacheApis[i].Method,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for i := range apis {
|
||||
var flag bool
|
||||
// 如果存在于api数组不存在于内存
|
||||
for j := range cacheApis {
|
||||
if cacheApis[j].Path == apis[i].Path && cacheApis[j].Method == apis[i].Method {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
deleteApis = append(deleteApis, apis[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (apiService *ApiService) IgnoreApi(ignoreApi system.SysIgnoreApi) (err error) {
|
||||
if ignoreApi.Flag {
|
||||
return global.GVA_DB.Create(&ignoreApi).Error
|
||||
}
|
||||
return global.GVA_DB.Unscoped().Delete(&ignoreApi, "path = ? AND method = ?", ignoreApi.Path, ignoreApi.Method).Error
|
||||
}
|
||||
|
||||
func (apiService *ApiService) EnterSyncApi(syncApis systemRes.SysSyncApis) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
var txErr error
|
||||
if len(syncApis.NewApis) > 0 {
|
||||
txErr = tx.Create(&syncApis.NewApis).Error
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
}
|
||||
for i := range syncApis.DeleteApis {
|
||||
CasbinServiceApp.ClearCasbin(1, syncApis.DeleteApis[i].Path, syncApis.DeleteApis[i].Method)
|
||||
txErr = tx.Delete(&system.SysApi{}, "path = ? AND method = ?", syncApis.DeleteApis[i].Path, syncApis.DeleteApis[i].Method).Error
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteApi
|
||||
//@description: 删除基础api
|
||||
//@param: api model.SysApi
|
||||
//@return: err error
|
||||
|
||||
func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) {
|
||||
var entity system.SysApi
|
||||
err = global.GVA_DB.First(&entity, "id = ?", api.ID).Error // 根据id查询api记录
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) { // api记录不存在
|
||||
return err
|
||||
}
|
||||
err = global.GVA_DB.Delete(&entity).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)
|
||||
return nil
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetAPIInfoList
|
||||
//@description: 分页获取数据,
|
||||
//@param: api model.SysApi, info request.PageInfo, order string, desc bool
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.PageInfo, order string, desc bool) (list interface{}, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
db := global.GVA_DB.Model(&system.SysApi{})
|
||||
var apiList []system.SysApi
|
||||
|
||||
if api.Path != "" {
|
||||
db = db.Where("path LIKE ?", "%"+api.Path+"%")
|
||||
}
|
||||
|
||||
if api.Description != "" {
|
||||
db = db.Where("description LIKE ?", "%"+api.Description+"%")
|
||||
}
|
||||
|
||||
if api.Method != "" {
|
||||
db = db.Where("method = ?", api.Method)
|
||||
}
|
||||
|
||||
if api.ApiGroup != "" {
|
||||
db = db.Where("api_group = ?", api.ApiGroup)
|
||||
}
|
||||
|
||||
err = db.Count(&total).Error
|
||||
|
||||
if err != nil {
|
||||
return apiList, total, err
|
||||
}
|
||||
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
OrderStr := "id desc"
|
||||
if order != "" {
|
||||
orderMap := make(map[string]bool, 5)
|
||||
orderMap["id"] = true
|
||||
orderMap["path"] = true
|
||||
orderMap["api_group"] = true
|
||||
orderMap["description"] = true
|
||||
orderMap["method"] = true
|
||||
if !orderMap[order] {
|
||||
err = fmt.Errorf("非法的排序字段: %v", order)
|
||||
return apiList, total, err
|
||||
}
|
||||
OrderStr = order
|
||||
if desc {
|
||||
OrderStr = order + " desc"
|
||||
}
|
||||
}
|
||||
err = db.Order(OrderStr).Find(&apiList).Error
|
||||
return apiList, total, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetAllApis
|
||||
//@description: 获取所有的api
|
||||
//@return: apis []model.SysApi, err error
|
||||
|
||||
func (apiService *ApiService) GetAllApis(authorityID uint) (apis []system.SysApi, err error) {
|
||||
parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = global.GVA_DB.Order("id desc").Find(&apis).Error
|
||||
if parentAuthorityID == 0 || !global.GVA_CONFIG.System.UseStrictAuth {
|
||||
return
|
||||
}
|
||||
paths := CasbinServiceApp.GetPolicyPathByAuthorityId(authorityID)
|
||||
// 挑选 apis里面的path和method也在paths里面的api
|
||||
var authApis []system.SysApi
|
||||
for i := range apis {
|
||||
for j := range paths {
|
||||
if paths[j].Path == apis[i].Path && paths[j].Method == apis[i].Method {
|
||||
authApis = append(authApis, apis[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return authApis, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetApiById
|
||||
//@description: 根据id获取api
|
||||
//@param: id float64
|
||||
//@return: api model.SysApi, err error
|
||||
|
||||
func (apiService *ApiService) GetApiById(id int) (api system.SysApi, err error) {
|
||||
err = global.GVA_DB.First(&api, "id = ?", id).Error
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateApi
|
||||
//@description: 根据id更新api
|
||||
//@param: api model.SysApi
|
||||
//@return: err error
|
||||
|
||||
func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) {
|
||||
var oldA system.SysApi
|
||||
err = global.GVA_DB.First(&oldA, "id = ?", api.ID).Error
|
||||
if oldA.Path != api.Path || oldA.Method != api.Method {
|
||||
var duplicateApi system.SysApi
|
||||
if ferr := global.GVA_DB.First(&duplicateApi, "path = ? AND method = ?", api.Path, api.Method).Error; ferr != nil {
|
||||
if !errors.Is(ferr, gorm.ErrRecordNotFound) {
|
||||
return ferr
|
||||
}
|
||||
} else {
|
||||
if duplicateApi.ID != api.ID {
|
||||
return errors.New("存在相同api路径")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return global.GVA_DB.Save(&api).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteApisByIds
|
||||
//@description: 删除选中API
|
||||
//@param: apis []model.SysApi
|
||||
//@return: err error
|
||||
|
||||
func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
var apis []system.SysApi
|
||||
err = tx.Find(&apis, "id in ?", ids.Ids).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Delete(&[]system.SysApi{}, "id in ?", ids.Ids).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sysApi := range apis {
|
||||
CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
330
service/system/sys_authority.go
Normal file
330
service/system/sys_authority.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
systemReq "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var ErrRoleExistence = errors.New("存在相同角色id")
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateAuthority
|
||||
//@description: 创建一个角色
|
||||
//@param: auth model.SysAuthority
|
||||
//@return: authority system.SysAuthority, err error
|
||||
|
||||
type AuthorityService struct{}
|
||||
|
||||
var AuthorityServiceApp = new(AuthorityService)
|
||||
|
||||
func (authorityService *AuthorityService) CreateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) {
|
||||
|
||||
if err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return auth, ErrRoleExistence
|
||||
}
|
||||
|
||||
e := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
if err = tx.Create(&auth).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth.SysBaseMenus = systemReq.DefaultMenu()
|
||||
if err = tx.Model(&auth).Association("SysBaseMenus").Replace(&auth.SysBaseMenus); err != nil {
|
||||
return err
|
||||
}
|
||||
casbinInfos := systemReq.DefaultCasbin()
|
||||
authorityId := strconv.Itoa(int(auth.AuthorityId))
|
||||
rules := [][]string{}
|
||||
for _, v := range casbinInfos {
|
||||
rules = append(rules, []string{authorityId, v.Path, v.Method})
|
||||
}
|
||||
return CasbinServiceApp.AddPolicies(tx, rules)
|
||||
})
|
||||
|
||||
return auth, e
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CopyAuthority
|
||||
//@description: 复制一个角色
|
||||
//@param: copyInfo response.SysAuthorityCopyResponse
|
||||
//@return: authority system.SysAuthority, err error
|
||||
|
||||
func (authorityService *AuthorityService) CopyAuthority(adminAuthorityID uint, copyInfo response.SysAuthorityCopyResponse) (authority system.SysAuthority, err error) {
|
||||
var authorityBox system.SysAuthority
|
||||
if !errors.Is(global.GVA_DB.Where("authority_id = ?", copyInfo.Authority.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) {
|
||||
return authority, ErrRoleExistence
|
||||
}
|
||||
copyInfo.Authority.Children = []system.SysAuthority{}
|
||||
menus, err := MenuServiceApp.GetMenuAuthority(&request.GetAuthorityId{AuthorityId: copyInfo.OldAuthorityId})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var baseMenu []system.SysBaseMenu
|
||||
for _, v := range menus {
|
||||
intNum := v.MenuId
|
||||
v.SysBaseMenu.ID = uint(intNum)
|
||||
baseMenu = append(baseMenu, v.SysBaseMenu)
|
||||
}
|
||||
copyInfo.Authority.SysBaseMenus = baseMenu
|
||||
err = global.GVA_DB.Create(©Info.Authority).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var btns []system.SysAuthorityBtn
|
||||
|
||||
err = global.GVA_DB.Find(&btns, "authority_id = ?", copyInfo.OldAuthorityId).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(btns) > 0 {
|
||||
for i := range btns {
|
||||
btns[i].AuthorityId = copyInfo.Authority.AuthorityId
|
||||
}
|
||||
err = global.GVA_DB.Create(&btns).Error
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
paths := CasbinServiceApp.GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId)
|
||||
err = CasbinServiceApp.UpdateCasbin(adminAuthorityID, copyInfo.Authority.AuthorityId, paths)
|
||||
if err != nil {
|
||||
_ = authorityService.DeleteAuthority(©Info.Authority)
|
||||
}
|
||||
return copyInfo.Authority, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateAuthority
|
||||
//@description: 更改一个角色
|
||||
//@param: auth model.SysAuthority
|
||||
//@return: authority system.SysAuthority, err error
|
||||
|
||||
func (authorityService *AuthorityService) UpdateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) {
|
||||
var oldAuthority system.SysAuthority
|
||||
err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&oldAuthority).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Debug(err.Error())
|
||||
return system.SysAuthority{}, errors.New("查询角色数据失败")
|
||||
}
|
||||
err = global.GVA_DB.Model(&oldAuthority).Updates(&auth).Error
|
||||
return auth, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteAuthority
|
||||
//@description: 删除角色
|
||||
//@param: auth *model.SysAuthority
|
||||
//@return: err error
|
||||
|
||||
func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthority) error {
|
||||
if errors.Is(global.GVA_DB.Debug().Preload("Users").First(&auth).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("该角色不存在")
|
||||
}
|
||||
if len(auth.Users) != 0 {
|
||||
return errors.New("此角色有用户正在使用禁止删除")
|
||||
}
|
||||
if !errors.Is(global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysUser{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("此角色有用户正在使用禁止删除")
|
||||
}
|
||||
if !errors.Is(global.GVA_DB.Where("parent_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("此角色存在子角色不允许删除")
|
||||
}
|
||||
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
var err error
|
||||
if err = tx.Preload("SysBaseMenus").Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(auth).Unscoped().Delete(auth).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(auth.SysBaseMenus) > 0 {
|
||||
if err = tx.Model(auth).Association("SysBaseMenus").Delete(auth.SysBaseMenus); err != nil {
|
||||
return err
|
||||
}
|
||||
// err = db.Association("SysBaseMenus").Delete(&auth)
|
||||
}
|
||||
if len(auth.DataAuthorityId) > 0 {
|
||||
if err = tx.Model(auth).Association("DataAuthorityId").Delete(auth.DataAuthorityId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.Delete(&system.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tx.Where("authority_id = ?", auth.AuthorityId).Delete(&[]system.SysAuthorityBtn{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authorityId := strconv.Itoa(int(auth.AuthorityId))
|
||||
|
||||
if err = CasbinServiceApp.RemoveFilteredPolicy(tx, authorityId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetAuthorityInfoList
|
||||
//@description: 分页获取数据
|
||||
//@param: info request.PageInfo
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (authorityService *AuthorityService) GetAuthorityInfoList(authorityID uint) (list []system.SysAuthority, err error) {
|
||||
var authority system.SysAuthority
|
||||
err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var authorities []system.SysAuthority
|
||||
db := global.GVA_DB.Model(&system.SysAuthority{})
|
||||
if global.GVA_CONFIG.System.UseStrictAuth {
|
||||
// 当开启了严格树形结构后
|
||||
if *authority.ParentId == 0 {
|
||||
// 只有顶级角色可以修改自己的权限和以下权限
|
||||
err = db.Preload("DataAuthorityId").Where("authority_id = ?", authorityID).Find(&authorities).Error
|
||||
} else {
|
||||
// 非顶级角色只能修改以下权限
|
||||
err = db.Debug().Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error
|
||||
}
|
||||
} else {
|
||||
err = db.Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authorities).Error
|
||||
}
|
||||
|
||||
for k := range authorities {
|
||||
err = authorityService.findChildrenAuthority(&authorities[k])
|
||||
}
|
||||
return authorities, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetAuthorityInfoList
|
||||
//@description: 分页获取数据
|
||||
//@param: info request.PageInfo
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (authorityService *AuthorityService) GetStructAuthorityList(authorityID uint) (list []uint, err error) {
|
||||
var auth system.SysAuthority
|
||||
_ = global.GVA_DB.First(&auth, "authority_id = ?", authorityID).Error
|
||||
var authorities []system.SysAuthority
|
||||
err = global.GVA_DB.Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error
|
||||
if len(authorities) > 0 {
|
||||
for k := range authorities {
|
||||
list = append(list, authorities[k].AuthorityId)
|
||||
childrenList, err := authorityService.GetStructAuthorityList(authorities[k].AuthorityId)
|
||||
if err == nil {
|
||||
list = append(list, childrenList...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if *auth.ParentId == 0 {
|
||||
list = append(list, authorityID)
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (authorityService *AuthorityService) CheckAuthorityIDAuth(authorityID, targetID uint) (err error) {
|
||||
if !global.GVA_CONFIG.System.UseStrictAuth {
|
||||
return nil
|
||||
}
|
||||
authIDS, err := authorityService.GetStructAuthorityList(authorityID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasAuth := false
|
||||
for _, v := range authIDS {
|
||||
if v == targetID {
|
||||
hasAuth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasAuth {
|
||||
return errors.New("您提交的角色ID不合法")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetAuthorityInfo
|
||||
//@description: 获取所有角色信息
|
||||
//@param: auth model.SysAuthority
|
||||
//@return: sa system.SysAuthority, err error
|
||||
|
||||
func (authorityService *AuthorityService) GetAuthorityInfo(auth system.SysAuthority) (sa system.SysAuthority, err error) {
|
||||
err = global.GVA_DB.Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(&sa).Error
|
||||
return sa, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetDataAuthority
|
||||
//@description: 设置角色资源权限
|
||||
//@param: auth model.SysAuthority
|
||||
//@return: error
|
||||
|
||||
func (authorityService *AuthorityService) SetDataAuthority(adminAuthorityID uint, auth system.SysAuthority) error {
|
||||
var checkIDs []uint
|
||||
checkIDs = append(checkIDs, auth.AuthorityId)
|
||||
for i := range auth.DataAuthorityId {
|
||||
checkIDs = append(checkIDs, auth.DataAuthorityId[i].AuthorityId)
|
||||
}
|
||||
|
||||
for i := range checkIDs {
|
||||
err := authorityService.CheckAuthorityIDAuth(adminAuthorityID, checkIDs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var s system.SysAuthority
|
||||
global.GVA_DB.Preload("DataAuthorityId").First(&s, "authority_id = ?", auth.AuthorityId)
|
||||
err := global.GVA_DB.Model(&s).Association("DataAuthorityId").Replace(&auth.DataAuthorityId)
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetMenuAuthority
|
||||
//@description: 菜单与角色绑定
|
||||
//@param: auth *model.SysAuthority
|
||||
//@return: error
|
||||
|
||||
func (authorityService *AuthorityService) SetMenuAuthority(auth *system.SysAuthority) error {
|
||||
var s system.SysAuthority
|
||||
global.GVA_DB.Preload("SysBaseMenus").First(&s, "authority_id = ?", auth.AuthorityId)
|
||||
err := global.GVA_DB.Model(&s).Association("SysBaseMenus").Replace(&auth.SysBaseMenus)
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: findChildrenAuthority
|
||||
//@description: 查询子角色
|
||||
//@param: authority *model.SysAuthority
|
||||
//@return: err error
|
||||
|
||||
func (authorityService *AuthorityService) findChildrenAuthority(authority *system.SysAuthority) (err error) {
|
||||
err = global.GVA_DB.Preload("DataAuthorityId").Where("parent_id = ?", authority.AuthorityId).Find(&authority.Children).Error
|
||||
if len(authority.Children) > 0 {
|
||||
for k := range authority.Children {
|
||||
err = authorityService.findChildrenAuthority(&authority.Children[k])
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
60
service/system/sys_authority_btn.go
Normal file
60
service/system/sys_authority_btn.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AuthorityBtnService struct{}
|
||||
|
||||
var AuthorityBtnServiceApp = new(AuthorityBtnService)
|
||||
|
||||
func (a *AuthorityBtnService) GetAuthorityBtn(req request.SysAuthorityBtnReq) (res response.SysAuthorityBtnRes, err error) {
|
||||
var authorityBtn []system.SysAuthorityBtn
|
||||
err = global.GVA_DB.Find(&authorityBtn, "authority_id = ? and sys_menu_id = ?", req.AuthorityId, req.MenuID).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var selected []uint
|
||||
for _, v := range authorityBtn {
|
||||
selected = append(selected, v.SysBaseMenuBtnID)
|
||||
}
|
||||
res.Selected = selected
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (a *AuthorityBtnService) SetAuthorityBtn(req request.SysAuthorityBtnReq) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
var authorityBtn []system.SysAuthorityBtn
|
||||
err = tx.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ? and sys_menu_id = ?", req.AuthorityId, req.MenuID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range req.Selected {
|
||||
authorityBtn = append(authorityBtn, system.SysAuthorityBtn{
|
||||
AuthorityId: req.AuthorityId,
|
||||
SysMenuID: req.MenuID,
|
||||
SysBaseMenuBtnID: v,
|
||||
})
|
||||
}
|
||||
if len(authorityBtn) > 0 {
|
||||
err = tx.Create(&authorityBtn).Error
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AuthorityBtnService) CanRemoveAuthorityBtn(ID string) (err error) {
|
||||
fErr := global.GVA_DB.First(&system.SysAuthorityBtn{}, "sys_base_menu_btn_id = ?", ID).Error
|
||||
if errors.Is(fErr, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("此按钮正在被使用无法删除")
|
||||
}
|
55
service/system/sys_auto_code_interface.go
Normal file
55
service/system/sys_auto_code_interface.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
)
|
||||
|
||||
type AutoCodeService struct{}
|
||||
|
||||
type Database interface {
|
||||
GetDB(businessDB string) (data []response.Db, err error)
|
||||
GetTables(businessDB string, dbName string) (data []response.Table, err error)
|
||||
GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error)
|
||||
}
|
||||
|
||||
func (autoCodeService *AutoCodeService) Database(businessDB string) Database {
|
||||
|
||||
if businessDB == "" {
|
||||
switch global.GVA_CONFIG.System.DbType {
|
||||
case "mysql":
|
||||
return AutoCodeMysql
|
||||
case "pgsql":
|
||||
return AutoCodePgsql
|
||||
case "mssql":
|
||||
return AutoCodeMssql
|
||||
case "oracle":
|
||||
return AutoCodeOracle
|
||||
case "sqlite":
|
||||
return AutoCodeSqlite
|
||||
default:
|
||||
return AutoCodeMysql
|
||||
}
|
||||
} else {
|
||||
for _, info := range global.GVA_CONFIG.DBList {
|
||||
if info.AliasName == businessDB {
|
||||
switch info.Type {
|
||||
case "mysql":
|
||||
return AutoCodeMysql
|
||||
case "mssql":
|
||||
return AutoCodeMssql
|
||||
case "pgsql":
|
||||
return AutoCodePgsql
|
||||
case "oracle":
|
||||
return AutoCodeOracle
|
||||
case "sqlite":
|
||||
return AutoCodeSqlite
|
||||
default:
|
||||
return AutoCodeMysql
|
||||
}
|
||||
}
|
||||
}
|
||||
return AutoCodeMysql
|
||||
}
|
||||
|
||||
}
|
83
service/system/sys_auto_code_mssql.go
Normal file
83
service/system/sys_auto_code_mssql.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
)
|
||||
|
||||
var AutoCodeMssql = new(autoCodeMssql)
|
||||
|
||||
type autoCodeMssql struct{}
|
||||
|
||||
// GetDB 获取数据库的所有数据库名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMssql) GetDB(businessDB string) (data []response.Db, err error) {
|
||||
var entities []response.Db
|
||||
sql := "select name AS 'database' from sys.databases;"
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
}
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetTables 获取数据库的所有表名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMssql) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
|
||||
var entities []response.Table
|
||||
|
||||
sql := fmt.Sprintf(`select name as 'table_name' from %s.DBO.sysobjects where xtype='U'`, dbName)
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
}
|
||||
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMssql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
|
||||
var entities []response.Column
|
||||
sql := fmt.Sprintf(`
|
||||
SELECT
|
||||
sc.name AS column_name,
|
||||
st.name AS data_type,
|
||||
sc.max_length AS data_type_long,
|
||||
CASE
|
||||
WHEN pk.object_id IS NOT NULL THEN 1
|
||||
ELSE 0
|
||||
END AS primary_key,
|
||||
sc.column_id
|
||||
FROM
|
||||
%s.sys.columns sc
|
||||
JOIN
|
||||
sys.types st ON sc.user_type_id=st.user_type_id
|
||||
LEFT JOIN
|
||||
%s.sys.objects so ON so.name='%s' AND so.type='U'
|
||||
LEFT JOIN
|
||||
%s.sys.indexes si ON si.object_id = so.object_id AND si.is_primary_key = 1
|
||||
LEFT JOIN
|
||||
%s.sys.index_columns sic ON sic.object_id = si.object_id AND sic.index_id = si.index_id AND sic.column_id = sc.column_id
|
||||
LEFT JOIN
|
||||
%s.sys.key_constraints pk ON pk.object_id = si.object_id
|
||||
WHERE
|
||||
st.is_user_defined=0 AND sc.object_id = so.object_id
|
||||
ORDER BY
|
||||
sc.column_id
|
||||
`, dbName, dbName, tableName, dbName, dbName, dbName)
|
||||
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
}
|
||||
|
||||
return entities, err
|
||||
}
|
83
service/system/sys_auto_code_mysql.go
Normal file
83
service/system/sys_auto_code_mysql.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
)
|
||||
|
||||
var AutoCodeMysql = new(autoCodeMysql)
|
||||
|
||||
type autoCodeMysql struct{}
|
||||
|
||||
// GetDB 获取数据库的所有数据库名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMysql) GetDB(businessDB string) (data []response.Db, err error) {
|
||||
var entities []response.Db
|
||||
sql := "SELECT SCHEMA_NAME AS `database` FROM INFORMATION_SCHEMA.SCHEMATA;"
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
}
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetTables 获取数据库的所有表名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMysql) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
|
||||
var entities []response.Table
|
||||
sql := `select table_name as table_name from information_schema.tables where table_schema = ?`
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql, dbName).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error
|
||||
}
|
||||
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeMysql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
|
||||
var entities []response.Column
|
||||
sql := `
|
||||
SELECT
|
||||
c.COLUMN_NAME column_name,
|
||||
c.DATA_TYPE data_type,
|
||||
CASE c.DATA_TYPE
|
||||
WHEN 'longtext' THEN c.CHARACTER_MAXIMUM_LENGTH
|
||||
WHEN 'varchar' THEN c.CHARACTER_MAXIMUM_LENGTH
|
||||
WHEN 'double' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE)
|
||||
WHEN 'decimal' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE)
|
||||
WHEN 'int' THEN c.NUMERIC_PRECISION
|
||||
WHEN 'bigint' THEN c.NUMERIC_PRECISION
|
||||
ELSE ''
|
||||
END AS data_type_long,
|
||||
c.COLUMN_COMMENT column_comment,
|
||||
CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key,
|
||||
c.ORDINAL_POSITION
|
||||
FROM
|
||||
INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN
|
||||
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
|
||||
ON
|
||||
c.TABLE_SCHEMA = kcu.TABLE_SCHEMA
|
||||
AND c.TABLE_NAME = kcu.TABLE_NAME
|
||||
AND c.COLUMN_NAME = kcu.COLUMN_NAME
|
||||
AND kcu.CONSTRAINT_NAME = 'PRIMARY'
|
||||
WHERE
|
||||
c.TABLE_NAME = ?
|
||||
AND c.TABLE_SCHEMA = ?
|
||||
ORDER BY
|
||||
c.ORDINAL_POSITION;`
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql, tableName, dbName).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error
|
||||
}
|
||||
|
||||
return entities, err
|
||||
}
|
72
service/system/sys_auto_code_oracle.go
Normal file
72
service/system/sys_auto_code_oracle.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
)
|
||||
|
||||
var AutoCodeOracle = new(autoCodeOracle)
|
||||
|
||||
type autoCodeOracle struct{}
|
||||
|
||||
// GetDB 获取数据库的所有数据库名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeOracle) GetDB(businessDB string) (data []response.Db, err error) {
|
||||
var entities []response.Db
|
||||
sql := `SELECT lower(username) AS "database" FROM all_users`
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetTables 获取数据库的所有表名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeOracle) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
|
||||
var entities []response.Table
|
||||
sql := `select lower(table_name) as "table_name" from all_tables where lower(owner) = ?`
|
||||
|
||||
err = global.GVA_DBList[businessDB].Raw(sql, dbName).Scan(&entities).Error
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (s *autoCodeOracle) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
|
||||
var entities []response.Column
|
||||
sql := `
|
||||
SELECT
|
||||
lower(a.COLUMN_NAME) as "column_name",
|
||||
(CASE WHEN a.DATA_TYPE = 'NUMBER' AND a.DATA_SCALE=0 THEN 'int' else lower(a.DATA_TYPE) end) as "data_type",
|
||||
(CASE WHEN a.DATA_TYPE = 'NUMBER' THEN a.DATA_PRECISION else a.DATA_LENGTH end) as "data_type_long",
|
||||
b.COMMENTS as "column_comment",
|
||||
(CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key",
|
||||
a.COLUMN_ID
|
||||
FROM
|
||||
all_tab_columns a
|
||||
JOIN
|
||||
all_col_comments b ON a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME
|
||||
LEFT JOIN
|
||||
(
|
||||
SELECT
|
||||
acc.OWNER,
|
||||
acc.TABLE_NAME,
|
||||
acc.COLUMN_NAME
|
||||
FROM
|
||||
all_cons_columns acc
|
||||
JOIN
|
||||
all_constraints ac ON acc.OWNER = ac.OWNER AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME
|
||||
WHERE
|
||||
ac.CONSTRAINT_TYPE = 'P'
|
||||
) pk ON a.OWNER = pk.OWNER AND a.TABLE_NAME = pk.TABLE_NAME AND a.COLUMN_NAME = pk.COLUMN_NAME
|
||||
WHERE
|
||||
lower(a.table_name) = ?
|
||||
AND lower(a.OWNER) = ?
|
||||
ORDER BY
|
||||
a.COLUMN_ID;
|
||||
`
|
||||
|
||||
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error
|
||||
return entities, err
|
||||
}
|
135
service/system/sys_auto_code_pgsql.go
Normal file
135
service/system/sys_auto_code_pgsql.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
)
|
||||
|
||||
var AutoCodePgsql = new(autoCodePgsql)
|
||||
|
||||
type autoCodePgsql struct{}
|
||||
|
||||
// GetDB 获取数据库的所有数据库名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodePgsql) GetDB(businessDB string) (data []response.Db, err error) {
|
||||
var entities []response.Db
|
||||
sql := `SELECT datname as database FROM pg_database WHERE datistemplate = false`
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&entities).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&entities).Error
|
||||
}
|
||||
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetTables 获取数据库的所有表名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodePgsql) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
|
||||
var entities []response.Table
|
||||
sql := `select table_name as table_name from information_schema.tables where table_catalog = ? and table_schema = ?`
|
||||
|
||||
db := global.GVA_DB
|
||||
if businessDB != "" {
|
||||
db = global.GVA_DBList[businessDB]
|
||||
}
|
||||
|
||||
err = db.Raw(sql, dbName, "public").Scan(&entities).Error
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetColumn 获取指定数据库和指定数据表的所有字段名,类型值等
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodePgsql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
|
||||
// todo 数据获取不全, 待完善sql
|
||||
sql := `
|
||||
SELECT
|
||||
psc.COLUMN_NAME AS COLUMN_NAME,
|
||||
psc.udt_name AS data_type,
|
||||
CASE
|
||||
psc.udt_name
|
||||
WHEN 'text' THEN
|
||||
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
|
||||
WHEN 'varchar' THEN
|
||||
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
|
||||
WHEN 'smallint' THEN
|
||||
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
|
||||
WHEN 'decimal' THEN
|
||||
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
|
||||
WHEN 'integer' THEN
|
||||
concat_ws ( '', '', psc.NUMERIC_PRECISION )
|
||||
WHEN 'int4' THEN
|
||||
concat_ws ( '', '', psc.NUMERIC_PRECISION )
|
||||
WHEN 'int8' THEN
|
||||
concat_ws ( '', '', psc.NUMERIC_PRECISION )
|
||||
WHEN 'bigint' THEN
|
||||
concat_ws ( '', '', psc.NUMERIC_PRECISION )
|
||||
WHEN 'timestamp' THEN
|
||||
concat_ws ( '', '', psc.datetime_precision )
|
||||
ELSE ''
|
||||
END AS data_type_long,
|
||||
(
|
||||
SELECT
|
||||
pd.description
|
||||
FROM
|
||||
pg_description pd
|
||||
WHERE
|
||||
(pd.objoid,pd.objsubid) in (
|
||||
SELECT pa.attrelid,pa.attnum
|
||||
FROM
|
||||
pg_attribute pa
|
||||
WHERE pa.attrelid = ( SELECT oid FROM pg_class pc WHERE
|
||||
pc.relname = psc.table_name
|
||||
)
|
||||
and attname = psc.column_name
|
||||
)
|
||||
) AS column_comment,
|
||||
(
|
||||
SELECT
|
||||
COUNT(*)
|
||||
FROM
|
||||
pg_constraint
|
||||
WHERE
|
||||
contype = 'p'
|
||||
AND conrelid = (
|
||||
SELECT
|
||||
oid
|
||||
FROM
|
||||
pg_class
|
||||
WHERE
|
||||
relname = psc.table_name
|
||||
)
|
||||
AND conkey::int[] @> ARRAY[(
|
||||
SELECT
|
||||
attnum::integer
|
||||
FROM
|
||||
pg_attribute
|
||||
WHERE
|
||||
attrelid = conrelid
|
||||
AND attname = psc.column_name
|
||||
)]
|
||||
) > 0 AS primary_key,
|
||||
psc.ordinal_position
|
||||
FROM
|
||||
INFORMATION_SCHEMA.COLUMNS psc
|
||||
WHERE
|
||||
table_catalog = ?
|
||||
AND table_schema = 'public'
|
||||
AND TABLE_NAME = ?
|
||||
ORDER BY
|
||||
psc.ordinal_position;
|
||||
`
|
||||
var entities []response.Column
|
||||
//sql = strings.ReplaceAll(sql, "@table_catalog", dbName)
|
||||
//sql = strings.ReplaceAll(sql, "@table_name", tableName)
|
||||
db := global.GVA_DB
|
||||
if businessDB != "" {
|
||||
db = global.GVA_DBList[businessDB]
|
||||
}
|
||||
|
||||
err = db.Raw(sql, dbName, tableName).Scan(&entities).Error
|
||||
return entities, err
|
||||
}
|
84
service/system/sys_auto_code_sqlite.go
Normal file
84
service/system/sys_auto_code_sqlite.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/response"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var AutoCodeSqlite = new(autoCodeSqlite)
|
||||
|
||||
type autoCodeSqlite struct{}
|
||||
|
||||
// GetDB 获取数据库的所有数据库名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodeSqlite) GetDB(businessDB string) (data []response.Db, err error) {
|
||||
var entities []response.Db
|
||||
sql := "PRAGMA database_list;"
|
||||
var databaseList []struct {
|
||||
File string `gorm:"column:file"`
|
||||
}
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Find(&databaseList).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Find(&databaseList).Error
|
||||
}
|
||||
for _, database := range databaseList {
|
||||
if database.File != "" {
|
||||
fileName := filepath.Base(database.File)
|
||||
fileExt := filepath.Ext(fileName)
|
||||
fileNameWithoutExt := strings.TrimSuffix(fileName, fileExt)
|
||||
|
||||
entities = append(entities, response.Db{fileNameWithoutExt})
|
||||
}
|
||||
}
|
||||
// entities = append(entities, response.Db{global.GVA_CONFIG.Sqlite.Dbname})
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetTables 获取数据库的所有表名
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodeSqlite) GetTables(businessDB string, dbName string) (data []response.Table, err error) {
|
||||
var entities []response.Table
|
||||
sql := `SELECT name FROM sqlite_master WHERE type='table'`
|
||||
tabelNames := []string{}
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Find(&tabelNames).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Find(&tabelNames).Error
|
||||
}
|
||||
for _, tabelName := range tabelNames {
|
||||
entities = append(entities, response.Table{tabelName})
|
||||
}
|
||||
return entities, err
|
||||
}
|
||||
|
||||
// GetColumn 获取指定数据表的所有字段名,类型值等
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (a *autoCodeSqlite) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
|
||||
var entities []response.Column
|
||||
sql := fmt.Sprintf("PRAGMA table_info(%s);", tableName)
|
||||
var columnInfos []struct {
|
||||
Name string `gorm:"column:name"`
|
||||
Type string `gorm:"column:type"`
|
||||
Pk int `gorm:"column:pk"`
|
||||
}
|
||||
if businessDB == "" {
|
||||
err = global.GVA_DB.Raw(sql).Scan(&columnInfos).Error
|
||||
} else {
|
||||
err = global.GVA_DBList[businessDB].Raw(sql).Scan(&columnInfos).Error
|
||||
}
|
||||
for _, columnInfo := range columnInfos {
|
||||
entities = append(entities, response.Column{
|
||||
ColumnName: columnInfo.Name,
|
||||
DataType: columnInfo.Type,
|
||||
PrimaryKey: columnInfo.Pk == 1,
|
||||
})
|
||||
}
|
||||
return entities, err
|
||||
}
|
146
service/system/sys_base_menu.go
Normal file
146
service/system/sys_base_menu.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BaseMenuService struct{}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteBaseMenu
|
||||
//@description: 删除基础路由
|
||||
//@param: id float64
|
||||
//@return: err error
|
||||
|
||||
var BaseMenuServiceApp = new(BaseMenuService)
|
||||
|
||||
func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
|
||||
err = global.GVA_DB.First(&system.SysBaseMenu{}, "parent_id = ?", id).Error
|
||||
if err == nil {
|
||||
return errors.New("此菜单存在子菜单不可删除")
|
||||
}
|
||||
var menu system.SysBaseMenu
|
||||
err = global.GVA_DB.First(&menu, id).Error
|
||||
if err != nil {
|
||||
return errors.New("记录不存在")
|
||||
}
|
||||
err = global.GVA_DB.First(&system.SysAuthority{}, "default_router = ?", menu.Name).Error
|
||||
if err == nil {
|
||||
return errors.New("此菜单有角色正在作为首页,不可删除")
|
||||
}
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateBaseMenu
|
||||
//@description: 更新路由
|
||||
//@param: menu model.SysBaseMenu
|
||||
//@return: err error
|
||||
|
||||
func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.SysBaseMenu) (err error) {
|
||||
var oldMenu system.SysBaseMenu
|
||||
upDateMap := make(map[string]interface{})
|
||||
upDateMap["keep_alive"] = menu.KeepAlive
|
||||
upDateMap["close_tab"] = menu.CloseTab
|
||||
upDateMap["default_menu"] = menu.DefaultMenu
|
||||
upDateMap["parent_id"] = menu.ParentId
|
||||
upDateMap["path"] = menu.Path
|
||||
upDateMap["name"] = menu.Name
|
||||
upDateMap["hidden"] = menu.Hidden
|
||||
upDateMap["component"] = menu.Component
|
||||
upDateMap["title"] = menu.Title
|
||||
upDateMap["active_name"] = menu.ActiveName
|
||||
upDateMap["icon"] = menu.Icon
|
||||
upDateMap["sort"] = menu.Sort
|
||||
|
||||
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
tx.Where("id = ?", menu.ID).Find(&oldMenu)
|
||||
if oldMenu.Name != menu.Name {
|
||||
if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
|
||||
global.GVA_LOG.Debug("存在相同name修改失败")
|
||||
return errors.New("存在相同name修改失败")
|
||||
}
|
||||
}
|
||||
txErr := tx.Unscoped().Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", menu.ID).Error
|
||||
if txErr != nil {
|
||||
global.GVA_LOG.Debug(txErr.Error())
|
||||
return txErr
|
||||
}
|
||||
txErr = tx.Unscoped().Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", menu.ID).Error
|
||||
if txErr != nil {
|
||||
global.GVA_LOG.Debug(txErr.Error())
|
||||
return txErr
|
||||
}
|
||||
if len(menu.Parameters) > 0 {
|
||||
for k := range menu.Parameters {
|
||||
menu.Parameters[k].SysBaseMenuID = menu.ID
|
||||
}
|
||||
txErr = tx.Create(&menu.Parameters).Error
|
||||
if txErr != nil {
|
||||
global.GVA_LOG.Debug(txErr.Error())
|
||||
return txErr
|
||||
}
|
||||
}
|
||||
|
||||
if len(menu.MenuBtn) > 0 {
|
||||
for k := range menu.MenuBtn {
|
||||
menu.MenuBtn[k].SysBaseMenuID = menu.ID
|
||||
}
|
||||
txErr = tx.Create(&menu.MenuBtn).Error
|
||||
if txErr != nil {
|
||||
global.GVA_LOG.Debug(txErr.Error())
|
||||
return txErr
|
||||
}
|
||||
}
|
||||
|
||||
txErr = tx.Model(&oldMenu).Updates(upDateMap).Error
|
||||
if txErr != nil {
|
||||
global.GVA_LOG.Debug(txErr.Error())
|
||||
return txErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetBaseMenuById
|
||||
//@description: 返回当前选中menu
|
||||
//@param: id float64
|
||||
//@return: menu system.SysBaseMenu, err error
|
||||
|
||||
func (baseMenuService *BaseMenuService) GetBaseMenuById(id int) (menu system.SysBaseMenu, err error) {
|
||||
err = global.GVA_DB.Preload("MenuBtn").Preload("Parameters").Where("id = ?", id).First(&menu).Error
|
||||
return
|
||||
}
|
221
service/system/sys_casbin.go
Normal file
221
service/system/sys_casbin.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
gormadapter "github.com/casbin/gorm-adapter/v3"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateCasbin
|
||||
//@description: 更新casbin权限
|
||||
//@param: authorityId string, casbinInfos []request.CasbinInfo
|
||||
//@return: error
|
||||
|
||||
type CasbinService struct{}
|
||||
|
||||
var CasbinServiceApp = new(CasbinService)
|
||||
|
||||
func (casbinService *CasbinService) UpdateCasbin(adminAuthorityID, AuthorityID uint, casbinInfos []request.CasbinInfo) error {
|
||||
|
||||
err := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, AuthorityID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if global.GVA_CONFIG.System.UseStrictAuth {
|
||||
apis, e := ApiServiceApp.GetAllApis(adminAuthorityID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for i := range casbinInfos {
|
||||
hasApi := false
|
||||
for j := range apis {
|
||||
if apis[j].Path == casbinInfos[i].Path && apis[j].Method == casbinInfos[i].Method {
|
||||
hasApi = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasApi {
|
||||
return errors.New("存在api不在权限列表中")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authorityId := strconv.Itoa(int(AuthorityID))
|
||||
casbinService.ClearCasbin(0, authorityId)
|
||||
rules := [][]string{}
|
||||
//做权限去重处理
|
||||
deduplicateMap := make(map[string]bool)
|
||||
for _, v := range casbinInfos {
|
||||
key := authorityId + v.Path + v.Method
|
||||
if _, ok := deduplicateMap[key]; !ok {
|
||||
deduplicateMap[key] = true
|
||||
rules = append(rules, []string{authorityId, v.Path, v.Method})
|
||||
}
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
} // 设置空权限无需调用 AddPolicies 方法
|
||||
e := casbinService.Casbin()
|
||||
success, _ := e.AddPolicies(rules)
|
||||
if !success {
|
||||
return errors.New("存在相同api,添加失败,请联系管理员")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateCasbinApi
|
||||
//@description: API更新随动
|
||||
//@param: oldPath string, newPath string, oldMethod string, newMethod string
|
||||
//@return: error
|
||||
|
||||
func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error {
|
||||
err := global.GVA_DB.Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{
|
||||
"v1": newPath,
|
||||
"v2": newMethod,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e := casbinService.Casbin()
|
||||
return e.LoadPolicy()
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetPolicyPathByAuthorityId
|
||||
//@description: 获取权限列表
|
||||
//@param: authorityId string
|
||||
//@return: pathMaps []request.CasbinInfo
|
||||
|
||||
func (casbinService *CasbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []request.CasbinInfo) {
|
||||
e := casbinService.Casbin()
|
||||
authorityId := strconv.Itoa(int(AuthorityID))
|
||||
list, _ := e.GetFilteredPolicy(0, authorityId)
|
||||
for _, v := range list {
|
||||
pathMaps = append(pathMaps, request.CasbinInfo{
|
||||
Path: v[1],
|
||||
Method: v[2],
|
||||
})
|
||||
}
|
||||
return pathMaps
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: ClearCasbin
|
||||
//@description: 清除匹配的权限
|
||||
//@param: v int, p ...string
|
||||
//@return: bool
|
||||
|
||||
func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool {
|
||||
e := casbinService.Casbin()
|
||||
success, _ := e.RemoveFilteredPolicy(v, p...)
|
||||
return success
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: RemoveFilteredPolicy
|
||||
//@description: 使用数据库方法清理筛选的politicy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效
|
||||
//@param: db *gorm.DB, authorityId string
|
||||
//@return: error
|
||||
|
||||
func (casbinService *CasbinService) RemoveFilteredPolicy(db *gorm.DB, authorityId string) error {
|
||||
return db.Delete(&gormadapter.CasbinRule{}, "v0 = ?", authorityId).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SyncPolicy
|
||||
//@description: 同步目前数据库的policy 此方法需要调用FreshCasbin方法才可以在系统中即刻生效
|
||||
//@param: db *gorm.DB, authorityId string, rules [][]string
|
||||
//@return: error
|
||||
|
||||
func (casbinService *CasbinService) SyncPolicy(db *gorm.DB, authorityId string, rules [][]string) error {
|
||||
err := casbinService.RemoveFilteredPolicy(db, authorityId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return casbinService.AddPolicies(db, rules)
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: AddPolicies
|
||||
//@description: 添加匹配的权限
|
||||
//@param: v int, p ...string
|
||||
//@return: bool
|
||||
|
||||
func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) error {
|
||||
var casbinRules []gormadapter.CasbinRule
|
||||
for i := range rules {
|
||||
casbinRules = append(casbinRules, gormadapter.CasbinRule{
|
||||
Ptype: "p",
|
||||
V0: rules[i][0],
|
||||
V1: rules[i][1],
|
||||
V2: rules[i][2],
|
||||
})
|
||||
}
|
||||
return db.Create(&casbinRules).Error
|
||||
}
|
||||
|
||||
func (casbinService *CasbinService) FreshCasbin() (err error) {
|
||||
e := casbinService.Casbin()
|
||||
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
|
||||
}
|
112
service/system/sys_dictionary.go
Normal file
112
service/system/sys_dictionary.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateSysDictionary
|
||||
//@description: 创建字典数据
|
||||
//@param: sysDictionary model.SysDictionary
|
||||
//@return: err error
|
||||
|
||||
type DictionaryService struct{}
|
||||
|
||||
var DictionaryServiceApp = new(DictionaryService)
|
||||
|
||||
func (dictionaryService *DictionaryService) CreateSysDictionary(sysDictionary system.SysDictionary) (err error) {
|
||||
if (!errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound)) {
|
||||
return errors.New("存在相同的type,不允许创建")
|
||||
}
|
||||
err = global.GVA_DB.Create(&sysDictionary).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteSysDictionary
|
||||
//@description: 删除字典数据
|
||||
//@param: sysDictionary model.SysDictionary
|
||||
//@return: err error
|
||||
|
||||
func (dictionaryService *DictionaryService) DeleteSysDictionary(sysDictionary system.SysDictionary) (err error) {
|
||||
err = global.GVA_DB.Where("id = ?", sysDictionary.ID).Preload("SysDictionaryDetails").First(&sysDictionary).Error
|
||||
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("请不要搞事")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = global.GVA_DB.Delete(&sysDictionary).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sysDictionary.SysDictionaryDetails != nil {
|
||||
return global.GVA_DB.Where("sys_dictionary_id=?", sysDictionary.ID).Delete(sysDictionary.SysDictionaryDetails).Error
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateSysDictionary
|
||||
//@description: 更新字典数据
|
||||
//@param: sysDictionary *model.SysDictionary
|
||||
//@return: err error
|
||||
|
||||
func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *system.SysDictionary) (err error) {
|
||||
var dict system.SysDictionary
|
||||
sysDictionaryMap := map[string]interface{}{
|
||||
"Name": sysDictionary.Name,
|
||||
"Type": sysDictionary.Type,
|
||||
"Status": sysDictionary.Status,
|
||||
"Desc": sysDictionary.Desc,
|
||||
}
|
||||
err = global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Debug(err.Error())
|
||||
return errors.New("查询字典数据失败")
|
||||
}
|
||||
if dict.Type != sysDictionary.Type {
|
||||
if !errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound) {
|
||||
return errors.New("存在相同的type,不允许创建")
|
||||
}
|
||||
}
|
||||
err = global.GVA_DB.Model(&dict).Updates(sysDictionaryMap).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetSysDictionary
|
||||
//@description: 根据id或者type获取字典单条数据
|
||||
//@param: Type string, Id uint
|
||||
//@return: err error, sysDictionary model.SysDictionary
|
||||
|
||||
func (dictionaryService *DictionaryService) GetSysDictionary(Type string, Id uint, status *bool) (sysDictionary system.SysDictionary, err error) {
|
||||
var flag = false
|
||||
if status == nil {
|
||||
flag = true
|
||||
} else {
|
||||
flag = *status
|
||||
}
|
||||
err = global.GVA_DB.Where("(type = ? OR id = ?) and status = ?", Type, Id, flag).Preload("SysDictionaryDetails", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("status = ?", true).Order("sort")
|
||||
}).First(&sysDictionary).Error
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: GetSysDictionaryInfoList
|
||||
//@description: 分页获取字典列表
|
||||
//@param: info request.SysDictionarySearch
|
||||
//@return: err error, list interface{}, total int64
|
||||
|
||||
func (dictionaryService *DictionaryService) GetSysDictionaryInfoList() (list interface{}, err error) {
|
||||
var sysDictionarys []system.SysDictionary
|
||||
err = global.GVA_DB.Find(&sysDictionarys).Error
|
||||
return sysDictionarys, err
|
||||
}
|
118
service/system/sys_dictionary_detail.go
Normal file
118
service/system/sys_dictionary_detail.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: CreateSysDictionaryDetail
|
||||
//@description: 创建字典详情数据
|
||||
//@param: sysDictionaryDetail model.SysDictionaryDetail
|
||||
//@return: err error
|
||||
|
||||
type DictionaryDetailService struct{}
|
||||
|
||||
var DictionaryDetailServiceApp = new(DictionaryDetailService)
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) CreateSysDictionaryDetail(sysDictionaryDetail system.SysDictionaryDetail) (err error) {
|
||||
err = global.GVA_DB.Create(&sysDictionaryDetail).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteSysDictionaryDetail
|
||||
//@description: 删除字典详情数据
|
||||
//@param: sysDictionaryDetail model.SysDictionaryDetail
|
||||
//@return: err error
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) DeleteSysDictionaryDetail(sysDictionaryDetail system.SysDictionaryDetail) (err error) {
|
||||
err = global.GVA_DB.Delete(&sysDictionaryDetail).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateSysDictionaryDetail
|
||||
//@description: 更新字典详情数据
|
||||
//@param: sysDictionaryDetail *model.SysDictionaryDetail
|
||||
//@return: err error
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) UpdateSysDictionaryDetail(sysDictionaryDetail *system.SysDictionaryDetail) (err error) {
|
||||
err = global.GVA_DB.Save(sysDictionaryDetail).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetSysDictionaryDetail
|
||||
//@description: 根据id获取字典详情单条数据
|
||||
//@param: id uint
|
||||
//@return: sysDictionaryDetail system.SysDictionaryDetail, err error
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetail(id uint) (sysDictionaryDetail system.SysDictionaryDetail, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", id).First(&sysDictionaryDetail).Error
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetSysDictionaryDetailInfoList
|
||||
//@description: 分页获取字典详情列表
|
||||
//@param: info request.SysDictionaryDetailSearch
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetSysDictionaryDetailInfoList(info request.SysDictionaryDetailSearch) (list interface{}, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysDictionaryDetail{})
|
||||
var sysDictionaryDetails []system.SysDictionaryDetail
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.Label != "" {
|
||||
db = db.Where("label LIKE ?", "%"+info.Label+"%")
|
||||
}
|
||||
if info.Value != "" {
|
||||
db = db.Where("value = ?", info.Value)
|
||||
}
|
||||
if info.Status != nil {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
if info.SysDictionaryID != 0 {
|
||||
db = db.Where("sys_dictionary_id = ?", info.SysDictionaryID)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Order("sort").Find(&sysDictionaryDetails).Error
|
||||
return sysDictionaryDetails, total, err
|
||||
}
|
||||
|
||||
// 按照字典id获取字典全部内容的方法
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetDictionaryList(dictionaryID uint) (list []system.SysDictionaryDetail, err error) {
|
||||
var sysDictionaryDetails []system.SysDictionaryDetail
|
||||
err = global.GVA_DB.Find(&sysDictionaryDetails, "sys_dictionary_id = ?", dictionaryID).Error
|
||||
return sysDictionaryDetails, err
|
||||
}
|
||||
|
||||
// 按照字典type获取字典全部内容的方法
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetDictionaryListByType(t string) (list []system.SysDictionaryDetail, err error) {
|
||||
var sysDictionaryDetails []system.SysDictionaryDetail
|
||||
db := global.GVA_DB.Model(&system.SysDictionaryDetail{}).Joins("JOIN sys_dictionaries ON sys_dictionaries.id = sys_dictionary_details.sys_dictionary_id")
|
||||
err = db.Debug().Find(&sysDictionaryDetails, "type = ?", t).Error
|
||||
return sysDictionaryDetails, err
|
||||
}
|
||||
|
||||
// 按照字典id+字典内容value获取单条字典内容
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByValue(dictionaryID uint, value string) (detail system.SysDictionaryDetail, err error) {
|
||||
var sysDictionaryDetail system.SysDictionaryDetail
|
||||
err = global.GVA_DB.First(&sysDictionaryDetail, "sys_dictionary_id = ? and value = ?", dictionaryID, value).Error
|
||||
return sysDictionaryDetail, err
|
||||
}
|
||||
|
||||
// 按照字典type+字典内容value获取单条字典内容
|
||||
func (dictionaryDetailService *DictionaryDetailService) GetDictionaryInfoByTypeValue(t string, value string) (detail system.SysDictionaryDetail, err error) {
|
||||
var sysDictionaryDetails system.SysDictionaryDetail
|
||||
db := global.GVA_DB.Model(&system.SysDictionaryDetail{}).Joins("JOIN sys_dictionaries ON sys_dictionaries.id = sys_dictionary_details.sys_dictionary_id")
|
||||
err = db.First(&sysDictionaryDetails, "sys_dictionaries.type = ? and sys_dictionary_details.value = ?", t, value).Error
|
||||
return sysDictionaryDetails, err
|
||||
}
|
424
service/system/sys_export_template.go
Normal file
424
service/system/sys_export_template.go
Normal file
@@ -0,0 +1,424 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
systemReq "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SysExportTemplateService struct {
|
||||
}
|
||||
|
||||
var SysExportTemplateServiceApp = new(SysExportTemplateService)
|
||||
|
||||
// CreateSysExportTemplate 创建导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) {
|
||||
err = global.GVA_DB.Create(sysExportTemplate).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplate 删除导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
||||
err = global.GVA_DB.Delete(&sysExportTemplate).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplateByIds 批量删除导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplateByIds(ids request.IdsReq) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]system.SysExportTemplate{}, "id in ?", ids.Ids).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateSysExportTemplate 更新导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
conditions := sysExportTemplate.Conditions
|
||||
e := tx.Delete(&[]system.Condition{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
sysExportTemplate.Conditions = nil
|
||||
|
||||
joins := sysExportTemplate.JoinTemplate
|
||||
e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
sysExportTemplate.JoinTemplate = nil
|
||||
|
||||
e = tx.Updates(&sysExportTemplate).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if len(conditions) > 0 {
|
||||
for i := range conditions {
|
||||
conditions[i].ID = 0
|
||||
}
|
||||
e = tx.Create(&conditions).Error
|
||||
}
|
||||
if len(joins) > 0 {
|
||||
for i := range joins {
|
||||
joins[i].ID = 0
|
||||
}
|
||||
e = tx.Create(&joins).Error
|
||||
}
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// GetSysExportTemplate 根据id获取导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetSysExportTemplateInfoList 分页获取导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateInfoList(info systemReq.SysExportTemplateSearch) (list []system.SysExportTemplate, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysExportTemplate{})
|
||||
var sysExportTemplates []system.SysExportTemplate
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Name != "" {
|
||||
db = db.Where("name LIKE ?", "%"+info.Name+"%")
|
||||
}
|
||||
if info.TableName != "" {
|
||||
db = db.Where("table_name = ?", info.TableName)
|
||||
}
|
||||
if info.TemplateID != "" {
|
||||
db = db.Where("template_id = ?", info.TemplateID)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Find(&sysExportTemplates).Error
|
||||
return sysExportTemplates, total, err
|
||||
}
|
||||
|
||||
// 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 template system.SysExportTemplate
|
||||
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
f := excelize.NewFile()
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
// Create a new sheet.
|
||||
index, err := f.NewSheet("Sheet1")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var tableTitle []string
|
||||
var selectKeyFmt []string
|
||||
for _, key := range columns {
|
||||
selectKeyFmt = append(selectKeyFmt, key)
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
selects := strings.Join(selectKeyFmt, ", ")
|
||||
var tableMap []map[string]interface{}
|
||||
db := global.GVA_DB
|
||||
if template.DBName != "" {
|
||||
db = global.MustGetGlobalDBByDBName(template.DBName)
|
||||
}
|
||||
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
db = db.Joins(join.JOINS + " " + join.Table + " ON " + join.ON)
|
||||
}
|
||||
}
|
||||
|
||||
db = db.Select(selects).Table(template.TableName)
|
||||
|
||||
if len(template.Conditions) > 0 {
|
||||
for _, condition := range template.Conditions {
|
||||
sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator)
|
||||
value := values.Get(condition.From)
|
||||
if value != "" {
|
||||
if condition.Operator == "LIKE" {
|
||||
value = "%" + value + "%"
|
||||
}
|
||||
db = db.Where(sql, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 通过参数传入limit
|
||||
limit := values.Get("limit")
|
||||
if limit != "" {
|
||||
l, e := strconv.Atoi(limit)
|
||||
if e == nil {
|
||||
db = db.Limit(l)
|
||||
}
|
||||
}
|
||||
// 模板的默认limit
|
||||
if limit == "" && template.Limit != nil && *template.Limit != 0 {
|
||||
db = db.Limit(*template.Limit)
|
||||
}
|
||||
|
||||
// 通过参数传入offset
|
||||
offset := values.Get("offset")
|
||||
if offset != "" {
|
||||
o, e := strconv.Atoi(offset)
|
||||
if e == nil {
|
||||
db = db.Offset(o)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前表的所有字段
|
||||
table := template.TableName
|
||||
orderColumns, err := db.Migrator().ColumnTypes(table)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// 创建一个 map 来存储字段名
|
||||
fields := make(map[string]bool)
|
||||
|
||||
for _, column := range orderColumns {
|
||||
fields[column.Name()] = true
|
||||
}
|
||||
|
||||
// 通过参数传入order
|
||||
order := values.Get("order")
|
||||
|
||||
if order == "" && template.Order != "" {
|
||||
// 如果没有order入参,这里会使用模板的默认排序
|
||||
order = template.Order
|
||||
}
|
||||
|
||||
if order != "" {
|
||||
checkOrderArr := strings.Split(order, " ")
|
||||
orderStr := ""
|
||||
// 检查请求的排序字段是否在字段列表中
|
||||
if _, ok := fields[checkOrderArr[0]]; !ok {
|
||||
return nil, "", fmt.Errorf("order by %s is not in the fields", order)
|
||||
}
|
||||
orderStr = checkOrderArr[0]
|
||||
if len(checkOrderArr) > 1 {
|
||||
if checkOrderArr[1] != "asc" && checkOrderArr[1] != "desc" {
|
||||
return nil, "", fmt.Errorf("order by %s is not secure", order)
|
||||
}
|
||||
orderStr = orderStr + " " + checkOrderArr[1]
|
||||
}
|
||||
db = db.Order(orderStr)
|
||||
}
|
||||
|
||||
err = db.Debug().Find(&tableMap).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var rows [][]string
|
||||
rows = append(rows, tableTitle)
|
||||
for _, exTable := range tableMap {
|
||||
var row []string
|
||||
for _, column := range columns {
|
||||
column = strings.ReplaceAll(column, "\"", "")
|
||||
column = strings.ReplaceAll(column, "`", "")
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
columnAs := strings.Split(column, " as ")
|
||||
if len(columnAs) > 1 {
|
||||
column = strings.TrimSpace(strings.Split(column, " as ")[1])
|
||||
} else {
|
||||
columnArr := strings.Split(column, ".")
|
||||
if len(columnArr) > 1 {
|
||||
column = strings.Split(column, ".")[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
// 需要对时间类型特殊处理
|
||||
if t, ok := exTable[column].(time.Time); ok {
|
||||
row = append(row, t.Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
row = append(row, fmt.Sprintf("%v", exTable[column]))
|
||||
}
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
for i, row := range rows {
|
||||
for j, colCell := range row {
|
||||
sErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(j+1), i+1), colCell)
|
||||
if sErr != nil {
|
||||
return nil, "", sErr
|
||||
}
|
||||
}
|
||||
}
|
||||
f.SetActiveSheet(index)
|
||||
file, err = f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return file, template.Name, nil
|
||||
}
|
||||
|
||||
// ExportTemplate 导出Excel模板
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templateID string) (file *bytes.Buffer, name string, err error) {
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
f := excelize.NewFile()
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
// Create a new sheet.
|
||||
index, err := f.NewSheet("Sheet1")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
var templateInfoMap = make(map[string]string)
|
||||
|
||||
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
||||
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var tableTitle []string
|
||||
for _, key := range columns {
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
for i := range tableTitle {
|
||||
fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i])
|
||||
if fErr != nil {
|
||||
return nil, "", fErr
|
||||
}
|
||||
}
|
||||
f.SetActiveSheet(index)
|
||||
file, err = f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return file, template.Name, nil
|
||||
}
|
||||
|
||||
// ImportExcel 导入Excel
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) {
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
f, err := excelize.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := f.GetRows("Sheet1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var templateInfoMap = make(map[string]string)
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var titleKeyMap = make(map[string]string)
|
||||
for key, title := range templateInfoMap {
|
||||
titleKeyMap[title] = key
|
||||
}
|
||||
|
||||
db := global.GVA_DB
|
||||
if template.DBName != "" {
|
||||
db = global.MustGetGlobalDBByDBName(template.DBName)
|
||||
}
|
||||
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
excelTitle := rows[0]
|
||||
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 {
|
||||
key := titleKeyMap[excelTitle[ii]]
|
||||
item[key] = value
|
||||
}
|
||||
|
||||
needCreated := tx.Migrator().HasColumn(template.TableName, "created_at")
|
||||
needUpdated := tx.Migrator().HasColumn(template.TableName, "updated_at")
|
||||
|
||||
if item["created_at"] == nil && needCreated {
|
||||
item["created_at"] = time.Now()
|
||||
}
|
||||
if item["updated_at"] == nil && needUpdated {
|
||||
item["updated_at"] = time.Now()
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
cErr := tx.Table(template.TableName).CreateInBatches(&items, 1000).Error
|
||||
return cErr
|
||||
})
|
||||
}
|
||||
|
||||
func getColumnName(n int) string {
|
||||
columnName := ""
|
||||
for n > 0 {
|
||||
n--
|
||||
columnName = string(rune('A'+n%26)) + columnName
|
||||
n /= 26
|
||||
}
|
||||
return columnName
|
||||
}
|
189
service/system/sys_initdb.go
Normal file
189
service/system/sys_initdb.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"gorm.io/gorm"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
Mysql = "mysql"
|
||||
Pgsql = "pgsql"
|
||||
Sqlite = "sqlite"
|
||||
Mssql = "mssql"
|
||||
InitSuccess = "\n[%v] --> 初始数据成功!\n"
|
||||
InitDataExist = "\n[%v] --> %v 的初始数据已存在!\n"
|
||||
InitDataFailed = "\n[%v] --> %v 初始数据失败! \nerr: %+v\n"
|
||||
InitDataSuccess = "\n[%v] --> %v 初始数据成功!\n"
|
||||
)
|
||||
|
||||
const (
|
||||
InitOrderSystem = 10
|
||||
InitOrderInternal = 1000
|
||||
InitOrderExternal = 100000
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingDBContext = errors.New("missing db in context")
|
||||
ErrMissingDependentContext = errors.New("missing dependent value in context")
|
||||
ErrDBTypeMismatch = errors.New("db type mismatch")
|
||||
)
|
||||
|
||||
// SubInitializer 提供 source/*/init() 使用的接口,每个 initializer 完成一个初始化过程
|
||||
type SubInitializer interface {
|
||||
InitializerName() string // 不一定代表单独一个表,所以改成了更宽泛的语义
|
||||
MigrateTable(ctx context.Context) (next context.Context, err error)
|
||||
InitializeData(ctx context.Context) (next context.Context, err error)
|
||||
TableCreated(ctx context.Context) bool
|
||||
DataInserted(ctx context.Context) bool
|
||||
}
|
||||
|
||||
// TypedDBInitHandler 执行传入的 initializer
|
||||
type TypedDBInitHandler interface {
|
||||
EnsureDB(ctx context.Context, conf *request.InitDB) (context.Context, error) // 建库,失败属于 fatal error,因此让它 panic
|
||||
WriteConfig(ctx context.Context) error // 回写配置
|
||||
InitTables(ctx context.Context, inits initSlice) error // 建表 handler
|
||||
InitData(ctx context.Context, inits initSlice) error // 建数据 handler
|
||||
}
|
||||
|
||||
// orderedInitializer 组合一个顺序字段,以供排序
|
||||
type orderedInitializer struct {
|
||||
order int
|
||||
SubInitializer
|
||||
}
|
||||
|
||||
// initSlice 供 initializer 排序依赖时使用
|
||||
type initSlice []*orderedInitializer
|
||||
|
||||
var (
|
||||
initializers initSlice
|
||||
cache map[string]*orderedInitializer
|
||||
)
|
||||
|
||||
// RegisterInit 注册要执行的初始化过程,会在 InitDB() 时调用
|
||||
func RegisterInit(order int, i SubInitializer) {
|
||||
if initializers == nil {
|
||||
initializers = initSlice{}
|
||||
}
|
||||
if cache == nil {
|
||||
cache = map[string]*orderedInitializer{}
|
||||
}
|
||||
name := i.InitializerName()
|
||||
if _, existed := cache[name]; existed {
|
||||
panic(fmt.Sprintf("Name conflict on %s", name))
|
||||
}
|
||||
ni := orderedInitializer{order, i}
|
||||
initializers = append(initializers, &ni)
|
||||
cache[name] = &ni
|
||||
}
|
||||
|
||||
/* ---- * service * ---- */
|
||||
|
||||
type InitDBService struct{}
|
||||
|
||||
// InitDB 创建数据库并初始化 总入口
|
||||
func (initDBService *InitDBService) InitDB(conf request.InitDB) (err error) {
|
||||
ctx := context.TODO()
|
||||
ctx = context.WithValue(ctx, "adminPassword", conf.AdminPassword)
|
||||
if len(initializers) == 0 {
|
||||
return errors.New("无可用初始化过程,请检查初始化是否已执行完成")
|
||||
}
|
||||
sort.Sort(&initializers) // 保证有依赖的 initializer 排在后面执行
|
||||
// Note: 若 initializer 只有单一依赖,可以写为 B=A+1, C=A+1; 由于 BC 之间没有依赖关系,所以谁先谁后并不影响初始化
|
||||
// 若存在多个依赖,可以写为 C=A+B, D=A+B+C, E=A+1;
|
||||
// C必然>A|B,因此在AB之后执行,D必然>A|B|C,因此在ABC后执行,而E只依赖A,顺序与CD无关,因此E与CD哪个先执行并不影响
|
||||
var initHandler TypedDBInitHandler
|
||||
switch conf.DBType {
|
||||
case "mysql":
|
||||
initHandler = NewMysqlInitHandler()
|
||||
ctx = context.WithValue(ctx, "dbtype", "mysql")
|
||||
case "pgsql":
|
||||
initHandler = NewPgsqlInitHandler()
|
||||
ctx = context.WithValue(ctx, "dbtype", "pgsql")
|
||||
case "sqlite":
|
||||
initHandler = NewSqliteInitHandler()
|
||||
ctx = context.WithValue(ctx, "dbtype", "sqlite")
|
||||
case "mssql":
|
||||
initHandler = NewMssqlInitHandler()
|
||||
ctx = context.WithValue(ctx, "dbtype", "mssql")
|
||||
default:
|
||||
initHandler = NewMysqlInitHandler()
|
||||
ctx = context.WithValue(ctx, "dbtype", "mysql")
|
||||
}
|
||||
ctx, err = initHandler.EnsureDB(ctx, &conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := ctx.Value("db").(*gorm.DB)
|
||||
global.GVA_DB = db
|
||||
|
||||
if err = initHandler.InitTables(ctx, initializers); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = initHandler.InitData(ctx, initializers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = initHandler.WriteConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
initializers = initSlice{}
|
||||
cache = map[string]*orderedInitializer{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDatabase 创建数据库( EnsureDB() 中调用 )
|
||||
func createDatabase(dsn string, driver string, createSql string) error {
|
||||
db, err := sql.Open(driver, dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(db *sql.DB) {
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}(db)
|
||||
if err = db.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec(createSql)
|
||||
return err
|
||||
}
|
||||
|
||||
// createTables 创建表(默认 dbInitHandler.initTables 行为)
|
||||
func createTables(ctx context.Context, inits initSlice) error {
|
||||
next, cancel := context.WithCancel(ctx)
|
||||
defer func(c func()) { c() }(cancel)
|
||||
for _, init := range inits {
|
||||
if init.TableCreated(next) {
|
||||
continue
|
||||
}
|
||||
if n, err := init.MigrateTable(next); err != nil {
|
||||
return err
|
||||
} else {
|
||||
next = n
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/* -- sortable interface -- */
|
||||
|
||||
func (a initSlice) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a initSlice) Less(i, j int) bool {
|
||||
return a[i].order < a[j].order
|
||||
}
|
||||
|
||||
func (a initSlice) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
92
service/system/sys_initdb_mssql.go
Normal file
92
service/system/sys_initdb_mssql.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"git.echol.cn/loser/xiecheng_server/config"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gookit/color"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type MssqlInitHandler struct{}
|
||||
|
||||
func NewMssqlInitHandler() *MssqlInitHandler {
|
||||
return &MssqlInitHandler{}
|
||||
}
|
||||
|
||||
// WriteConfig mssql回写配置
|
||||
func (h MssqlInitHandler) WriteConfig(ctx context.Context) error {
|
||||
c, ok := ctx.Value("config").(config.Mssql)
|
||||
if !ok {
|
||||
return errors.New("mssql config invalid")
|
||||
}
|
||||
global.GVA_CONFIG.System.DbType = "mssql"
|
||||
global.GVA_CONFIG.Mssql = c
|
||||
global.GVA_CONFIG.JWT.SigningKey = uuid.New().String()
|
||||
cs := utils.StructToMap(global.GVA_CONFIG)
|
||||
for k, v := range cs {
|
||||
global.GVA_VP.Set(k, v)
|
||||
}
|
||||
global.GVA_ACTIVE_DBNAME = &c.Dbname
|
||||
return global.GVA_VP.WriteConfig()
|
||||
}
|
||||
|
||||
// EnsureDB 创建数据库并初始化 mssql
|
||||
func (h MssqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) {
|
||||
if s, ok := ctx.Value("dbtype").(string); !ok || s != "mssql" {
|
||||
return ctx, ErrDBTypeMismatch
|
||||
}
|
||||
|
||||
c := conf.ToMssqlConfig()
|
||||
next = context.WithValue(ctx, "config", c)
|
||||
if c.Dbname == "" {
|
||||
return ctx, nil
|
||||
} // 如果没有数据库名, 则跳出初始化数据
|
||||
|
||||
dsn := conf.MssqlEmptyDsn()
|
||||
|
||||
mssqlConfig := sqlserver.Config{
|
||||
DSN: dsn, // DSN data source name
|
||||
DefaultStringSize: 191, // string 类型字段的默认长度
|
||||
}
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
if db, err = gorm.Open(sqlserver.New(mssqlConfig), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
next = context.WithValue(next, "db", db)
|
||||
return next, err
|
||||
}
|
||||
|
||||
func (h MssqlInitHandler) InitTables(ctx context.Context, inits initSlice) error {
|
||||
return createTables(ctx, inits)
|
||||
}
|
||||
|
||||
func (h MssqlInitHandler) InitData(ctx context.Context, inits initSlice) error {
|
||||
next, cancel := context.WithCancel(ctx)
|
||||
defer func(c func()) { c() }(cancel)
|
||||
for _, init := range inits {
|
||||
if init.DataInserted(next) {
|
||||
color.Info.Printf(InitDataExist, Mssql, init.InitializerName())
|
||||
continue
|
||||
}
|
||||
if n, err := init.InitializeData(next); err != nil {
|
||||
color.Info.Printf(InitDataFailed, Mssql, init.InitializerName(), err)
|
||||
return err
|
||||
} else {
|
||||
next = n
|
||||
color.Info.Printf(InitDataSuccess, Mssql, init.InitializerName())
|
||||
}
|
||||
}
|
||||
color.Info.Printf(InitSuccess, Mssql)
|
||||
return nil
|
||||
}
|
97
service/system/sys_initdb_mysql.go
Normal file
97
service/system/sys_initdb_mysql.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/config"
|
||||
"github.com/gookit/color"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MysqlInitHandler struct{}
|
||||
|
||||
func NewMysqlInitHandler() *MysqlInitHandler {
|
||||
return &MysqlInitHandler{}
|
||||
}
|
||||
|
||||
// WriteConfig mysql回写配置
|
||||
func (h MysqlInitHandler) WriteConfig(ctx context.Context) error {
|
||||
c, ok := ctx.Value("config").(config.Mysql)
|
||||
if !ok {
|
||||
return errors.New("mysql config invalid")
|
||||
}
|
||||
global.GVA_CONFIG.System.DbType = "mysql"
|
||||
global.GVA_CONFIG.Mysql = c
|
||||
global.GVA_CONFIG.JWT.SigningKey = uuid.New().String()
|
||||
cs := utils.StructToMap(global.GVA_CONFIG)
|
||||
for k, v := range cs {
|
||||
global.GVA_VP.Set(k, v)
|
||||
}
|
||||
global.GVA_ACTIVE_DBNAME = &c.Dbname
|
||||
return global.GVA_VP.WriteConfig()
|
||||
}
|
||||
|
||||
// EnsureDB 创建数据库并初始化 mysql
|
||||
func (h MysqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) {
|
||||
if s, ok := ctx.Value("dbtype").(string); !ok || s != "mysql" {
|
||||
return ctx, ErrDBTypeMismatch
|
||||
}
|
||||
|
||||
c := conf.ToMysqlConfig()
|
||||
next = context.WithValue(ctx, "config", c)
|
||||
if c.Dbname == "" {
|
||||
return ctx, nil
|
||||
} // 如果没有数据库名, 则跳出初始化数据
|
||||
|
||||
dsn := conf.MysqlEmptyDsn()
|
||||
createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", c.Dbname)
|
||||
if err = createDatabase(dsn, "mysql", createSql); err != nil {
|
||||
return nil, err
|
||||
} // 创建数据库
|
||||
|
||||
var db *gorm.DB
|
||||
if db, err = gorm.Open(mysql.New(mysql.Config{
|
||||
DSN: c.Dsn(), // DSN data source name
|
||||
DefaultStringSize: 191, // string 类型字段的默认长度
|
||||
SkipInitializeWithVersion: true, // 根据版本自动配置
|
||||
}), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
next = context.WithValue(next, "db", db)
|
||||
return next, err
|
||||
}
|
||||
|
||||
func (h MysqlInitHandler) InitTables(ctx context.Context, inits initSlice) error {
|
||||
return createTables(ctx, inits)
|
||||
}
|
||||
|
||||
func (h MysqlInitHandler) InitData(ctx context.Context, inits initSlice) error {
|
||||
next, cancel := context.WithCancel(ctx)
|
||||
defer func(c func()) { c() }(cancel)
|
||||
for _, init := range inits {
|
||||
if init.DataInserted(next) {
|
||||
color.Info.Printf(InitDataExist, Mysql, init.InitializerName())
|
||||
continue
|
||||
}
|
||||
if n, err := init.InitializeData(next); err != nil {
|
||||
color.Info.Printf(InitDataFailed, Mysql, init.InitializerName(), err)
|
||||
return err
|
||||
} else {
|
||||
next = n
|
||||
color.Info.Printf(InitDataSuccess, Mysql, init.InitializerName())
|
||||
}
|
||||
}
|
||||
color.Info.Printf(InitSuccess, Mysql)
|
||||
return nil
|
||||
}
|
101
service/system/sys_initdb_pgsql.go
Normal file
101
service/system/sys_initdb_pgsql.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/config"
|
||||
"github.com/gookit/color"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type PgsqlInitHandler struct{}
|
||||
|
||||
func NewPgsqlInitHandler() *PgsqlInitHandler {
|
||||
return &PgsqlInitHandler{}
|
||||
}
|
||||
|
||||
// WriteConfig pgsql 回写配置
|
||||
func (h PgsqlInitHandler) WriteConfig(ctx context.Context) error {
|
||||
c, ok := ctx.Value("config").(config.Pgsql)
|
||||
if !ok {
|
||||
return errors.New("postgresql config invalid")
|
||||
}
|
||||
global.GVA_CONFIG.System.DbType = "pgsql"
|
||||
global.GVA_CONFIG.Pgsql = c
|
||||
global.GVA_CONFIG.JWT.SigningKey = uuid.New().String()
|
||||
cs := utils.StructToMap(global.GVA_CONFIG)
|
||||
for k, v := range cs {
|
||||
global.GVA_VP.Set(k, v)
|
||||
}
|
||||
global.GVA_ACTIVE_DBNAME = &c.Dbname
|
||||
return global.GVA_VP.WriteConfig()
|
||||
}
|
||||
|
||||
// EnsureDB 创建数据库并初始化 pg
|
||||
func (h PgsqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) {
|
||||
if s, ok := ctx.Value("dbtype").(string); !ok || s != "pgsql" {
|
||||
return ctx, ErrDBTypeMismatch
|
||||
}
|
||||
|
||||
c := conf.ToPgsqlConfig()
|
||||
next = context.WithValue(ctx, "config", c)
|
||||
if c.Dbname == "" {
|
||||
return ctx, nil
|
||||
} // 如果没有数据库名, 则跳出初始化数据
|
||||
|
||||
dsn := conf.PgsqlEmptyDsn()
|
||||
var createSql string
|
||||
if conf.Template != "" {
|
||||
createSql = fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s;", c.Dbname, conf.Template)
|
||||
} else {
|
||||
createSql = fmt.Sprintf("CREATE DATABASE %s;", c.Dbname)
|
||||
}
|
||||
if err = createDatabase(dsn, "pgx", createSql); err != nil {
|
||||
return nil, err
|
||||
} // 创建数据库
|
||||
|
||||
var db *gorm.DB
|
||||
if db, err = gorm.Open(postgres.New(postgres.Config{
|
||||
DSN: c.Dsn(), // DSN data source name
|
||||
PreferSimpleProtocol: false,
|
||||
}), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
next = context.WithValue(next, "db", db)
|
||||
return next, err
|
||||
}
|
||||
|
||||
func (h PgsqlInitHandler) InitTables(ctx context.Context, inits initSlice) error {
|
||||
return createTables(ctx, inits)
|
||||
}
|
||||
|
||||
func (h PgsqlInitHandler) InitData(ctx context.Context, inits initSlice) error {
|
||||
next, cancel := context.WithCancel(ctx)
|
||||
defer func(c func()) { c() }(cancel)
|
||||
for i := 0; i < len(inits); i++ {
|
||||
if inits[i].DataInserted(next) {
|
||||
color.Info.Printf(InitDataExist, Pgsql, inits[i].InitializerName())
|
||||
continue
|
||||
}
|
||||
if n, err := inits[i].InitializeData(next); err != nil {
|
||||
color.Info.Printf(InitDataFailed, Pgsql, inits[i].InitializerName(), err)
|
||||
return err
|
||||
} else {
|
||||
next = n
|
||||
color.Info.Printf(InitDataSuccess, Pgsql, inits[i].InitializerName())
|
||||
}
|
||||
}
|
||||
color.Info.Printf(InitSuccess, Pgsql)
|
||||
return nil
|
||||
}
|
88
service/system/sys_initdb_sqlite.go
Normal file
88
service/system/sys_initdb_sqlite.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gookit/color"
|
||||
"gorm.io/gorm"
|
||||
"path/filepath"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/config"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
)
|
||||
|
||||
type SqliteInitHandler struct{}
|
||||
|
||||
func NewSqliteInitHandler() *SqliteInitHandler {
|
||||
return &SqliteInitHandler{}
|
||||
}
|
||||
|
||||
// WriteConfig mysql回写配置
|
||||
func (h SqliteInitHandler) WriteConfig(ctx context.Context) error {
|
||||
c, ok := ctx.Value("config").(config.Sqlite)
|
||||
if !ok {
|
||||
return errors.New("sqlite config invalid")
|
||||
}
|
||||
global.GVA_CONFIG.System.DbType = "sqlite"
|
||||
global.GVA_CONFIG.Sqlite = c
|
||||
global.GVA_CONFIG.JWT.SigningKey = uuid.New().String()
|
||||
cs := utils.StructToMap(global.GVA_CONFIG)
|
||||
for k, v := range cs {
|
||||
global.GVA_VP.Set(k, v)
|
||||
}
|
||||
global.GVA_ACTIVE_DBNAME = &c.Dbname
|
||||
return global.GVA_VP.WriteConfig()
|
||||
}
|
||||
|
||||
// EnsureDB 创建数据库并初始化 sqlite
|
||||
func (h SqliteInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (next context.Context, err error) {
|
||||
if s, ok := ctx.Value("dbtype").(string); !ok || s != "sqlite" {
|
||||
return ctx, ErrDBTypeMismatch
|
||||
}
|
||||
|
||||
c := conf.ToSqliteConfig()
|
||||
next = context.WithValue(ctx, "config", c)
|
||||
if c.Dbname == "" {
|
||||
return ctx, nil
|
||||
} // 如果没有数据库名, 则跳出初始化数据
|
||||
|
||||
dsn := conf.SqliteEmptyDsn()
|
||||
|
||||
var db *gorm.DB
|
||||
if db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
}); err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
|
||||
next = context.WithValue(next, "db", db)
|
||||
return next, err
|
||||
}
|
||||
|
||||
func (h SqliteInitHandler) InitTables(ctx context.Context, inits initSlice) error {
|
||||
return createTables(ctx, inits)
|
||||
}
|
||||
|
||||
func (h SqliteInitHandler) InitData(ctx context.Context, inits initSlice) error {
|
||||
next, cancel := context.WithCancel(ctx)
|
||||
defer func(c func()) { c() }(cancel)
|
||||
for _, init := range inits {
|
||||
if init.DataInserted(next) {
|
||||
color.Info.Printf(InitDataExist, Sqlite, init.InitializerName())
|
||||
continue
|
||||
}
|
||||
if n, err := init.InitializeData(next); err != nil {
|
||||
color.Info.Printf(InitDataFailed, Sqlite, init.InitializerName(), err)
|
||||
return err
|
||||
} else {
|
||||
next = n
|
||||
color.Info.Printf(InitDataSuccess, Sqlite, init.InitializerName())
|
||||
}
|
||||
}
|
||||
color.Info.Printf(InitSuccess, Sqlite)
|
||||
return nil
|
||||
}
|
289
service/system/sys_menu.go
Normal file
289
service/system/sys_menu.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: getMenuTreeMap
|
||||
//@description: 获取路由总树map
|
||||
//@param: authorityId string
|
||||
//@return: treeMap map[string][]system.SysMenu, err error
|
||||
|
||||
type MenuService struct{}
|
||||
|
||||
var MenuServiceApp = new(MenuService)
|
||||
|
||||
func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[uint][]system.SysMenu, err error) {
|
||||
var allMenus []system.SysMenu
|
||||
var baseMenu []system.SysBaseMenu
|
||||
var btns []system.SysAuthorityBtn
|
||||
treeMap = make(map[uint][]system.SysMenu)
|
||||
|
||||
var SysAuthorityMenus []system.SysAuthorityMenu
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&SysAuthorityMenus).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var MenuIds []string
|
||||
|
||||
for i := range SysAuthorityMenus {
|
||||
MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Where("id in (?)", MenuIds).Order("sort").Preload("Parameters").Find(&baseMenu).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range baseMenu {
|
||||
allMenus = append(allMenus, system.SysMenu{
|
||||
SysBaseMenu: baseMenu[i],
|
||||
AuthorityId: authorityId,
|
||||
MenuId: baseMenu[i].ID,
|
||||
Parameters: baseMenu[i].Parameters,
|
||||
})
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Where("authority_id = ?", authorityId).Preload("SysBaseMenuBtn").Find(&btns).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var btnMap = make(map[uint]map[string]uint)
|
||||
for _, v := range btns {
|
||||
if btnMap[v.SysMenuID] == nil {
|
||||
btnMap[v.SysMenuID] = make(map[string]uint)
|
||||
}
|
||||
btnMap[v.SysMenuID][v.SysBaseMenuBtn.Name] = authorityId
|
||||
}
|
||||
for _, v := range allMenus {
|
||||
v.Btns = btnMap[v.SysBaseMenu.ID]
|
||||
treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
|
||||
}
|
||||
return treeMap, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetMenuTree
|
||||
//@description: 获取动态菜单树
|
||||
//@param: authorityId string
|
||||
//@return: menus []system.SysMenu, err error
|
||||
|
||||
func (menuService *MenuService) GetMenuTree(authorityId uint) (menus []system.SysMenu, err error) {
|
||||
menuTree, err := menuService.getMenuTreeMap(authorityId)
|
||||
menus = menuTree[0]
|
||||
for i := 0; i < len(menus); i++ {
|
||||
err = menuService.getChildrenList(&menus[i], menuTree)
|
||||
}
|
||||
return menus, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: getChildrenList
|
||||
//@description: 获取子菜单
|
||||
//@param: menu *model.SysMenu, treeMap map[string][]model.SysMenu
|
||||
//@return: err error
|
||||
|
||||
func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap map[uint][]system.SysMenu) (err error) {
|
||||
menu.Children = treeMap[menu.MenuId]
|
||||
for i := 0; i < len(menu.Children); i++ {
|
||||
err = menuService.getChildrenList(&menu.Children[i], treeMap)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetInfoList
|
||||
//@description: 获取路由分页
|
||||
//@return: list interface{}, total int64,err error
|
||||
|
||||
func (menuService *MenuService) GetInfoList(authorityID uint) (list interface{}, err error) {
|
||||
var menuList []system.SysBaseMenu
|
||||
treeMap, err := menuService.getBaseMenuTreeMap(authorityID)
|
||||
menuList = treeMap[0]
|
||||
for i := 0; i < len(menuList); i++ {
|
||||
err = menuService.getBaseChildrenList(&menuList[i], treeMap)
|
||||
}
|
||||
return menuList, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: getBaseChildrenList
|
||||
//@description: 获取菜单的子菜单
|
||||
//@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu
|
||||
//@return: err error
|
||||
|
||||
func (menuService *MenuService) getBaseChildrenList(menu *system.SysBaseMenu, treeMap map[uint][]system.SysBaseMenu) (err error) {
|
||||
menu.Children = treeMap[menu.ID]
|
||||
for i := 0; i < len(menu.Children); i++ {
|
||||
err = menuService.getBaseChildrenList(&menu.Children[i], treeMap)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: AddBaseMenu
|
||||
//@description: 添加基础路由
|
||||
//@param: menu model.SysBaseMenu
|
||||
//@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
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: getBaseMenuTreeMap
|
||||
//@description: 获取路由总树map
|
||||
//@return: treeMap map[string][]system.SysBaseMenu, err error
|
||||
|
||||
func (menuService *MenuService) getBaseMenuTreeMap(authorityID uint) (treeMap map[uint][]system.SysBaseMenu, err error) {
|
||||
parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var allMenus []system.SysBaseMenu
|
||||
treeMap = make(map[uint][]system.SysBaseMenu)
|
||||
db := global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters")
|
||||
|
||||
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
|
||||
if global.GVA_CONFIG.System.UseStrictAuth && parentAuthorityID != 0 {
|
||||
var authorityMenus []system.SysAuthorityMenu
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityID).Find(&authorityMenus).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var menuIds []string
|
||||
for i := range authorityMenus {
|
||||
menuIds = append(menuIds, authorityMenus[i].MenuId)
|
||||
}
|
||||
db = db.Where("id in (?)", menuIds)
|
||||
}
|
||||
|
||||
err = db.Find(&allMenus).Error
|
||||
for _, v := range allMenus {
|
||||
treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
|
||||
}
|
||||
return treeMap, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetBaseMenuTree
|
||||
//@description: 获取基础路由树
|
||||
//@return: menus []system.SysBaseMenu, err error
|
||||
|
||||
func (menuService *MenuService) GetBaseMenuTree(authorityID uint) (menus []system.SysBaseMenu, err error) {
|
||||
treeMap, err := menuService.getBaseMenuTreeMap(authorityID)
|
||||
menus = treeMap[0]
|
||||
for i := 0; i < len(menus); i++ {
|
||||
err = menuService.getBaseChildrenList(&menus[i], treeMap)
|
||||
}
|
||||
return menus, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: AddMenuAuthority
|
||||
//@description: 为角色增加menu树
|
||||
//@param: menus []model.SysBaseMenu, authorityId string
|
||||
//@return: err error
|
||||
|
||||
func (menuService *MenuService) AddMenuAuthority(menus []system.SysBaseMenu, adminAuthorityID, authorityId uint) (err error) {
|
||||
var auth system.SysAuthority
|
||||
auth.AuthorityId = authorityId
|
||||
auth.SysBaseMenus = menus
|
||||
|
||||
err = AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, authorityId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var authority system.SysAuthority
|
||||
_ = global.GVA_DB.First(&authority, "authority_id = ?", adminAuthorityID).Error
|
||||
var menuIds []string
|
||||
|
||||
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
|
||||
if global.GVA_CONFIG.System.UseStrictAuth && *authority.ParentId != 0 {
|
||||
var authorityMenus []system.SysAuthorityMenu
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", adminAuthorityID).Find(&authorityMenus).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range authorityMenus {
|
||||
menuIds = append(menuIds, authorityMenus[i].MenuId)
|
||||
}
|
||||
|
||||
for i := range menus {
|
||||
hasMenu := false
|
||||
for j := range menuIds {
|
||||
idStr := strconv.Itoa(int(menus[i].ID))
|
||||
if idStr == menuIds[j] {
|
||||
hasMenu = true
|
||||
}
|
||||
}
|
||||
if !hasMenu {
|
||||
return errors.New("添加失败,请勿跨级操作")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = AuthorityServiceApp.SetMenuAuthority(&auth)
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetMenuAuthority
|
||||
//@description: 查看当前角色树
|
||||
//@param: info *request.GetAuthorityId
|
||||
//@return: menus []system.SysMenu, err error
|
||||
|
||||
func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (menus []system.SysMenu, err error) {
|
||||
var baseMenu []system.SysBaseMenu
|
||||
var SysAuthorityMenus []system.SysAuthorityMenu
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", info.AuthorityId).Find(&SysAuthorityMenus).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var MenuIds []string
|
||||
|
||||
for i := range SysAuthorityMenus {
|
||||
MenuIds = append(MenuIds, SysAuthorityMenus[i].MenuId)
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Where("id in (?) ", MenuIds).Order("sort").Find(&baseMenu).Error
|
||||
|
||||
for i := range baseMenu {
|
||||
menus = append(menus, system.SysMenu{
|
||||
SysBaseMenu: baseMenu[i],
|
||||
AuthorityId: info.AuthorityId,
|
||||
MenuId: baseMenu[i].ID,
|
||||
Parameters: baseMenu[i].Parameters,
|
||||
})
|
||||
}
|
||||
return menus, err
|
||||
}
|
||||
|
||||
// UserAuthorityDefaultRouter 用户角色默认路由检查
|
||||
//
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
func (menuService *MenuService) UserAuthorityDefaultRouter(user *system.SysUser) {
|
||||
var menuIds []string
|
||||
err := global.GVA_DB.Model(&system.SysAuthorityMenu{}).Where("sys_authority_authority_id = ?", user.AuthorityId).Pluck("sys_base_menu_id", &menuIds).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var am system.SysBaseMenu
|
||||
err = global.GVA_DB.First(&am, "name = ? and id in (?)", user.Authority.DefaultRouter, menuIds).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
user.Authority.DefaultRouter = "404"
|
||||
}
|
||||
}
|
88
service/system/sys_operation_record.go
Normal file
88
service/system/sys_operation_record.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common/request"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
systemReq "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
)
|
||||
|
||||
//@author: [granty1](https://github.com/granty1)
|
||||
//@function: CreateSysOperationRecord
|
||||
//@description: 创建记录
|
||||
//@param: sysOperationRecord model.SysOperationRecord
|
||||
//@return: err error
|
||||
|
||||
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
|
||||
//@description: 批量删除记录
|
||||
//@param: ids request.IdsReq
|
||||
//@return: err error
|
||||
|
||||
func (operationRecordService *OperationRecordService) DeleteSysOperationRecordByIds(ids request.IdsReq) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]system.SysOperationRecord{}, "id in (?)", ids.Ids).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [granty1](https://github.com/granty1)
|
||||
//@function: DeleteSysOperationRecord
|
||||
//@description: 删除操作记录
|
||||
//@param: sysOperationRecord model.SysOperationRecord
|
||||
//@return: err error
|
||||
|
||||
func (operationRecordService *OperationRecordService) DeleteSysOperationRecord(sysOperationRecord system.SysOperationRecord) (err error) {
|
||||
err = global.GVA_DB.Delete(&sysOperationRecord).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [granty1](https://github.com/granty1)
|
||||
//@function: GetSysOperationRecord
|
||||
//@description: 根据id获取单条操作记录
|
||||
//@param: id uint
|
||||
//@return: sysOperationRecord system.SysOperationRecord, err error
|
||||
|
||||
func (operationRecordService *OperationRecordService) GetSysOperationRecord(id uint) (sysOperationRecord system.SysOperationRecord, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", id).First(&sysOperationRecord).Error
|
||||
return
|
||||
}
|
||||
|
||||
//@author: [granty1](https://github.com/granty1)
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetSysOperationRecordInfoList
|
||||
//@description: 分页获取操作记录列表
|
||||
//@param: info systemReq.SysOperationRecordSearch
|
||||
//@return: list interface{}, total int64, err error
|
||||
|
||||
func (operationRecordService *OperationRecordService) GetSysOperationRecordInfoList(info systemReq.SysOperationRecordSearch) (list interface{}, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysOperationRecord{})
|
||||
var sysOperationRecords []system.SysOperationRecord
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.Method != "" {
|
||||
db = db.Where("method = ?", info.Method)
|
||||
}
|
||||
if info.Path != "" {
|
||||
db = db.Where("path LIKE ?", "%"+info.Path+"%")
|
||||
}
|
||||
if info.Status != 0 {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Order("id desc").Limit(limit).Offset(offset).Preload("User").Find(&sysOperationRecords).Error
|
||||
return sysOperationRecords, total, err
|
||||
}
|
82
service/system/sys_params.go
Normal file
82
service/system/sys_params.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
systemReq "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
)
|
||||
|
||||
type SysParamsService struct{}
|
||||
|
||||
// CreateSysParams 创建参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) CreateSysParams(sysParams *system.SysParams) (err error) {
|
||||
err = global.GVA_DB.Create(sysParams).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysParams 删除参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) DeleteSysParams(ID string) (err error) {
|
||||
err = global.GVA_DB.Delete(&system.SysParams{}, "id = ?", ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysParamsByIds 批量删除参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) DeleteSysParamsByIds(IDs []string) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]system.SysParams{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateSysParams 更新参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) UpdateSysParams(sysParams system.SysParams) (err error) {
|
||||
err = global.GVA_DB.Model(&system.SysParams{}).Where("id = ?", sysParams.ID).Updates(&sysParams).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSysParams 根据ID获取参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) GetSysParams(ID string) (sysParams system.SysParams, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", ID).First(&sysParams).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetSysParamsInfoList 分页获取参数记录
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) GetSysParamsInfoList(info systemReq.SysParamsSearch) (list []system.SysParams, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysParams{})
|
||||
var sysParamss []system.SysParams
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Name != "" {
|
||||
db = db.Where("name LIKE ?", "%"+info.Name+"%")
|
||||
}
|
||||
if info.Key != "" {
|
||||
db = db.Where("key LIKE ?", "%"+info.Key+"%")
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Find(&sysParamss).Error
|
||||
return sysParamss, total, err
|
||||
}
|
||||
|
||||
// GetSysParam 根据key获取参数value
|
||||
// Author [Mr.奇淼](https://github.com/pixelmaxQm)
|
||||
func (sysParamsService *SysParamsService) GetSysParam(key string) (param system.SysParams, err error) {
|
||||
err = global.GVA_DB.Where(system.SysParams{Key: key}).First(¶m).Error
|
||||
return
|
||||
}
|
62
service/system/sys_system.go
Normal file
62
service/system/sys_system.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/xiecheng_server/config"
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetSystemConfig
|
||||
//@description: 读取配置文件
|
||||
//@return: conf config.Server, err error
|
||||
|
||||
type SystemConfigService struct{}
|
||||
|
||||
var SystemConfigServiceApp = new(SystemConfigService)
|
||||
|
||||
func (systemConfigService *SystemConfigService) GetSystemConfig() (conf config.Server, err error) {
|
||||
return global.GVA_CONFIG, nil
|
||||
}
|
||||
|
||||
// @description set system config,
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetSystemConfig
|
||||
//@description: 设置配置文件
|
||||
//@param: system model.System
|
||||
//@return: err error
|
||||
|
||||
func (systemConfigService *SystemConfigService) SetSystemConfig(system system.System) (err error) {
|
||||
cs := utils.StructToMap(system.Config)
|
||||
for k, v := range cs {
|
||||
global.GVA_VP.Set(k, v)
|
||||
}
|
||||
err = global.GVA_VP.WriteConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: GetServerInfo
|
||||
//@description: 获取服务器信息
|
||||
//@return: server *utils.Server, err error
|
||||
|
||||
func (systemConfigService *SystemConfigService) GetServerInfo() (server *utils.Server, err error) {
|
||||
var s utils.Server
|
||||
s.Os = utils.InitOS()
|
||||
if s.Cpu, err = utils.InitCPU(); err != nil {
|
||||
global.GVA_LOG.Error("func utils.InitCPU() Failed", zap.String("err", err.Error()))
|
||||
return &s, err
|
||||
}
|
||||
if s.Ram, err = utils.InitRAM(); err != nil {
|
||||
global.GVA_LOG.Error("func utils.InitRAM() Failed", zap.String("err", err.Error()))
|
||||
return &s, err
|
||||
}
|
||||
if s.Disk, err = utils.InitDisk(); err != nil {
|
||||
global.GVA_LOG.Error("func utils.InitDisk() Failed", zap.String("err", err.Error()))
|
||||
return &s, err
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
317
service/system/sys_user.go
Normal file
317
service/system/sys_user.go
Normal file
@@ -0,0 +1,317 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/xiecheng_server/model/common"
|
||||
systemReq "git.echol.cn/loser/xiecheng_server/model/system/request"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/xiecheng_server/global"
|
||||
"git.echol.cn/loser/xiecheng_server/model/system"
|
||||
"git.echol.cn/loser/xiecheng_server/utils"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: Register
|
||||
//@description: 用户注册
|
||||
//@param: u model.SysUser
|
||||
//@return: userInter system.SysUser, err error
|
||||
|
||||
type UserService struct{}
|
||||
|
||||
var UserServiceApp = new(UserService)
|
||||
|
||||
func (userService *UserService) Register(u system.SysUser) (userInter system.SysUser, err error) {
|
||||
var user system.SysUser
|
||||
if !errors.Is(global.GVA_DB.Where("username = ?", u.Username).First(&user).Error, gorm.ErrRecordNotFound) { // 判断用户名是否注册
|
||||
return userInter, errors.New("用户名已注册")
|
||||
}
|
||||
// 否则 附加uuid 密码hash加密 注册
|
||||
u.Password = utils.BcryptHash(u.Password)
|
||||
u.UUID = uuid.New()
|
||||
err = global.GVA_DB.Create(&u).Error
|
||||
return u, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: Login
|
||||
//@description: 用户登录
|
||||
//@param: u *model.SysUser
|
||||
//@return: err error, userInter *model.SysUser
|
||||
|
||||
func (userService *UserService) Login(u *system.SysUser) (userInter *system.SysUser, err error) {
|
||||
if nil == global.GVA_DB {
|
||||
return nil, fmt.Errorf("db not init")
|
||||
}
|
||||
|
||||
var user system.SysUser
|
||||
err = global.GVA_DB.Where("username = ?", u.Username).Preload("Authorities").Preload("Authority").First(&user).Error
|
||||
if err == nil {
|
||||
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
|
||||
return nil, errors.New("密码错误")
|
||||
}
|
||||
MenuServiceApp.UserAuthorityDefaultRouter(&user)
|
||||
}
|
||||
return &user, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: ChangePassword
|
||||
//@description: 修改用户密码
|
||||
//@param: u *model.SysUser, newPassword string
|
||||
//@return: userInter *model.SysUser,err error
|
||||
|
||||
func (userService *UserService) ChangePassword(u *system.SysUser, newPassword string) (userInter *system.SysUser, err error) {
|
||||
var user system.SysUser
|
||||
if err = global.GVA_DB.Where("id = ?", u.ID).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
|
||||
return nil, errors.New("原密码错误")
|
||||
}
|
||||
user.Password = utils.BcryptHash(newPassword)
|
||||
err = global.GVA_DB.Save(&user).Error
|
||||
return &user, err
|
||||
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: GetUserInfoList
|
||||
//@description: 分页获取数据
|
||||
//@param: info request.PageInfo
|
||||
//@return: err error, list interface{}, total int64
|
||||
|
||||
func (userService *UserService) GetUserInfoList(info systemReq.GetUserList) (list interface{}, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
db := global.GVA_DB.Model(&system.SysUser{})
|
||||
var userList []system.SysUser
|
||||
|
||||
if info.NickName != "" {
|
||||
db = db.Where("nick_name LIKE ?", "%"+info.NickName+"%")
|
||||
}
|
||||
if info.Phone != "" {
|
||||
db = db.Where("phone LIKE ?", "%"+info.Phone+"%")
|
||||
}
|
||||
if info.Username != "" {
|
||||
db = db.Where("username LIKE ?", "%"+info.Username+"%")
|
||||
}
|
||||
if info.Email != "" {
|
||||
db = db.Where("email LIKE ?", "%"+info.Email+"%")
|
||||
}
|
||||
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Preload("Authorities").Preload("Authority").Find(&userList).Error
|
||||
return userList, total, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetUserAuthority
|
||||
//@description: 设置一个用户的权限
|
||||
//@param: uuid uuid.UUID, authorityId string
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) {
|
||||
|
||||
assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUserAuthority{}).Error
|
||||
if errors.Is(assignErr, gorm.ErrRecordNotFound) {
|
||||
return errors.New("该用户无此角色")
|
||||
}
|
||||
|
||||
var authority system.SysAuthority
|
||||
err = global.GVA_DB.Where("authority_id = ?", authorityId).First(&authority).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var authorityMenu []system.SysAuthorityMenu
|
||||
var authorityMenuIDs []string
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenu).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range authorityMenu {
|
||||
authorityMenuIDs = append(authorityMenuIDs, authorityMenu[i].MenuId)
|
||||
}
|
||||
|
||||
var authorityMenus []system.SysBaseMenu
|
||||
err = global.GVA_DB.Preload("Parameters").Where("id in (?)", authorityMenuIDs).Find(&authorityMenus).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasMenu := false
|
||||
for i := range authorityMenus {
|
||||
if authorityMenus[i].Name == authority.DefaultRouter {
|
||||
hasMenu = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasMenu {
|
||||
return errors.New("找不到默认路由,无法切换本角色")
|
||||
}
|
||||
|
||||
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", id).Update("authority_id", authorityId).Error
|
||||
return err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetUserAuthorities
|
||||
//@description: 设置一个用户的权限
|
||||
//@param: id uint, authorityIds []string
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) SetUserAuthorities(adminAuthorityID, id uint, authorityIds []uint) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
var user system.SysUser
|
||||
TxErr := tx.Where("id = ?", id).First(&user).Error
|
||||
if TxErr != nil {
|
||||
global.GVA_LOG.Debug(TxErr.Error())
|
||||
return errors.New("查询用户数据失败")
|
||||
}
|
||||
TxErr = tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
|
||||
if TxErr != nil {
|
||||
return TxErr
|
||||
}
|
||||
var useAuthority []system.SysUserAuthority
|
||||
for _, v := range authorityIds {
|
||||
e := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, v)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
useAuthority = append(useAuthority, system.SysUserAuthority{
|
||||
SysUserId: id, SysAuthorityAuthorityId: v,
|
||||
})
|
||||
}
|
||||
TxErr = tx.Create(&useAuthority).Error
|
||||
if TxErr != nil {
|
||||
return TxErr
|
||||
}
|
||||
TxErr = tx.Model(&user).Update("authority_id", authorityIds[0]).Error
|
||||
if TxErr != nil {
|
||||
return TxErr
|
||||
}
|
||||
// 返回 nil 提交事务
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: DeleteUser
|
||||
//@description: 删除用户
|
||||
//@param: id float64
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) DeleteUser(id int) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Where("id = ?", id).Delete(&system.SysUser{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetUserInfo
|
||||
//@description: 设置用户信息
|
||||
//@param: reqUser model.SysUser
|
||||
//@return: err error, user model.SysUser
|
||||
|
||||
func (userService *UserService) SetUserInfo(req system.SysUser) error {
|
||||
return global.GVA_DB.Model(&system.SysUser{}).
|
||||
Select("updated_at", "nick_name", "header_img", "phone", "email", "enable").
|
||||
Where("id=?", req.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"updated_at": time.Now(),
|
||||
"nick_name": req.NickName,
|
||||
"header_img": req.HeaderImg,
|
||||
"phone": req.Phone,
|
||||
"email": req.Email,
|
||||
"enable": req.Enable,
|
||||
}).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetSelfInfo
|
||||
//@description: 设置用户信息
|
||||
//@param: reqUser model.SysUser
|
||||
//@return: err error, user model.SysUser
|
||||
|
||||
func (userService *UserService) SetSelfInfo(req system.SysUser) error {
|
||||
return global.GVA_DB.Model(&system.SysUser{}).
|
||||
Where("id=?", req.ID).
|
||||
Updates(req).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: SetSelfSetting
|
||||
//@description: 设置用户配置
|
||||
//@param: req datatypes.JSON, uid uint
|
||||
//@return: err error
|
||||
|
||||
func (userService *UserService) SetSelfSetting(req common.JSONMap, uid uint) error {
|
||||
return global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", uid).Update("origin_setting", req).Error
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: GetUserInfo
|
||||
//@description: 获取用户信息
|
||||
//@param: uuid uuid.UUID
|
||||
//@return: err error, user system.SysUser
|
||||
|
||||
func (userService *UserService) GetUserInfo(uuid uuid.UUID) (user system.SysUser, err error) {
|
||||
var reqUser system.SysUser
|
||||
err = global.GVA_DB.Preload("Authorities").Preload("Authority").First(&reqUser, "uuid = ?", uuid).Error
|
||||
if err != nil {
|
||||
return reqUser, err
|
||||
}
|
||||
MenuServiceApp.UserAuthorityDefaultRouter(&reqUser)
|
||||
return reqUser, err
|
||||
}
|
||||
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: FindUserById
|
||||
//@description: 通过id获取用户信息
|
||||
//@param: id int
|
||||
//@return: err error, user *model.SysUser
|
||||
|
||||
func (userService *UserService) FindUserById(id int) (user *system.SysUser, err error) {
|
||||
var u system.SysUser
|
||||
err = global.GVA_DB.Where("id = ?", id).First(&u).Error
|
||||
return &u, err
|
||||
}
|
||||
|
||||
//@author: [SliverHorn](https://github.com/SliverHorn)
|
||||
//@function: FindUserByUuid
|
||||
//@description: 通过uuid获取用户信息
|
||||
//@param: uuid string
|
||||
//@return: err error, user *model.SysUser
|
||||
|
||||
func (userService *UserService) FindUserByUuid(uuid string) (user *system.SysUser, err error) {
|
||||
var u system.SysUser
|
||||
if err = global.GVA_DB.Where("uuid = ?", uuid).First(&u).Error; err != nil {
|
||||
return &u, errors.New("用户不存在")
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: ResetPassword
|
||||
//@description: 修改用户密码
|
||||
//@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
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user