This commit is contained in:
2023-11-02 04:34:46 +08:00
commit c4548fe498
369 changed files with 40208 additions and 0 deletions

104
service/app/base.go Normal file
View File

@@ -0,0 +1,104 @@
package app
import (
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
"miniapp/global"
)
// 分页组件
func Page(current, size int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if current == 0 {
current = 1
}
if size < 1 {
size = 10
}
// 计算偏移量
offset := (current - 1) * size
// 返回组装结果
return db.Offset(offset).Limit(size)
}
}
// @title updateSortBefore
// @description 更新之前处理序号
// @param tx *gorm.DB "已开启的事务对象"
// @param model any "模型对象"
// @return error "错误信息"
func updateSortBefore(tx *gorm.DB, tableName, id string, sort int, param string) (err error) {
// 查出原来的排序号
var oldSort int
err = tx.Table(tableName).Select("sort").Where("id = ?", id).Scan(&oldSort).Error
if err != nil {
global.GVA_LOG.Error("查询老数据失败: %v", zap.Error(err))
return
}
// 如果相等,啥都不干
if oldSort == sort {
return nil
}
// 处理排序
// 如果老的排序号小于新的,(老, 新]之间的排序号都要-1
// 如果老的大于新的,[老, 新)排序号-1
if oldSort < sort {
// 老的小于新的,[老, 新) + 1
sel := tx.Table(tableName).
Where("sort <= ? AND sort > ?", sort, oldSort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param) // 自定义条件
}
err = sel.Update("sort", gorm.Expr("sort - 1")).Error
} else {
// 老的大于新的,[新, 老) + 1
sel := tx.Table(tableName).
Where("sort >= ? AND sort < ?", sort, oldSort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param) // 自定义条件
}
err = sel.Update("sort", gorm.Expr("sort + 1")).Error
}
return
}
// @title createSortBefore
// @description 新建之前处理序号
// @param tx *gorm.DB "已开启的事务对象"
// @param model any "模型对象"
// @return error "错误信息"
func createSortBefore(tx *gorm.DB, tableName string, sort int, param string) (err error) {
// 处理排序,如果没有传,就会是在最前面
sel := tx.Table(tableName).Where("sort >= ?", sort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param)
}
err = sel.Update("sort", gorm.Expr("sort + 1")).Error
if err != nil {
global.GVA_LOG.Error("处理前置排序失败:%v", zap.Error(err))
}
return
}
// @title dealSortAfter
// @description 处理序号之后
// @param tx *gorm.DB "已开启的事务对象"
// @param modelName string "表名"
// @return error "错误信息"
func dealSortAfter(tx *gorm.DB, modelName, param string) (err error) {
// 保存成功,刷新排序
if param != "" {
param += " AND "
}
sql := fmt.Sprintf("UPDATE %s a, (SELECT (@i := @i + 1) i, id FROM %s WHERE %s deleted_at IS NULL order by sort ASC) i, "+
"(SELECT @i := 0) ir SET a.sort = i.i, updated_at=now() WHERE a.id = i.id", modelName, modelName, param)
err = tx.Exec(sql).Error
if err != nil {
global.GVA_LOG.Error("刷新排序失败: %v", zap.Error(err))
}
return
}

9
service/app/enter.go Normal file
View File

@@ -0,0 +1,9 @@
package app
type ServiceGroup struct {
Oauth2ClientService
UserService
FavoriteService
VisionService
TodesService
}

34
service/app/favorite.go Normal file
View File

@@ -0,0 +1,34 @@
package app
import (
"miniapp/global"
"miniapp/model/app"
"miniapp/model/app/request"
)
type FavoriteService struct{}
// GetFavoriteList 获取收藏列表
func (f *FavoriteService) GetFavoriteList(p request.GetFavoriteList) (err error, list []app.Favorite, total int64) {
limit := p.PageSize
offset := p.PageSize * (p.Page - 1)
db := global.GVA_DB.Model(&app.Favorite{}).Where("user_id = ?", p.UserId)
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Find(&list).Error
return
}
// CreateFavorite 创建收藏
func (f *FavoriteService) CreateFavorite(favorite *app.Favorite) (err error) {
err = global.GVA_DB.Create(&favorite).Error
return
}
// DeleteFavorite 删除收藏
func (f *FavoriteService) DeleteFavorite(favorite *app.Favorite) (err error) {
err = global.GVA_DB.Delete(&favorite).Error
return
}

View File

@@ -0,0 +1,18 @@
package app
import (
"miniapp/global"
"miniapp/model/app"
)
type Oauth2ClientService struct{}
// FinAll 查询所有客户端
func (o *Oauth2ClientService) FinAll(clients *[]app.OAuth2Client) error {
return global.GVA_DB.Find(&clients).Error
}
// FindOne 查询某一个
func (o *Oauth2ClientService) FindOne(c *app.OAuth2Client) error {
return global.GVA_DB.Take(&c, c).Error
}

19
service/app/todes.go Normal file
View File

@@ -0,0 +1,19 @@
package app
import (
"miniapp/global"
"miniapp/model/common"
)
type TodesService struct{}
func (t TodesService) GetUserTodos(userId uint) (list []common.UserTodo, err error) {
err = global.GVA_DB.Where("user_id = ?", userId).Find(&list).Error
return
}
func (t TodesService) UpdateTodoById(c *common.UserTodo) error {
return global.GVA_DB.Table("t_user_todo").Where("id = ?", c.ID).Update("is_finish", c.IsFinish).Error
}

156
service/app/user.go Normal file
View File

@@ -0,0 +1,156 @@
package app
import (
"errors"
"github.com/duke-git/lancet/v2/datetime"
"go.uber.org/zap"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/app"
"miniapp/model/app/request"
"miniapp/model/app/response"
"miniapp/model/common"
"miniapp/model/types"
"time"
)
type UserService struct{}
func (UserService) GetUser(user *app.User) (err error) {
return global.GVA_DB.Take(&user, user).Error
}
func (UserService) GetOrCreate(user *app.User) (err error) {
//err = global.GVA_DB.Take(&user, user).Error
err = global.GVA_DB.Model(&user).Where("phone = ?", user.Phone).First(&user).Error
if err == nil {
return
}
// 如果是没查询到记录,则创建用户
if err == gorm.ErrRecordNotFound {
// 用户不存在,创建用户
if err = global.GVA_DB.Create(&user).Error; err != nil {
global.GVA_LOG.Error("账号创建失败: %v", zap.Error(err))
err = errors.New("登录失败")
return
}
}
return
}
// CheckUnionIdIsExist 检查UnionId和OpenId是否存在
func (UserService) CheckUnionIdIsExist(unionId, openId string) bool {
var count int64
err := global.GVA_DB.Model(&app.User{}).Where("wechat_union_id = ? and wechat_open_id = ?", unionId, openId).Count(&count).Error
if err != nil {
return false
}
return count > 0
}
// UpdateUserInfo 更新普通用户信息
func (UserService) UpdateUserInfo(e *app.User) (err error) {
return global.GVA_DB.Updates(&e).Error
}
// GetAverageUserList 查询普通用户列表
func (UserService) GetAverageUserList(p request.GetUserList) (records []response.UserItem, count int64, err error) {
tx := global.GVA_DB.Scopes(Page(p.Page, p.PageSize)).
Table("`t_user` AS tu").
Select("tu.id AS id,tu.avatar AS avatar,tu.phone AS phone,tu.nickname AS nickname,tu.sex AS sex,tu.created_at AS created_at,tu.last_login_at AS last_login_at,tu.`status` AS `status`").
Order("tu.last_login_at DESC")
if p.Phone != "" {
tx = tx.Where("tu.phone LIKE ?", "%"+p.Phone+"%")
}
//加一个姓名的模糊查询
if p.Name != "" {
tx = tx.Where("tu.nickname LIKE ?", "&"+p.Name+"%")
}
if p.Status != "" {
tx = tx.Where("tu.`status` = ?", p.Status)
}
if p.StartAt != "" || p.EndAt != "" {
start := time.Date(2021, 1, 1, 0, 0, 0, 0, time.Local)
end := time.Now()
// 处理数据
if p.StartAt != "" {
st, err := datetime.FormatStrToTime(p.StartAt, "yyyy-mm-dd")
if err != nil {
global.GVA_LOG.Error("解析开始时间异常")
return nil, 0, errors.New("解析开始时间异常")
}
start = datetime.BeginOfDay(st)
}
if p.EndAt != "" {
et, err := datetime.FormatStrToTime(p.EndAt, "yyyy-mm-dd")
if err != nil {
global.GVA_LOG.Error("解析结束时间异常")
return nil, 0, errors.New("解析结束时间异常")
}
end = datetime.EndOfDay(et)
}
// 处理查询条件
tx.Where("tor.pay_at BETWEEN ? AND ?", start, end)
}
err = tx.Offset(-1).Limit(-1).Count(&count).Find(&records).Error
return
}
func (UserService) UpdateLastLoginInfo(username int) {
now := time.Now().Local()
global.GVA_DB.Model(&app.User{}).
Where("id = ?", username).
Updates(app.User{LastLoginAt: (*types.DateTime)(&now)})
}
// UpdateUserHospital 更新用户医院信息
func (UserService) UpdateUserHospital(r *request.ChangeUserHospital) (err error) {
err = global.GVA_DB.Delete(&common.UserTodo{}, "user_id = ?", r.UserId).Error
if err != nil {
global.GVA_LOG.Error("清除用户Todo列表失败", zap.Error(err))
return
}
var hospital common.Hospital
err = global.GVA_DB.Where("id = ?", r.HospitalId).Preload("Todos").First(&hospital).Error
if err != nil {
global.GVA_LOG.Error("获取医院信息失败", zap.Error(err))
return
}
if hospital.Todos != nil {
var userTodos []common.UserTodo
for _, todo := range hospital.Todos {
userTodos = append(userTodos, common.UserTodo{
Content: todo.Content,
UserId: r.UserId,
IsFinish: 0,
})
}
err = global.GVA_DB.Create(&userTodos).Error
if err != nil {
global.GVA_LOG.Error("创建用户Todo列表失败", zap.Error(err))
return
}
}
return
}
func (s UserService) GetUserInfo(id string) (user response.UserVO, err error) {
var u app.User
err = global.GVA_DB.Where("id = ?", id).First(&u).Error
user.ParseOrdinary(u)
if u.IsSurgery == 1 {
user.TimeNote = "距离下次复查时间还剩:"
} else {
user.TimeNote = "距离手术时间还剩:"
}
return
}

26
service/app/vision.go Normal file
View File

@@ -0,0 +1,26 @@
package app
import (
"miniapp/global"
"miniapp/model/app"
"miniapp/model/app/request"
)
type VisionService struct{}
func (s *VisionService) GetVisionList(p request.VisionListRequest) (list *[]app.Vision, total int64, err error) {
limit := p.PageSize
offset := p.PageSize * (p.Page - 1)
db := global.GVA_DB.Model(&app.Vision{}).Where("user_id = ?", p.UserId)
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Find(&list).Error
return
}
func (s *VisionService) CreateVision(vision *app.Vision) (err error) {
err = global.GVA_DB.Create(&vision).Error
return
}

15
service/enter.go Normal file
View File

@@ -0,0 +1,15 @@
package service
import (
"miniapp/service/app"
"miniapp/service/example"
"miniapp/service/system"
)
type ServiceGroup struct {
SystemServiceGroup system.ServiceGroup
ExampleServiceGroup example.ServiceGroup
AppServiceGroup app.ServiceGroup
}
var ServiceGroupApp = new(ServiceGroup)

6
service/example/enter.go Normal file
View File

@@ -0,0 +1,6 @@
package example
type ServiceGroup struct {
CustomerService
FileUploadAndDownloadService
}

View File

@@ -0,0 +1,69 @@
package example
import (
"errors"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/example"
)
type FileUploadAndDownloadService struct{}
//@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
}

View File

@@ -0,0 +1,85 @@
package example
import (
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/example"
"miniapp/model/system"
systemService "miniapp/service/system"
)
type CustomerService struct{}
//@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
}

View File

@@ -0,0 +1,108 @@
package example
import (
"errors"
"mime/multipart"
"strings"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/example"
"miniapp/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.PageInfo
//@return: list interface{}, total int64, err error
func (e *FileUploadAndDownloadService) GetFileRecordInfoList(info request.PageInfo) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
keyword := info.Keyword
db := global.GVA_DB.Model(&example.ExaFileUploadAndDownload{})
var fileLists []example.ExaFileUploadAndDownload
if len(keyword) > 0 {
db = db.Where("name LIKE ?", "%"+keyword+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error
return fileLists, 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) (file example.ExaFileUploadAndDownload, err error) {
oss := upload.NewOss()
filePath, key, uploadErr := oss.UploadFile(header)
if uploadErr != nil {
panic(uploadErr)
}
s := strings.Split(header.Filename, ".")
f := example.ExaFileUploadAndDownload{
Url: filePath,
Name: header.Filename,
Tag: s[len(s)-1],
Key: key,
}
if noSave == "0" {
return f, e.Upload(f)
}
return f, nil
}

38
service/system/article.go Normal file
View File

@@ -0,0 +1,38 @@
package system
import (
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/common"
"miniapp/model/common/request"
)
type ArticleService struct{}
// GetArticleList 获取文章列表
func (a ArticleService) GetArticleList(p request.PageInfo) (list []common.Article, total int64, err error) {
err = global.GVA_DB.Scopes(Page(p.Page, p.PageSize)).Find(&list).Offset(-1).Limit(-1).Count(&total).Error
return
}
// CreateArticle 创建文章
func (a ArticleService) CreateArticle(article *common.Article) (err error) {
return global.GVA_DB.Create(&article).Error
}
// UpdateArticle 更新文章
func (a ArticleService) UpdateArticle(article *common.Article) (err error) {
return global.GVA_DB.Updates(&article).Error
}
// DeleteArticle 删除文章
func (a ArticleService) DeleteArticle(article *common.Article) (err error) {
return global.GVA_DB.Delete(&article).Error
}
// GetArticleById 根据id获取文章
func (a ArticleService) GetArticleById(id string) (article *common.Article, err error) {
err = global.GVA_DB.Where("id = ?", id).First(&article).Error
err = global.GVA_DB.Table("articles").Where("id = ?", id).Update("reading_num", gorm.Expr("reading_num + ?", 1)).Error
return
}

37
service/system/banner.go Normal file
View File

@@ -0,0 +1,37 @@
package system
import (
"miniapp/global"
"miniapp/model/common"
"miniapp/model/common/request"
)
type BannerService struct {
}
// GetBannerList 获取轮播图列表
func (b BannerService) GetBannerList(p request.PageInfo) (list []common.Banner, total int64, err error) {
err = global.GVA_DB.Scopes(Page(p.Page, p.PageSize)).Find(&list).Limit(-1).Offset(-1).Count(&total).Error
return
}
// CreateBanner 创建轮播图
func (b BannerService) CreateBanner(banner *common.Banner) (err error) {
return global.GVA_DB.Create(&banner).Error
}
// UpdateBanner 更新轮播图
func (b BannerService) UpdateBanner(banner *common.Banner) (err error) {
return global.GVA_DB.Updates(&banner).Error
}
// DeleteBanner 删除轮播图
func (b BannerService) DeleteBanner(banner *common.Banner) (err error) {
return global.GVA_DB.Delete(&banner).Error
}
// GetBannerById 根据id获取轮播图
func (b BannerService) GetBannerById(id string) (banner *common.Banner, err error) {
err = global.GVA_DB.Where("id = ?", id).First(&banner).Error
return
}

104
service/system/base.go Normal file
View File

@@ -0,0 +1,104 @@
package system
import (
"fmt"
"go.uber.org/zap"
"gorm.io/gorm"
"miniapp/global"
)
// 分页组件
func Page(current, size int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if current == 0 {
current = 1
}
if size < 1 {
size = 10
}
// 计算偏移量
offset := (current - 1) * size
// 返回组装结果
return db.Offset(offset).Limit(size)
}
}
// @title updateSortBefore
// @description 更新之前处理序号
// @param tx *gorm.DB "已开启的事务对象"
// @param model any "模型对象"
// @return error "错误信息"
func updateSortBefore(tx *gorm.DB, tableName, id string, sort int, param string) (err error) {
// 查出原来的排序号
var oldSort int
err = tx.Table(tableName).Select("sort").Where("id = ?", id).Scan(&oldSort).Error
if err != nil {
global.GVA_LOG.Error("查询老数据失败: %v", zap.Error(err))
return
}
// 如果相等,啥都不干
if oldSort == sort {
return nil
}
// 处理排序
// 如果老的排序号小于新的,(老, 新]之间的排序号都要-1
// 如果老的大于新的,[老, 新)排序号-1
if oldSort < sort {
// 老的小于新的,[老, 新) + 1
sel := tx.Table(tableName).
Where("sort <= ? AND sort > ?", sort, oldSort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param) // 自定义条件
}
err = sel.Update("sort", gorm.Expr("sort - 1")).Error
} else {
// 老的大于新的,[新, 老) + 1
sel := tx.Table(tableName).
Where("sort >= ? AND sort < ?", sort, oldSort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param) // 自定义条件
}
err = sel.Update("sort", gorm.Expr("sort + 1")).Error
}
return
}
// @title createSortBefore
// @description 新建之前处理序号
// @param tx *gorm.DB "已开启的事务对象"
// @param model any "模型对象"
// @return error "错误信息"
func createSortBefore(tx *gorm.DB, tableName string, sort int, param string) (err error) {
// 处理排序,如果没有传,就会是在最前面
sel := tx.Table(tableName).Where("sort >= ?", sort).
Where("deleted_at IS NULL")
if param != "" {
sel.Where(param)
}
err = sel.Update("sort", gorm.Expr("sort + 1")).Error
if err != nil {
global.GVA_LOG.Error("处理前置排序失败:%v", zap.Error(err))
}
return
}
// @title dealSortAfter
// @description 处理序号之后
// @param tx *gorm.DB "已开启的事务对象"
// @param modelName string "表名"
// @return error "错误信息"
func dealSortAfter(tx *gorm.DB, modelName, param string) (err error) {
// 保存成功,刷新排序
if param != "" {
param += " AND "
}
sql := fmt.Sprintf("UPDATE %s a, (SELECT (@i := @i + 1) i, id FROM %s WHERE %s deleted_at IS NULL order by sort ASC) i, "+
"(SELECT @i := 0) ir SET a.sort = i.i, updated_at=now() WHERE a.id = i.id", modelName, modelName, param)
err = tx.Exec(sql).Error
if err != nil {
global.GVA_LOG.Error("刷新排序失败: %v", zap.Error(err))
}
return
}

23
service/system/enter.go Normal file
View File

@@ -0,0 +1,23 @@
package system
type ServiceGroup struct {
JwtService
ApiService
MenuService
UserService
CasbinService
InitDBService
AutoCodeService
BaseMenuService
AuthorityService
DictionaryService
SystemConfigService
AutoCodeHistoryService
OperationRecordService
DictionaryDetailService
AuthorityBtnService
ChatGptService
HospitalService
BannerService
ArticleService
}

View File

@@ -0,0 +1,37 @@
package system
import (
"miniapp/global"
"miniapp/model/common"
"miniapp/model/common/request"
)
type HospitalService struct {
}
// GetHospitalList 获取医院列表
func (h HospitalService) GetHospitalList(p request.PageInfo) (list []common.Hospital, total int64, err error) {
err = global.GVA_DB.Scopes(Page(p.Page, p.PageSize)).Preload("Notes").Preload("Todos").Find(&list).Offset(-1).Limit(-1).Count(&total).Error
return
}
// CreateHospital 创建医院
func (h HospitalService) CreateHospital(hospital *common.Hospital) (err error) {
return global.GVA_DB.Create(&hospital).Error
}
// UpdateHospital 更新医院
func (h HospitalService) UpdateHospital(hospital *common.Hospital) (err error) {
return global.GVA_DB.Updates(&hospital).Error
}
// DeleteHospital 删除医院
func (h HospitalService) DeleteHospital(hospital *common.Hospital) (err error) {
return global.GVA_DB.Select("Notes").Delete(&hospital).Error
}
// GetHospitalById 根据id获取医院
func (h HospitalService) GetHospitalById(id uint) (hospital *common.Hospital, err error) {
err = global.GVA_DB.Where("id = ?", id).Preload("Notes").Preload("Todos").First(&hospital).Error
return
}

View File

@@ -0,0 +1,82 @@
package system
import (
"context"
"go.uber.org/zap"
"miniapp/global"
"miniapp/model/system"
"miniapp/utils"
)
type JwtService struct{}
//@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 中
}

196
service/system/sys_api.go Normal file
View File

@@ -0,0 +1,196 @@
package system
import (
"errors"
"fmt"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
"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
}
//@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.Where("id = ?", api.ID).First(&entity).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)
if err != nil {
return err
}
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
} else {
db = db.Limit(limit).Offset(offset)
if order != "" {
var OrderStr string
// 设置有效排序key 防止sql注入
// 感谢 Tom4t0 提交漏洞信息
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] {
if desc {
OrderStr = order + " desc"
} else {
OrderStr = order
}
} else { // didn't match any order key in `orderMap`
err = fmt.Errorf("非法的排序字段: %v", order)
return apiList, total, err
}
err = db.Order(OrderStr).Find(&apiList).Error
} else {
err = db.Order("api_group").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() (apis []system.SysApi, err error) {
err = global.GVA_DB.Find(&apis).Error
return
}
//@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.Where("id = ?", id).First(&api).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.Where("id = ?", api.ID).First(&oldA).Error
if oldA.Path != api.Path || oldA.Method != api.Method {
if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.SysApi{}).Error, gorm.ErrRecordNotFound) {
return errors.New("存在相同api路径")
}
}
if err != nil {
return err
} else {
err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
if err != nil {
return err
} else {
err = global.GVA_DB.Save(&api).Error
}
}
return err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteApis
//@description: 删除选中API
//@param: apis []model.SysApi
//@return: err error
func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
var apis []system.SysApi
err = global.GVA_DB.Find(&apis, "id in ?", ids.Ids).Delete(&apis).Error
if err != nil {
return err
} else {
for _, sysApi := range apis {
CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
}
if err != nil {
return err
}
}
return err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteApis
//@description: 删除选中API
//@param: apis []model.SysApi
//@return: err error
func (apiService *ApiService) FreshCasbin() (err error) {
e := CasbinServiceApp.Casbin()
err = e.LoadPolicy()
return err
}

View File

@@ -0,0 +1,220 @@
package system
import (
"errors"
"strconv"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
"miniapp/model/system/response"
)
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) {
var authorityBox system.SysAuthority
if !errors.Is(global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) {
return auth, ErrRoleExistence
}
err = global.GVA_DB.Create(&auth).Error
return auth, err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CopyAuthority
//@description: 复制一个角色
//@param: copyInfo response.SysAuthorityCopyResponse
//@return: authority system.SysAuthority, err error
func (authorityService *AuthorityService) CopyAuthority(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: int(copyInfo.OldAuthorityId)})
if err != nil {
return
}
var baseMenu []system.SysBaseMenu
for _, v := range menus {
intNum, _ := strconv.Atoi(v.MenuId)
v.SysBaseMenu.ID = uint(intNum)
baseMenu = append(baseMenu, v.SysBaseMenu)
}
copyInfo.Authority.SysBaseMenus = baseMenu
err = global.GVA_DB.Create(&copyInfo.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(copyInfo.Authority.AuthorityId, paths)
if err != nil {
_ = authorityService.DeleteAuthority(&copyInfo.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) {
err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).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) (err 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("此角色存在子角色不允许删除")
}
db := global.GVA_DB.Preload("SysBaseMenus").Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(auth)
err = db.Unscoped().Delete(auth).Error
if err != nil {
return
}
if len(auth.SysBaseMenus) > 0 {
err = global.GVA_DB.Model(auth).Association("SysBaseMenus").Delete(auth.SysBaseMenus)
if err != nil {
return
}
// err = db.Association("SysBaseMenus").Delete(&auth)
}
if len(auth.DataAuthorityId) > 0 {
err = global.GVA_DB.Model(auth).Association("DataAuthorityId").Delete(auth.DataAuthorityId)
if err != nil {
return
}
}
err = global.GVA_DB.Delete(&[]system.SysUserAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error
if err != nil {
return
}
err = global.GVA_DB.Delete(&[]system.SysAuthorityBtn{}, "authority_id = ?", auth.AuthorityId).Error
if err != nil {
return
}
authorityId := strconv.Itoa(int(auth.AuthorityId))
CasbinServiceApp.ClearCasbin(0, authorityId)
return err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetAuthorityInfoList
//@description: 分页获取数据
//@param: info request.PageInfo
//@return: list interface{}, total int64, err error
func (authorityService *AuthorityService) GetAuthorityInfoList(info request.PageInfo) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
db := global.GVA_DB.Model(&system.SysAuthority{})
if err = db.Where("parent_id = ?", "0").Count(&total).Error; total == 0 || err != nil {
return
}
var authority []system.SysAuthority
err = db.Limit(limit).Offset(offset).Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authority).Error
for k := range authority {
err = authorityService.findChildrenAuthority(&authority[k])
}
return authority, total, err
}
//@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(auth system.SysAuthority) error {
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
}

View File

@@ -0,0 +1,58 @@
package system
import (
"errors"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system"
"miniapp/model/system/request"
"miniapp/model/system/response"
)
type AuthorityBtnService struct{}
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("此按钮正在被使用无法删除")
}

View File

@@ -0,0 +1,958 @@
package system
import (
"archive/zip"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"os"
"path/filepath"
"strconv"
"strings"
"text/template"
ast2 "miniapp/utils/ast"
cp "github.com/otiai10/copy"
"go.uber.org/zap"
"miniapp/resource/autocode_template/subcontract"
"miniapp/global"
"miniapp/model/system"
"miniapp/utils"
"gorm.io/gorm"
)
const (
autoPath = "autocode_template/"
autocodePath = "resource/autocode_template"
plugPath = "resource/plug_template"
packageService = "service/%s/enter.go"
packageServiceName = "service"
packageRouter = "router/%s/enter.go"
packageRouterName = "router"
packageAPI = "api/v1/%s/enter.go"
packageAPIName = "api/v1"
)
type autoPackage struct {
path string
temp string
name string
}
var (
packageInjectionMap map[string]astInjectionMeta
injectionPaths []injectionMeta
)
func Init(Package string) {
injectionPaths = []injectionMeta{
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, Package), "enter.go"),
funcName: "ApiGroup",
structNameF: "%sApi",
},
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, Package), "enter.go"),
funcName: "RouterGroup",
structNameF: "%sRouter",
},
{
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, Package), "enter.go"),
funcName: "ServiceGroup",
structNameF: "%sService",
},
}
packageInjectionMap = map[string]astInjectionMeta{
packageServiceName: {
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, "service", "enter.go"),
importCodeF: "miniapp/%s/%s",
packageNameF: "%s",
groupName: "ServiceGroup",
structNameF: "%sServiceGroup",
},
packageRouterName: {
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, "router", "enter.go"),
importCodeF: "miniapp/%s/%s",
packageNameF: "%s",
groupName: "RouterGroup",
structNameF: "%s",
},
packageAPIName: {
path: filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, "api/v1", "enter.go"),
importCodeF: "miniapp/%s/%s",
packageNameF: "%s",
groupName: "ApiGroup",
structNameF: "%sApiGroup",
},
}
}
type injectionMeta struct {
path string
funcName string
structNameF string // 带格式化的
}
type astInjectionMeta struct {
path string
importCodeF string
structNameF string
packageNameF string
groupName string
}
type tplData struct {
template *template.Template
autoPackage string
locationPath string
autoCodePath string
autoMoveFilePath string
}
type AutoCodeService struct{}
var AutoCodeServiceApp = new(AutoCodeService)
// @author: [songzhibin97](https://github.com/songzhibin97)
// @function: PreviewTemp
// @description: 预览创建代码
// @param: model.AutoCodeStruct
// @return: map[string]string, error
func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
}
if autoCode.Fields[i].Require {
autoCode.NeedValid = true
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "video" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.HasPic = true
autoCode.NeedJSON = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.HasFile = true
autoCode.NeedJSON = true
}
}
dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
if err != nil {
return nil, err
}
// 写入文件前,先创建文件夹
if err = utils.CreateDir(needMkdir...); err != nil {
return nil, err
}
// 创建map
ret := make(map[string]string)
// 生成map
for _, value := range dataList {
ext := ""
if ext = filepath.Ext(value.autoCodePath); ext == ".txt" {
continue
}
f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
if err != nil {
return nil, err
}
if err = value.template.Execute(f, autoCode); err != nil {
return nil, err
}
_ = f.Close()
f, err = os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_RDONLY, 0o755)
if err != nil {
return nil, err
}
builder := strings.Builder{}
builder.WriteString("```")
if ext != "" && strings.Contains(ext, ".") {
builder.WriteString(strings.Replace(ext, ".", "", -1))
}
builder.WriteString("\n\n")
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
builder.Write(data)
builder.WriteString("\n\n```")
pathArr := strings.Split(value.autoCodePath, string(os.PathSeparator))
ret[pathArr[1]+"-"+pathArr[3]] = builder.String()
_ = f.Close()
}
defer func() { // 移除中间文件
if err := os.RemoveAll(autoPath); err != nil {
return
}
}()
return ret, nil
}
func makeDictTypes(autoCode *system.AutoCodeStruct) {
DictTypeM := make(map[string]string)
for _, v := range autoCode.Fields {
if v.DictType != "" {
DictTypeM[v.DictType] = ""
}
}
for k := range DictTypeM {
autoCode.DictTypes = append(autoCode.DictTypes, k)
}
}
// @author: [piexlmax](https://github.com/piexlmax)
// @function: CreateTemp
// @description: 创建代码
// @param: model.AutoCodeStruct
// @return: err error
func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, ids ...uint) (err error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
}
if autoCode.Fields[i].Require {
autoCode.NeedValid = true
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "video" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.NeedJSON = true
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.NeedJSON = true
autoCode.HasFile = true
}
}
// 增加判断: 重复创建struct
if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.BusinessDB, autoCode.StructName, autoCode.Package) {
return RepeatErr
}
dataList, fileList, needMkdir, err := autoCodeService.getNeedList(&autoCode)
if err != nil {
return err
}
meta, _ := json.Marshal(autoCode)
// 写入文件前,先创建文件夹
if err = utils.CreateDir(needMkdir...); err != nil {
return err
}
// 生成文件
for _, value := range dataList {
f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0o755)
if err != nil {
return err
}
if err = value.template.Execute(f, autoCode); err != nil {
return err
}
_ = f.Close()
}
defer func() { // 移除中间文件
if err := os.RemoveAll(autoPath); err != nil {
return
}
}()
bf := strings.Builder{}
idBf := strings.Builder{}
injectionCodeMeta := strings.Builder{}
for _, id := range ids {
idBf.WriteString(strconv.Itoa(int(id)))
idBf.WriteString(";")
}
if autoCode.AutoMoveFile { // 判断是否需要自动转移
Init(autoCode.Package)
for index := range dataList {
autoCodeService.addAutoMoveFile(&dataList[index])
}
// 判断目标文件是否都可以移动
for _, value := range dataList {
if utils.FileExist(value.autoMoveFilePath) {
return errors.New(fmt.Sprintf("目标文件已存在:%s\n", value.autoMoveFilePath))
}
}
for _, value := range dataList { // 移动文件
if err := utils.FileMove(value.autoCodePath, value.autoMoveFilePath); err != nil {
return err
}
}
{
// 在gorm.go 注入 自动迁移
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "gorm.go")
varDB := utils.MaheHump(autoCode.BusinessDB)
ast2.AddRegisterTablesAst(path, "RegisterTables", autoCode.Package, varDB, autoCode.BusinessDB, autoCode.StructName)
}
{
// router.go 注入 自动迁移
path := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.SInitialize, "router.go")
ast2.AddRouterCode(path, "Routers", autoCode.Package, autoCode.StructName)
}
// 给各个enter进行注入
err = injectionCode(autoCode.StructName, &injectionCodeMeta)
if err != nil {
return
}
// 保存生成信息
for _, data := range dataList {
if len(data.autoMoveFilePath) != 0 {
bf.WriteString(data.autoMoveFilePath)
bf.WriteString(";")
}
}
} else { // 打包
if err = utils.ZipFiles("./ginvueadmin.zip", fileList, ".", "."); err != nil {
return err
}
}
if autoCode.AutoMoveFile || autoCode.AutoCreateApiToSql {
if autoCode.TableName != "" {
err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
string(meta),
autoCode.StructName,
autoCode.Description,
bf.String(),
injectionCodeMeta.String(),
autoCode.TableName,
idBf.String(),
autoCode.Package,
autoCode.BusinessDB,
)
} else {
err = AutoCodeHistoryServiceApp.CreateAutoCodeHistory(
string(meta),
autoCode.StructName,
autoCode.Description,
bf.String(),
injectionCodeMeta.String(),
autoCode.StructName,
idBf.String(),
autoCode.Package,
autoCode.BusinessDB,
)
}
}
if err != nil {
return err
}
if autoCode.AutoMoveFile {
return system.ErrAutoMove
}
return nil
}
// @author: [piexlmax](https://github.com/piexlmax)
// @function: GetAllTplFile
// @description: 获取 pathName 文件夹下所有 tpl 文件
// @param: pathName string, fileList []string
// @return: []string, error
func (autoCodeService *AutoCodeService) GetAllTplFile(pathName string, fileList []string) ([]string, error) {
files, err := os.ReadDir(pathName)
for _, fi := range files {
if fi.IsDir() {
fileList, err = autoCodeService.GetAllTplFile(pathName+"/"+fi.Name(), fileList)
if err != nil {
return nil, err
}
} else {
if strings.HasSuffix(fi.Name(), ".tpl") {
fileList = append(fileList, pathName+"/"+fi.Name())
}
}
}
return fileList, err
}
// @author: [piexlmax](https://github.com/piexlmax)
// @function: GetDB
// @description: 获取指定数据库和指定数据表的所有字段名,类型值等
// @param: tableName string, dbName string
// @return: err error, Columns []request.ColumnReq
func (autoCodeService *AutoCodeService) 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
}
}
// @author: [SliverHorn](https://github.com/SliverHorn)
// @author: [songzhibin97](https://github.com/songzhibin97)
// @function: addAutoMoveFile
// @description: 生成对应的迁移文件路径
// @param: *tplData
// @return: null
func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
base := filepath.Base(data.autoCodePath)
fileSlice := strings.Split(data.autoCodePath, string(os.PathSeparator))
n := len(fileSlice)
if n <= 2 {
return
}
if strings.Contains(fileSlice[1], "server.exe.exe") {
if strings.Contains(fileSlice[n-2], "router") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server,
fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRouter, data.autoPackage), base)
} else if strings.Contains(fileSlice[n-2], "api") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SApi, data.autoPackage), base)
} else if strings.Contains(fileSlice[n-2], "service") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SService, data.autoPackage), base)
} else if strings.Contains(fileSlice[n-2], "model") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SModel, data.autoPackage), base)
} else if strings.Contains(fileSlice[n-2], "request") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SRequest, data.autoPackage), base)
}
} else if strings.Contains(fileSlice[1], "web") {
if strings.Contains(fileSlice[n-1], "js") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, base)
} else if strings.Contains(fileSlice[n-2], "form") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
} else if strings.Contains(fileSlice[n-2], "table") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
}
}
}
// @author: [piexlmax](https://github.com/piexlmax)
// @author: [SliverHorn](https://github.com/SliverHorn)
// @function: CreateApi
// @description: 自动创建api数据,
// @param: a *model.AutoCodeStruct
// @return: err error
func (autoCodeService *AutoCodeService) AutoCreateApi(a *system.AutoCodeStruct) (ids []uint, err error) {
apiList := []system.SysApi{
{
Path: "/" + a.Abbreviation + "/" + "create" + a.StructName,
Description: "新增" + a.Description,
ApiGroup: a.Description,
Method: "POST",
},
{
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName,
Description: "删除" + a.Description,
ApiGroup: a.Description,
Method: "DELETE",
},
{
Path: "/" + a.Abbreviation + "/" + "delete" + a.StructName + "ByIds",
Description: "批量删除" + a.Description,
ApiGroup: a.Description,
Method: "DELETE",
},
{
Path: "/" + a.Abbreviation + "/" + "update" + a.StructName,
Description: "更新" + a.Description,
ApiGroup: a.Description,
Method: "PUT",
},
{
Path: "/" + a.Abbreviation + "/" + "find" + a.StructName,
Description: "根据ID获取" + a.Description,
ApiGroup: a.Description,
Method: "GET",
},
{
Path: "/" + a.Abbreviation + "/" + "get" + a.StructName + "List",
Description: "获取" + a.Description + "列表",
ApiGroup: a.Description,
Method: "GET",
},
}
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
for _, v := range apiList {
var api system.SysApi
if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
if err = tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
return err
} else {
ids = append(ids, v.ID)
}
}
}
return nil
})
return ids, err
}
func (autoCodeService *AutoCodeService) getNeedList(autoCode *system.AutoCodeStruct) (dataList []tplData, fileList []string, needMkdir []string, err error) {
// 去除所有空格
utils.TrimSpace(autoCode)
for _, field := range autoCode.Fields {
utils.TrimSpace(field)
}
// 获取 basePath 文件夹下所有tpl文件
tplFileList, err := autoCodeService.GetAllTplFile(autocodePath, nil)
if err != nil {
return nil, nil, nil, err
}
dataList = make([]tplData, 0, len(tplFileList))
fileList = make([]string, 0, len(tplFileList))
needMkdir = make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时改为map更合理
// 根据文件路径生成 tplData 结构体,待填充数据
for _, value := range tplFileList {
dataList = append(dataList, tplData{locationPath: value, autoPackage: autoCode.Package})
}
// 生成 *Template, 填充 template 字段
for index, value := range dataList {
dataList[index].template, err = template.ParseFiles(value.locationPath)
if err != nil {
return nil, nil, nil, err
}
}
// 生成文件路径,填充 autoCodePath 字段readme.txt.tpl不符合规则需要特殊处理
// resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
// resource/template/readme.txt.tpl -> autoCode/readme.txt
for index, value := range dataList {
trimBase := strings.TrimPrefix(value.locationPath, autocodePath+"/")
if trimBase == "readme.txt.tpl" {
dataList[index].autoCodePath = autoPath + "readme.txt"
continue
}
if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
firstDot := strings.Index(origFileName, ".")
if firstDot != -1 {
var fileName string
if origFileName[firstDot:] != ".go" {
fileName = autoCode.PackageName + origFileName[firstDot:]
} else {
fileName = autoCode.HumpPackageName + origFileName[firstDot:]
}
dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
origFileName[:firstDot], fileName)
}
}
if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
}
}
for _, value := range dataList {
fileList = append(fileList, value.autoCodePath)
}
return dataList, fileList, needMkdir, err
}
// injectionCode 封装代码注入
func injectionCode(structName string, bf *strings.Builder) error {
for _, meta := range injectionPaths {
code := fmt.Sprintf(meta.structNameF, structName)
ast2.ImportForAutoEnter(meta.path, meta.funcName, code)
bf.WriteString(fmt.Sprintf("%s@%s@%s;", meta.path, meta.funcName, code))
}
return nil
}
func (autoCodeService *AutoCodeService) CreateAutoCode(s *system.SysAutoCode) error {
if s.PackageName == "autocode" || s.PackageName == "system" || s.PackageName == "example" || s.PackageName == "" {
return errors.New("不能使用已保留的package name")
}
if !errors.Is(global.GVA_DB.Where("package_name = ?", s.PackageName).First(&system.SysAutoCode{}).Error, gorm.ErrRecordNotFound) {
return errors.New("存在相同PackageName")
}
if e := autoCodeService.CreatePackageTemp(s.PackageName); e != nil {
return e
}
return global.GVA_DB.Create(&s).Error
}
func (autoCodeService *AutoCodeService) GetPackage() (pkgList []system.SysAutoCode, err error) {
err = global.GVA_DB.Find(&pkgList).Error
return pkgList, err
}
func (autoCodeService *AutoCodeService) DelPackage(a system.SysAutoCode) error {
return global.GVA_DB.Delete(&a).Error
}
func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) error {
Init(packageName)
pendingTemp := []autoPackage{{
path: packageService,
name: packageServiceName,
temp: string(subcontract.Server),
}, {
path: packageRouter,
name: packageRouterName,
temp: string(subcontract.Router),
}, {
path: packageAPI,
name: packageAPIName,
temp: string(subcontract.API),
}}
for i, s := range pendingTemp {
pendingTemp[i].path = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, filepath.Clean(fmt.Sprintf(s.path, packageName)))
}
// 选择模板
for _, s := range pendingTemp {
err := os.MkdirAll(filepath.Dir(s.path), 0755)
if err != nil {
return err
}
f, err := os.Create(s.path)
if err != nil {
return err
}
defer f.Close()
temp, err := template.New("").Parse(s.temp)
if err != nil {
return err
}
err = temp.Execute(f, struct {
PackageName string `json:"package_name"`
}{packageName})
if err != nil {
return err
}
}
// 创建完成后在对应的位置插入结构代码
for _, v := range pendingTemp {
meta := packageInjectionMap[v.name]
if err := ast2.ImportReference(meta.path, fmt.Sprintf(meta.importCodeF, v.name, packageName), fmt.Sprintf(meta.structNameF, utils.FirstUpper(packageName)), fmt.Sprintf(meta.packageNameF, packageName), meta.groupName); err != nil {
return err
}
}
return nil
}
// CreatePlug 自动创建插件模板
func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
// 检查列表参数是否有效
plug.CheckList()
tplFileList, _ := autoCodeService.GetAllTplFile(plugPath, nil)
for _, tpl := range tplFileList {
temp, err := template.ParseFiles(tpl)
if err != nil {
zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
return err
}
pathArr := strings.SplitAfter(tpl, "/")
if strings.Index(pathArr[2], "tpl") < 0 {
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[2]))
os.MkdirAll(dirPath, 0755)
}
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugPath):len(tpl)-4]))
f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
zap.L().Error("open file", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
return err
}
defer f.Close()
err = temp.Execute(f, plug)
if err != nil {
zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
return err
}
}
return nil
}
func (autoCodeService *AutoCodeService) InstallPlugin(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
for i := range paths {
paths[i] = filepath.ToSlash(paths[i])
pathArr := strings.Split(paths[i], "/")
ln := len(pathArr)
if ln < 2 {
continue
}
if pathArr[ln-2] == "server.exe.exe" && pathArr[ln-1] == "plugin" {
serverIndex = i
}
if pathArr[ln-2] == "web" && pathArr[ln-1] == "plugin" {
webIndex = i
}
}
if webIndex == -1 && serverIndex == -1 {
zap.L().Error("非标准插件,请按照文档自动迁移使用")
return webIndex, serverIndex, errors.New("非标准插件,请按照文档自动迁移使用")
}
if webIndex != -1 {
err = installation(paths[webIndex], global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Web)
if err != nil {
return webIndex, serverIndex, err
}
}
if serverIndex != -1 {
err = installation(paths[serverIndex], global.GVA_CONFIG.AutoCode.Server, global.GVA_CONFIG.AutoCode.Server)
}
return webIndex, serverIndex, 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.ToSlash(global.GVA_CONFIG.AutoCode.Root + formPath + "/" + path)
var to = filepath.ToSlash(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(path); ok {
continue
}
np = append(np, path)
}
return np
}
func skipMacSpecialDocument(src string) (bool, error) {
if strings.Contains(src, ".DS_Store") || strings.Contains(src, "__MACOSX") {
return true, nil
}
return false, nil
}
func (autoCodeService *AutoCodeService) 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文件
// 判断目录是否存在
webInfo, err := os.Stat(webPath)
if err != nil {
return "", errors.New("web路径不存在")
}
serverInfo, err := os.Stat(serverPath)
if err != nil {
return "", errors.New("server路径不存在")
}
fileName := plugName + ".zip"
// 创建一个新的zip文件
zipFile, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
return
}
defer zipFile.Close()
// 创建一个zip写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
// 创建一个新的文件头
webHeader, err := zip.FileInfoHeader(webInfo)
if err != nil {
return
}
// 创建一个新的文件头
serverHeader, err := zip.FileInfoHeader(serverInfo)
if err != nil {
return
}
webHeader.Name = filepath.Join(plugName, "web", "plugin")
serverHeader.Name = filepath.Join(plugName, "server.exe.exe", "plugin")
// 将文件添加到zip归档中
_, err = zipWriter.CreateHeader(serverHeader)
_, err = zipWriter.CreateHeader(webHeader)
// 遍历webPath目录并将所有非隐藏文件添加到zip归档中
err = filepath.Walk(webPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过隐藏文件
if strings.HasPrefix(info.Name(), ".") {
return nil
}
// 创建一个新的文件头
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// 将文件头的名称设置为文件的相对路径
rel, _ := filepath.Rel(webPath, path)
header.Name = filepath.Join(plugName, "web", "plugin", plugName, rel)
// 将文件添加到zip归档中
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// 打开文件并将其内容复制到zip归档中
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
})
if err != nil {
return
}
// 遍历serverPath目录并将所有非隐藏文件添加到zip归档中
err = filepath.Walk(serverPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过隐藏文件和目录
if strings.HasPrefix(info.Name(), ".") {
return nil
}
// 创建一个新的文件头
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
// 将文件头的名称设置为文件的相对路径
rel, _ := filepath.Rel(serverPath, path)
header.Name = filepath.Join(plugName, "server.exe.exe", "plugin", plugName, rel)
// 将文件添加到zip归档中
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// 打开文件并将其内容复制到zip归档中
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
})
if err != nil {
return
}
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
}

View File

@@ -0,0 +1,49 @@
package system
import (
"miniapp/global"
"miniapp/model/system/response"
)
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 "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
}
}

View File

@@ -0,0 +1,58 @@
package system
import (
"fmt"
"miniapp/global"
"miniapp/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 sysdatabases;"
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.length as data_type_long
from %s.DBO.syscolumns sc,systypes st where sc.xtype=st.xtype and st.usertype=0 and sc.id in (select id from %s.DBO.sysobjects where xtype='U' and name='%s');`, dbName, dbName, tableName)
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
}

View File

@@ -0,0 +1,69 @@
package system
import (
"miniapp/global"
"miniapp/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 COLUMN_NAME column_name,
DATA_TYPE data_type,
CASE 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,
COLUMN_COMMENT column_comment
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE table_name = ?
AND table_schema = ?
`
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
}

View File

@@ -0,0 +1,53 @@
package system
import (
"miniapp/global"
"miniapp/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"
from all_tab_columns a , all_col_comments b
where a.OWNER = b.OWNER
and a.TABLE_NAME = b.TABLE_NAME
and a.COLUMN_NAME = b.COLUMN_NAME
and lower(a.table_name) = ?
and lower(a.OWNER) = ?
`
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error
return entities, err
}

View File

@@ -0,0 +1,108 @@
package system
import (
"github.com/pkg/errors"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"miniapp/global"
"miniapp/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, _err := gorm.Open(postgres.Open(global.GVA_CONFIG.Pgsql.LinkDsn(dbName)), &gorm.Config{Logger: logger.Default.LogMode(logger.Info)})
if _err != nil {
return nil, errors.Wrapf(err, "[pgsql] 连接 数据库(%s)的表失败!", dbName)
}
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
FROM
INFORMATION_SCHEMA.COLUMNS psc
WHERE
table_catalog = ?
AND table_schema = 'public'
AND TABLE_NAME = ?;
`
var entities []response.Column
db, _err := gorm.Open(postgres.Open(global.GVA_CONFIG.Pgsql.LinkDsn(dbName)), &gorm.Config{Logger: logger.Default.LogMode(logger.Info)})
if _err != nil {
return nil, errors.Wrapf(err, "[pgsql] 连接 数据库(%s)的表(%s)失败!", dbName, tableName)
}
//sql = strings.ReplaceAll(sql, "@table_catalog", dbName)
//sql = strings.ReplaceAll(sql, "@table_name", tableName)
err = db.Raw(sql, dbName, tableName).Scan(&entities).Error
return entities, err
}

View File

@@ -0,0 +1,82 @@
package system
import (
"fmt"
"miniapp/global"
"miniapp/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"`
}
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,
})
}
return entities, err
}

View File

@@ -0,0 +1,153 @@
package system
import (
"errors"
"fmt"
systemReq "miniapp/model/system/request"
"miniapp/utils/ast"
"path/filepath"
"strconv"
"strings"
"time"
"miniapp/model/system/response"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
"miniapp/utils"
"go.uber.org/zap"
)
var RepeatErr = errors.New("重复创建")
type AutoCodeHistoryService struct{}
var AutoCodeHistoryServiceApp = new(AutoCodeHistoryService)
// CreateAutoCodeHistory 创建代码生成器历史记录
// RouterPath : RouterPath@RouterString;RouterPath2@RouterString2
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) CreateAutoCodeHistory(meta, structName, structCNName, autoCodePath string, injectionMeta string, tableName string, apiIds string, Package string, BusinessDB string) error {
return global.GVA_DB.Create(&system.SysAutoCodeHistory{
Package: Package,
RequestMeta: meta,
AutoCodePath: autoCodePath,
InjectionMeta: injectionMeta,
StructName: structName,
StructCNName: structCNName,
TableName: tableName,
ApiIDs: apiIds,
BusinessDB: BusinessDB,
}).Error
}
// First 根据id获取代码生成器历史的数据
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) First(info *request.GetById) (string, error) {
var meta string
return meta, global.GVA_DB.Model(system.SysAutoCodeHistory{}).Select("request_meta").Where("id = ?", info.Uint()).First(&meta).Error
}
// Repeat 检测重复
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) Repeat(businessDB, structName, Package string) bool {
var count int64
global.GVA_DB.Model(&system.SysAutoCodeHistory{}).Where("business_db = ? and struct_name = ? and package = ? and flag = 0", businessDB, structName, Package).Count(&count)
return count > 0
}
// RollBack 回滚
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) RollBack(info *systemReq.RollBack) error {
md := system.SysAutoCodeHistory{}
if err := global.GVA_DB.Where("id = ?", info.ID).First(&md).Error; err != nil {
return err
}
// 清除API表
ids := request.IdsReq{}
idsStr := strings.Split(md.ApiIDs, ";")
for i := range idsStr[0 : len(idsStr)-1] {
id, err := strconv.Atoi(idsStr[i])
if err != nil {
return err
}
ids.Ids = append(ids.Ids, id)
}
err := ApiServiceApp.DeleteApisByIds(ids)
if err != nil {
global.GVA_LOG.Error("ClearTag DeleteApiByIds:", zap.Error(err))
}
// 删除表
if info.DeleteTable {
if err = AutoCodeServiceApp.DropTable(md.BusinessDB, md.TableName); err != nil {
global.GVA_LOG.Error("ClearTag DropTable:", zap.Error(err))
}
}
// 删除文件
for _, path := range strings.Split(md.AutoCodePath, ";") {
// 增加安全判断补丁:
_path, err := filepath.Abs(path)
if err != nil || _path != path {
continue
}
// 迁移
nPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root,
"rm_file", time.Now().Format("20060102"), filepath.Base(filepath.Dir(filepath.Dir(path))), filepath.Base(filepath.Dir(path)), filepath.Base(path))
// 判断目标文件是否存在
for utils.FileExist(nPath) {
fmt.Println("文件已存在:", nPath)
nPath += fmt.Sprintf("_%d", time.Now().Nanosecond())
}
err = utils.FileMove(path, nPath)
if err != nil {
global.GVA_LOG.Error("file move err ", zap.Error(err))
}
//_ = utils.DeLFile(path)
}
// 清除注入
for _, v := range strings.Split(md.InjectionMeta, ";") {
// RouterPath@functionName@RouterString
meta := strings.Split(v, "@")
if len(meta) == 3 {
_ = utils.AutoClearCode(meta[0], meta[2])
}
}
ast.RollBackAst(md.Package, md.StructName)
md.Flag = 1
return global.GVA_DB.Save(&md).Error
}
// Delete 删除历史数据
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) Delete(info *request.GetById) error {
return global.GVA_DB.Where("id = ?", info.Uint()).Delete(&system.SysAutoCodeHistory{}).Error
}
// GetList 获取系统历史数据
// Author [SliverHorn](https://github.com/SliverHorn)
// Author [songzhibin97](https://github.com/songzhibin97)
func (autoCodeHistoryService *AutoCodeHistoryService) GetList(info request.PageInfo) (list []response.AutoCodeHistory, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
db := global.GVA_DB.Model(&system.SysAutoCodeHistory{})
var entities []response.AutoCodeHistory
err = db.Count(&total).Error
if err != nil {
return nil, total, err
}
err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&entities).Error
return entities, total, err
}

View File

@@ -0,0 +1,125 @@
package system
import (
"errors"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system"
)
type BaseMenuService struct{}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteBaseMenu
//@description: 删除基础路由
//@param: id float64
//@return: err error
func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
err = global.GVA_DB.Preload("MenuBtn").Preload("Parameters").Where("parent_id = ?", id).First(&system.SysBaseMenu{}).Error
if err != nil {
var menu system.SysBaseMenu
db := global.GVA_DB.Preload("SysAuthoritys").Where("id = ?", id).First(&menu).Delete(&menu)
err = global.GVA_DB.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
err = global.GVA_DB.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
err = global.GVA_DB.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
if len(menu.SysAuthoritys) > 0 {
err = global.GVA_DB.Model(&menu).Association("SysAuthoritys").Delete(&menu.SysAuthoritys)
} else {
err = db.Error
if err != nil {
return
}
}
} else {
return errors.New("此菜单存在子菜单不可删除")
}
return err
}
//@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 {
db := 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 = db.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
}

View File

@@ -0,0 +1,141 @@
package system
import (
"errors"
"strconv"
"sync"
"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"
"miniapp/global"
"miniapp/model/system/request"
)
//@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(AuthorityID uint, casbinInfos []request.CasbinInfo) error {
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})
}
}
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
e := casbinService.Casbin()
err = e.LoadPolicy()
if err != nil {
return err
}
return err
}
//@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: Casbin
//@description: 持久化到数据库 引入自定义规则
//@return: *casbin.Enforcer
var (
syncedCachedEnforcer *casbin.SyncedCachedEnforcer
once sync.Once
)
func (casbinService *CasbinService) Casbin() *casbin.SyncedCachedEnforcer {
once.Do(func() {
a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
if err != nil {
zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
return
}
text := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
`
m, err := model.NewModelFromString(text)
if err != nil {
zap.L().Error("字符串加载模型失败!", zap.Error(err))
return
}
syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
syncedCachedEnforcer.SetExpireTime(60 * 60)
_ = syncedCachedEnforcer.LoadPolicy()
})
return syncedCachedEnforcer
}

View File

@@ -0,0 +1,167 @@
package system
import (
"context"
"errors"
"fmt"
"github.com/sashabaranov/go-openai"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system"
"miniapp/model/system/request"
"strings"
)
type ChatGptService struct{}
func (chat *ChatGptService) CreateSK(option system.SysChatGptOption) error {
_, err := chat.GetSK()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return global.GVA_DB.Create(option).Error
}
return err
}
return errors.New("已经存在sk")
}
func (chat *ChatGptService) GetSK() (option system.SysChatGptOption, err error) {
err = global.GVA_DB.First(&option).Error
return
}
func (chat *ChatGptService) DeleteSK() error {
option, err := chat.GetSK()
if err != nil {
return err
}
return global.GVA_DB.Delete(option, "sk = ?", option.SK).Error
}
func (chat *ChatGptService) GetTable(req request.ChatGptRequest) (sql string, results []map[string]interface{}, err error) {
if req.DBName == "" {
return "", nil, errors.New("未选择db")
}
var tablesInfo []system.ChatField
var tableName string
global.GVA_DB.Table("information_schema.columns").Where("TABLE_SCHEMA = ?", req.DBName).Scan(&tablesInfo)
var tablesMap = make(map[string]bool)
for i := range tablesInfo {
tablesMap[tablesInfo[i].TABLE_NAME] = true
}
for i := range tablesMap {
tableName += i + ","
}
option, err := chat.GetSK()
if err != nil {
return "", nil, err
}
client := openai.NewClient(option.SK)
ctx := context.Background()
tables, err := getTables(ctx, client, tableName, req.Chat)
if err != nil {
return "", nil, err
}
tableArr := strings.Split(tables, ",")
if len(tableArr) != 0 {
firstKeyArr := strings.Split(tableArr[0], ":")
tableArr[0] = strings.Trim(firstKeyArr[len(firstKeyArr)-1], "\n")
}
sql, err = getSql(ctx, client, tableArr, tablesInfo, req.Chat)
if err != nil {
return "", nil, err
}
err = global.GVA_DB.Raw(sql).Scan(&results).Error
return sql, results, err
}
func getTables(ctx context.Context, client *openai.Client, tables string, chat string) (string, error) {
var tablePrompt = `You are a database administrator
Filter out the table names you might need from the tables I provided formatted as:
Table1,Table2,Table3
I will provide you with the following table configuration information:
Table1,Table2,Table3
Do not return information other than the table
Configured as:
%s
The problem is:
%s
`
content := fmt.Sprintf(tablePrompt, tables, chat)
chatReq := openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: content,
},
},
}
resp, err := client.CreateChatCompletion(ctx, chatReq)
if err != nil {
fmt.Printf("Completion error: %v\n", err)
return "", err
}
return resp.Choices[0].Message.Content, nil
}
func getSql(ctx context.Context, client *openai.Client, tables []string, ChatField []system.ChatField, chat string) (string, error) {
var sqlPrompt = `You are a database administrator
Give me an SQL statement based on my question
I will provide you with my current database table configuration information in the form below
Table Name | Column Name | Column Description
Do not return information other than SQL
Configured as:
%s
The problem is:
%s`
var configured string
for ii := range ChatField {
for i := range tables {
if strings.Index(tables[i], ChatField[ii].TABLE_NAME) > -1 {
configured += fmt.Sprintf("%s | %s | %s \n", ChatField[ii].TABLE_NAME, ChatField[ii].COLUMN_NAME, ChatField[ii].COLUMN_COMMENT)
}
}
}
if configured == "" {
return "", errors.New("未找到表")
}
chatReq := openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: fmt.Sprintf(sqlPrompt, configured, chat),
},
},
}
resp, err := client.CreateChatCompletion(ctx, chatReq)
if err != nil {
fmt.Printf("Completion error: %v\n", err)
return "", err
}
sql := resp.Choices[0].Message.Content
sqlArr := strings.Split(sql, ":")
sql = sqlArr[len(sqlArr)-1]
return sql, nil
}

View File

@@ -0,0 +1,128 @@
package system
import (
"errors"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system"
"miniapp/model/system/request"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteSysDictionary
//@description: 创建字典数据
//@param: sysDictionary model.SysDictionary
//@return: err error
type DictionaryService struct{}
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,
}
db := global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict)
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 = db.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(info request.SysDictionarySearch) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
// 创建db
db := global.GVA_DB.Model(&system.SysDictionary{})
var sysDictionarys []system.SysDictionary
// 如果有条件搜索 下方会自动创建搜索语句
if info.Name != "" {
db = db.Where("`name` LIKE ?", "%"+info.Name+"%")
}
if info.Type != "" {
db = db.Where("`type` LIKE ?", "%"+info.Type+"%")
}
if info.Status != nil {
db = db.Where("`status` = ?", info.Status)
}
if info.Desc != "" {
db = db.Where("`desc` LIKE ?", "%"+info.Desc+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Find(&sysDictionarys).Error
return sysDictionarys, total, err
}

View File

@@ -0,0 +1,86 @@
package system
import (
"miniapp/global"
"miniapp/model/system"
"miniapp/model/system/request"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CreateSysDictionaryDetail
//@description: 创建字典详情数据
//@param: sysDictionaryDetail model.SysDictionaryDetail
//@return: err error
type DictionaryDetailService struct{}
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 != 0 {
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
}

View File

@@ -0,0 +1,184 @@
package system
import (
"context"
"database/sql"
"errors"
"fmt"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system/request"
"sort"
)
const (
Mysql = "mysql"
Pgsql = "pgsql"
Sqlite = "sqlite"
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()
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")
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]
}

View File

@@ -0,0 +1,96 @@
package system
import (
"context"
"errors"
"fmt"
"path/filepath"
"github.com/gookit/color"
"miniapp/config"
"miniapp/utils"
"github.com/gofrs/uuid/v5"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system/request"
)
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.Must(uuid.NewV4()).String()
cs := utils.StructToMap(global.GVA_CONFIG)
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
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
}

View File

@@ -0,0 +1,95 @@
package system
import (
"context"
"errors"
"fmt"
"path/filepath"
"github.com/gookit/color"
"miniapp/config"
"miniapp/utils"
"github.com/gofrs/uuid/v5"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/system/request"
)
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.Must(uuid.NewV4()).String()
cs := utils.StructToMap(global.GVA_CONFIG)
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
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()
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
}

View File

@@ -0,0 +1,86 @@
package system
import (
"context"
"errors"
"github.com/glebarez/sqlite"
"github.com/gofrs/uuid/v5"
"github.com/gookit/color"
"gorm.io/gorm"
"miniapp/config"
"miniapp/global"
"miniapp/model/system/request"
"miniapp/utils"
"path/filepath"
)
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("mysql config invalid")
}
global.GVA_CONFIG.System.DbType = "sqlite"
global.GVA_CONFIG.Sqlite = c
global.GVA_CONFIG.JWT.SigningKey = uuid.Must(uuid.NewV4()).String()
cs := utils.StructToMap(global.GVA_CONFIG)
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
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
}

236
service/system/sys_menu.go Normal file
View File

@@ -0,0 +1,236 @@
package system
import (
"errors"
"strconv"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
)
//@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[string][]system.SysMenu, err error) {
var allMenus []system.SysMenu
var baseMenu []system.SysBaseMenu
var btns []system.SysAuthorityBtn
treeMap = make(map[string][]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: strconv.Itoa(int(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.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[string][]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() (list interface{}, total int64, err error) {
var menuList []system.SysBaseMenu
treeMap, err := menuService.getBaseMenuTreeMap()
menuList = treeMap["0"]
for i := 0; i < len(menuList); i++ {
err = menuService.getBaseChildrenList(&menuList[i], treeMap)
}
return menuList, total, 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[string][]system.SysBaseMenu) (err error) {
menu.Children = treeMap[strconv.Itoa(int(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() (treeMap map[string][]system.SysBaseMenu, err error) {
var allMenus []system.SysBaseMenu
treeMap = make(map[string][]system.SysBaseMenu)
err = global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters").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() (menus []system.SysBaseMenu, err error) {
treeMap, err := menuService.getBaseMenuTreeMap()
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, authorityId uint) (err error) {
var auth system.SysAuthority
auth.AuthorityId = authorityId
auth.SysBaseMenus = menus
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: uint(info.AuthorityId),
MenuId: strconv.Itoa(int(baseMenu[i].ID)),
Parameters: baseMenu[i].Parameters,
})
}
// sql := "SELECT authority_menu.keep_alive,authority_menu.default_menu,authority_menu.created_at,authority_menu.updated_at,authority_menu.deleted_at,authority_menu.menu_level,authority_menu.parent_id,authority_menu.path,authority_menu.`name`,authority_menu.hidden,authority_menu.component,authority_menu.title,authority_menu.icon,authority_menu.sort,authority_menu.menu_id,authority_menu.authority_id FROM authority_menu WHERE authority_menu.authority_id = ? ORDER BY authority_menu.sort ASC"
// err = global.GVA_DB.Raw(sql, authorityId).Scan(&menus).Error
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"
}
}

View File

@@ -0,0 +1,86 @@
package system
import (
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
systemReq "miniapp/model/system/request"
)
//@author: [granty1](https://github.com/granty1)
//@function: CreateSysOperationRecord
//@description: 创建记录
//@param: sysOperationRecord model.SysOperationRecord
//@return: err error
type OperationRecordService struct{}
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: DeleteSysOperationRecord
//@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
}

View File

@@ -0,0 +1,60 @@
package system
import (
"go.uber.org/zap"
"miniapp/config"
"miniapp/global"
"miniapp/model/system"
"miniapp/utils"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetSystemConfig
//@description: 读取配置文件
//@return: conf config.Server, err error
type SystemConfigService struct{}
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.exe.exe *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
}

245
service/system/sys_user.go Normal file
View File

@@ -0,0 +1,245 @@
package system
import (
"errors"
"fmt"
"time"
"github.com/gofrs/uuid/v5"
"gorm.io/gorm"
"miniapp/global"
"miniapp/model/common/request"
"miniapp/model/system"
"miniapp/utils"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Register
//@description: 用户注册
//@param: u model.SysUser
//@return: userInter system.SysUser, err error
type UserService struct{}
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.Must(uuid.NewV4())
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 request.PageInfo) (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
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("该用户无此角色")
}
err = global.GVA_DB.Where("id = ?", id).First(&system.SysUser{}).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(id uint, authorityIds []uint) (err error) {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
TxErr := tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
if TxErr != nil {
return TxErr
}
var useAuthority []system.SysUserAuthority
for _, v := range authorityIds {
useAuthority = append(useAuthority, system.SysUserAuthority{
SysUserId: id, SysAuthorityAuthorityId: v,
})
}
TxErr = tx.Create(&useAuthority).Error
if TxErr != nil {
return TxErr
}
TxErr = tx.Where("id = ?", id).First(&system.SysUser{}).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) {
var user system.SysUser
err = global.GVA_DB.Where("id = ?", id).Delete(&user).Error
if err != nil {
return err
}
err = global.GVA_DB.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
return err
}
//@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", "sideMode", "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,
"side_mode": req.SideMode,
"enable": req.Enable,
}).Error
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: SetUserInfo
//@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)
//@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
}

1
service/system/todos.go Normal file
View File

@@ -0,0 +1 @@
package system