✨ Init
This commit is contained in:
104
service/app/base.go
Normal file
104
service/app/base.go
Normal 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
9
service/app/enter.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package app
|
||||
|
||||
type ServiceGroup struct {
|
||||
Oauth2ClientService
|
||||
UserService
|
||||
FavoriteService
|
||||
VisionService
|
||||
TodesService
|
||||
}
|
34
service/app/favorite.go
Normal file
34
service/app/favorite.go
Normal 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
|
||||
}
|
18
service/app/oauth2_client.go
Normal file
18
service/app/oauth2_client.go
Normal 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
19
service/app/todes.go
Normal 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
156
service/app/user.go
Normal 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
26
service/app/vision.go
Normal 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
15
service/enter.go
Normal 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
6
service/example/enter.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package example
|
||||
|
||||
type ServiceGroup struct {
|
||||
CustomerService
|
||||
FileUploadAndDownloadService
|
||||
}
|
69
service/example/exa_breakpoint_continue.go
Normal file
69
service/example/exa_breakpoint_continue.go
Normal 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
|
||||
}
|
85
service/example/exa_customer.go
Normal file
85
service/example/exa_customer.go
Normal 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
|
||||
}
|
108
service/example/exa_file_upload_download.go
Normal file
108
service/example/exa_file_upload_download.go
Normal 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
38
service/system/article.go
Normal 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
37
service/system/banner.go
Normal 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
104
service/system/base.go
Normal 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
23
service/system/enter.go
Normal 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
|
||||
}
|
37
service/system/hospital.go
Normal file
37
service/system/hospital.go
Normal 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
|
||||
}
|
82
service/system/jwt_black_list.go
Normal file
82
service/system/jwt_black_list.go
Normal 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
196
service/system/sys_api.go
Normal 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
|
||||
}
|
220
service/system/sys_authority.go
Normal file
220
service/system/sys_authority.go
Normal 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(©Info.Authority).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var btns []system.SysAuthorityBtn
|
||||
|
||||
err = global.GVA_DB.Find(&btns, "authority_id = ?", copyInfo.OldAuthorityId).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(btns) > 0 {
|
||||
for i := range btns {
|
||||
btns[i].AuthorityId = copyInfo.Authority.AuthorityId
|
||||
}
|
||||
err = global.GVA_DB.Create(&btns).Error
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
paths := CasbinServiceApp.GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId)
|
||||
err = CasbinServiceApp.UpdateCasbin(copyInfo.Authority.AuthorityId, paths)
|
||||
if err != nil {
|
||||
_ = authorityService.DeleteAuthority(©Info.Authority)
|
||||
}
|
||||
return copyInfo.Authority, err
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: UpdateAuthority
|
||||
//@description: 更改一个角色
|
||||
//@param: auth model.SysAuthority
|
||||
//@return: authority system.SysAuthority, err error
|
||||
|
||||
func (authorityService *AuthorityService) UpdateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) {
|
||||
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
|
||||
}
|
58
service/system/sys_authority_btn.go
Normal file
58
service/system/sys_authority_btn.go
Normal 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("此按钮正在被使用无法删除")
|
||||
}
|
958
service/system/sys_auto_code.go
Normal file
958
service/system/sys_auto_code.go
Normal 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
|
||||
}
|
49
service/system/sys_auto_code_interface.go
Normal file
49
service/system/sys_auto_code_interface.go
Normal 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
|
||||
}
|
||||
|
||||
}
|
58
service/system/sys_auto_code_mssql.go
Normal file
58
service/system/sys_auto_code_mssql.go
Normal 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
|
||||
}
|
69
service/system/sys_auto_code_mysql.go
Normal file
69
service/system/sys_auto_code_mysql.go
Normal 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
|
||||
}
|
53
service/system/sys_auto_code_oracle.go
Normal file
53
service/system/sys_auto_code_oracle.go
Normal 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
|
||||
}
|
108
service/system/sys_auto_code_pgsql.go
Normal file
108
service/system/sys_auto_code_pgsql.go
Normal 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
|
||||
}
|
82
service/system/sys_auto_code_sqlite.go
Normal file
82
service/system/sys_auto_code_sqlite.go
Normal 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
|
||||
}
|
153
service/system/sys_autocode_history.go
Normal file
153
service/system/sys_autocode_history.go
Normal 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
|
||||
}
|
125
service/system/sys_base_menu.go
Normal file
125
service/system/sys_base_menu.go
Normal 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
|
||||
}
|
141
service/system/sys_casbin.go
Normal file
141
service/system/sys_casbin.go
Normal 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
|
||||
}
|
167
service/system/sys_chatgpt.go
Normal file
167
service/system/sys_chatgpt.go
Normal 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
|
||||
}
|
128
service/system/sys_dictionary.go
Normal file
128
service/system/sys_dictionary.go
Normal 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
|
||||
}
|
86
service/system/sys_dictionary_detail.go
Normal file
86
service/system/sys_dictionary_detail.go
Normal 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
|
||||
}
|
184
service/system/sys_initdb.go
Normal file
184
service/system/sys_initdb.go
Normal 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]
|
||||
}
|
96
service/system/sys_initdb_mysql.go
Normal file
96
service/system/sys_initdb_mysql.go
Normal 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
|
||||
}
|
95
service/system/sys_initdb_pgsql.go
Normal file
95
service/system/sys_initdb_pgsql.go
Normal 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
|
||||
}
|
86
service/system/sys_initdb_sqlite.go
Normal file
86
service/system/sys_initdb_sqlite.go
Normal 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
236
service/system/sys_menu.go
Normal 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"
|
||||
}
|
||||
}
|
86
service/system/sys_operation_record.go
Normal file
86
service/system/sys_operation_record.go
Normal 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
|
||||
}
|
60
service/system/sys_system.go
Normal file
60
service/system/sys_system.go
Normal 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
245
service/system/sys_user.go
Normal 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
1
service/system/todos.go
Normal file
@@ -0,0 +1 @@
|
||||
package system
|
Reference in New Issue
Block a user