🎨 精简完善系统
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
| `packfile` | 静态文件打包 | 静态文件打包 |
|
||||
| `resource` | 静态资源文件夹 | 负责存放静态文件 |
|
||||
| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 |
|
||||
| `--page` | 表单生成器 | 表单生成器 打包后的dist |
|
||||
| `--page` | 页面静态资源目录 | 历史页面资源输出目录 |
|
||||
| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 |
|
||||
| `router` | 路由层 | 路由层 |
|
||||
| `service` | service层 | 存放业务逻辑问题 |
|
||||
@@ -51,4 +51,3 @@
|
||||
| `utils` | 工具包 | 工具函数封装 |
|
||||
| `--timer` | timer | 定时器接口封装 |
|
||||
| `--upload` | oss | oss接口封装 |
|
||||
|
||||
|
||||
@@ -15,10 +15,8 @@ type ApiGroup struct {
|
||||
OperationRecordApi
|
||||
DictionaryDetailApi
|
||||
AuthorityBtnApi
|
||||
SysExportTemplateApi
|
||||
McpApi
|
||||
SysParamsApi
|
||||
SysVersionApi
|
||||
SysErrorApi
|
||||
LoginLogApi
|
||||
ApiTokenApi
|
||||
@@ -39,7 +37,6 @@ var (
|
||||
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
|
||||
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
|
||||
mcpService = service.ServiceGroupApp.SystemServiceGroup.McpService
|
||||
sysErrorService = service.ServiceGroupApp.SystemServiceGroup.SysErrorService
|
||||
loginLogService = service.ServiceGroupApp.SystemServiceGroup.LoginLogService
|
||||
|
||||
@@ -1,456 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/response"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/service"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 用于token一次性存储
|
||||
var (
|
||||
exportTokenCache = make(map[string]interface{})
|
||||
exportTokenExpiration = make(map[string]time.Time)
|
||||
tokenMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// 五分钟检测窗口过期
|
||||
func cleanupExpiredTokens() {
|
||||
for {
|
||||
time.Sleep(5 * time.Minute)
|
||||
tokenMutex.Lock()
|
||||
now := time.Now()
|
||||
for token, expiry := range exportTokenExpiration {
|
||||
if now.After(expiry) {
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
}
|
||||
}
|
||||
tokenMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
go cleanupExpiredTokens()
|
||||
}
|
||||
|
||||
type SysExportTemplateApi struct {
|
||||
}
|
||||
|
||||
var sysExportTemplateService = service.ServiceGroupApp.SystemServiceGroup.SysExportTemplateService
|
||||
|
||||
// PreviewSQL 预览最终生成的SQL
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 预览最终生成的SQL(不执行查询,仅返回SQL字符串)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param templateID query string true "导出模板ID"
|
||||
// @Param params query string false "查询参数编码字符串,参考 ExportExcel 组件"
|
||||
// @Success 200 {object} response.Response{data=map[string]string} "获取成功"
|
||||
// @Router /sysExportTemplate/previewSQL [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) PreviewSQL(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 直接复用导出接口的参数组织方式:使用 URL Query,其中 params 为内部编码的查询字符串
|
||||
queryParams := c.Request.URL.Query()
|
||||
|
||||
if sqlPreview, err := sysExportTemplateService.PreviewSQL(templateID, queryParams); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithData(gin.H{"sql": sqlPreview}, c)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSysExportTemplate 创建导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 创建导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "创建导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysExportTemplate/createSysExportTemplate [post]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) CreateSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
verify := utils.Rules{
|
||||
"Name": {utils.NotEmpty()},
|
||||
}
|
||||
if err := utils.Verify(sysExportTemplate, verify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.CreateSysExportTemplate(&sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplate 删除导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.DeleteSysExportTemplate(sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplateByIds 批量删除导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 批量删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplateByIds [delete]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) {
|
||||
var IDS request.IdsReq
|
||||
err := c.ShouldBindJSON(&IDS)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.DeleteSysExportTemplateByIds(IDS); err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSysExportTemplate 更新导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 更新导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysExportTemplate true "更新导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysExportTemplate/updateSysExportTemplate [put]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindJSON(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
verify := utils.Rules{
|
||||
"Name": {utils.NotEmpty()},
|
||||
}
|
||||
if err := utils.Verify(sysExportTemplate, verify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.UpdateSysExportTemplate(sysExportTemplate); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// FindSysExportTemplate 用id查询导出模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 用id查询导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysExportTemplate true "用id查询导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysExportTemplate/findSysExportTemplate [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) FindSysExportTemplate(c *gin.Context) {
|
||||
var sysExportTemplate system.SysExportTemplate
|
||||
err := c.ShouldBindQuery(&sysExportTemplate)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if resysExportTemplate, err := sysExportTemplateService.GetSysExportTemplate(sysExportTemplate.ID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithData(gin.H{"resysExportTemplate": resysExportTemplate}, c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSysExportTemplateList 分页获取导出模板列表
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 分页获取导出模板列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysExportTemplateSearch true "分页获取导出模板列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysExportTemplate/getSysExportTemplateList [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) GetSysExportTemplateList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysExportTemplateSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if list, total, err := sysExportTemplateService.GetSysExportTemplateInfoList(pageInfo); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// ExportExcel 导出表格token
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportExcel [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcel(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
queryParams := c.Request.URL.Query()
|
||||
|
||||
//创造一次性token
|
||||
token := utils.RandomString(32) // 随机32位
|
||||
|
||||
// 记录本次请求参数
|
||||
exportParams := map[string]interface{}{
|
||||
"templateID": templateID,
|
||||
"queryParams": queryParams,
|
||||
}
|
||||
|
||||
// 参数保留记录完成鉴权
|
||||
tokenMutex.Lock()
|
||||
exportTokenCache[token] = exportParams
|
||||
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 生成一次性链接
|
||||
exportUrl := fmt.Sprintf("/sysExportTemplate/exportExcelByToken?token=%s", token)
|
||||
response.OkWithData(exportUrl, c)
|
||||
}
|
||||
|
||||
// ExportExcelByToken 导出表格
|
||||
// @Tags ExportExcelByToken
|
||||
// @Summary 导出表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportExcelByToken [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportExcelByToken(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
response.FailWithMessage("导出token不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取token并且从缓存中剔除
|
||||
tokenMutex.RLock()
|
||||
exportParamsRaw, exists := exportTokenCache[token]
|
||||
expiry, _ := exportTokenExpiration[token]
|
||||
tokenMutex.RUnlock()
|
||||
|
||||
if !exists || time.Now().After(expiry) {
|
||||
global.GVA_LOG.Error("导出token无效或已过期!")
|
||||
response.FailWithMessage("导出token无效或已过期", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 从token获取参数
|
||||
exportParams, ok := exportParamsRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
global.GVA_LOG.Error("解析导出参数失败!")
|
||||
response.FailWithMessage("解析导出参数失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取导出参数
|
||||
templateID := exportParams["templateID"].(string)
|
||||
queryParams := exportParams["queryParams"].(url.Values)
|
||||
|
||||
// 清理一次性token
|
||||
tokenMutex.Lock()
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 导出
|
||||
if file, name, err := sysExportTemplateService.ExportExcel(templateID, queryParams); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".xlsx"))
|
||||
c.Header("success", "true")
|
||||
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// ExportTemplate 导出表格模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportTemplate [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplate(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 创造一次性token
|
||||
token := utils.RandomString(32) // 随机32位
|
||||
|
||||
// 记录本次请求参数
|
||||
exportParams := map[string]interface{}{
|
||||
"templateID": templateID,
|
||||
"isTemplate": true,
|
||||
}
|
||||
|
||||
// 参数保留记录完成鉴权
|
||||
tokenMutex.Lock()
|
||||
exportTokenCache[token] = exportParams
|
||||
exportTokenExpiration[token] = time.Now().Add(30 * time.Minute)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 生成一次性链接
|
||||
exportUrl := fmt.Sprintf("/sysExportTemplate/exportTemplateByToken?token=%s", token)
|
||||
response.OkWithData(exportUrl, c)
|
||||
}
|
||||
|
||||
// ExportTemplateByToken 通过token导出表格模板
|
||||
// @Tags ExportTemplateByToken
|
||||
// @Summary 通过token导出表格模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportTemplateByToken [get]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ExportTemplateByToken(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
response.FailWithMessage("导出token不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取token并且从缓存中剔除
|
||||
tokenMutex.RLock()
|
||||
exportParamsRaw, exists := exportTokenCache[token]
|
||||
expiry, _ := exportTokenExpiration[token]
|
||||
tokenMutex.RUnlock()
|
||||
|
||||
if !exists || time.Now().After(expiry) {
|
||||
global.GVA_LOG.Error("导出token无效或已过期!")
|
||||
response.FailWithMessage("导出token无效或已过期", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 从token获取参数
|
||||
exportParams, ok := exportParamsRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
global.GVA_LOG.Error("解析导出参数失败!")
|
||||
response.FailWithMessage("解析导出参数失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否为模板导出
|
||||
isTemplate, _ := exportParams["isTemplate"].(bool)
|
||||
if !isTemplate {
|
||||
global.GVA_LOG.Error("token类型错误!")
|
||||
response.FailWithMessage("token类型错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取导出参数
|
||||
templateID := exportParams["templateID"].(string)
|
||||
|
||||
// 清理一次性token
|
||||
tokenMutex.Lock()
|
||||
delete(exportTokenCache, token)
|
||||
delete(exportTokenExpiration, token)
|
||||
tokenMutex.Unlock()
|
||||
|
||||
// 导出模板
|
||||
if file, name, err := sysExportTemplateService.ExportTemplate(templateID); err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.xlsx"))
|
||||
c.Header("success", "true")
|
||||
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// ImportExcel 导入表格
|
||||
// @Tags SysImportTemplate
|
||||
// @Summary 导入表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/importExcel [post]
|
||||
func (sysExportTemplateApi *SysExportTemplateApi) ImportExcel(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
response.FailWithMessage("模板ID不能为空", c)
|
||||
return
|
||||
}
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("文件获取失败", c)
|
||||
return
|
||||
}
|
||||
if err := sysExportTemplateService.ImportExcel(templateID, file); err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
}
|
||||
@@ -1,486 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/response"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/Go-Web-Template/server/model/system/response"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SysVersionApi struct{}
|
||||
|
||||
// buildMenuTree 构建菜单树结构
|
||||
func buildMenuTree(menus []system.SysBaseMenu) []system.SysBaseMenu {
|
||||
// 创建菜单映射
|
||||
menuMap := make(map[uint]*system.SysBaseMenu)
|
||||
for i := range menus {
|
||||
menuMap[menus[i].ID] = &menus[i]
|
||||
}
|
||||
|
||||
// 构建树结构
|
||||
var rootMenus []system.SysBaseMenu
|
||||
for _, menu := range menus {
|
||||
if menu.ParentId == 0 {
|
||||
// 根菜单
|
||||
menuData := convertMenuToStruct(menu, menuMap)
|
||||
rootMenus = append(rootMenus, menuData)
|
||||
}
|
||||
}
|
||||
|
||||
// 按sort排序根菜单
|
||||
sort.Slice(rootMenus, func(i, j int) bool {
|
||||
return rootMenus[i].Sort < rootMenus[j].Sort
|
||||
})
|
||||
|
||||
return rootMenus
|
||||
}
|
||||
|
||||
// convertMenuToStruct 将菜单转换为结构体并递归处理子菜单
|
||||
func convertMenuToStruct(menu system.SysBaseMenu, menuMap map[uint]*system.SysBaseMenu) system.SysBaseMenu {
|
||||
result := system.SysBaseMenu{
|
||||
Path: menu.Path,
|
||||
Name: menu.Name,
|
||||
Hidden: menu.Hidden,
|
||||
Component: menu.Component,
|
||||
Sort: menu.Sort,
|
||||
Meta: menu.Meta,
|
||||
}
|
||||
|
||||
// 清理并复制参数数据
|
||||
if len(menu.Parameters) > 0 {
|
||||
cleanParameters := make([]system.SysBaseMenuParameter, 0, len(menu.Parameters))
|
||||
for _, param := range menu.Parameters {
|
||||
cleanParam := system.SysBaseMenuParameter{
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
|
||||
}
|
||||
cleanParameters = append(cleanParameters, cleanParam)
|
||||
}
|
||||
result.Parameters = cleanParameters
|
||||
}
|
||||
|
||||
// 清理并复制菜单按钮数据
|
||||
if len(menu.MenuBtn) > 0 {
|
||||
cleanMenuBtns := make([]system.SysBaseMenuBtn, 0, len(menu.MenuBtn))
|
||||
for _, btn := range menu.MenuBtn {
|
||||
cleanBtn := system.SysBaseMenuBtn{
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysBaseMenuID
|
||||
}
|
||||
cleanMenuBtns = append(cleanMenuBtns, cleanBtn)
|
||||
}
|
||||
result.MenuBtn = cleanMenuBtns
|
||||
}
|
||||
|
||||
// 查找并处理子菜单
|
||||
var children []system.SysBaseMenu
|
||||
for _, childMenu := range menuMap {
|
||||
if childMenu.ParentId == menu.ID {
|
||||
childData := convertMenuToStruct(*childMenu, menuMap)
|
||||
children = append(children, childData)
|
||||
}
|
||||
}
|
||||
|
||||
// 按sort排序子菜单
|
||||
if len(children) > 0 {
|
||||
sort.Slice(children, func(i, j int) bool {
|
||||
return children[i].Sort < children[j].Sort
|
||||
})
|
||||
result.Children = children
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteSysVersion 删除版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 删除版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysVersion true "删除版本管理"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /sysVersion/deleteSysVersion [delete]
|
||||
func (sysVersionApi *SysVersionApi) DeleteSysVersion(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
err := sysVersionService.DeleteSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysVersionByIds 批量删除版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 批量删除版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /sysVersion/deleteSysVersionByIds [delete]
|
||||
func (sysVersionApi *SysVersionApi) DeleteSysVersionByIds(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := sysVersionService.DeleteSysVersionByIds(ctx, IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// FindSysVersion 用id查询版本管理
|
||||
// @Tags SysVersion
|
||||
// @Summary 用id查询版本管理
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query uint true "用id查询版本管理"
|
||||
// @Success 200 {object} response.Response{data=system.SysVersion,msg=string} "查询成功"
|
||||
// @Router /sysVersion/findSysVersion [get]
|
||||
func (sysVersionApi *SysVersionApi) FindSysVersion(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
resysVersion, err := sysVersionService.GetSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resysVersion, c)
|
||||
}
|
||||
|
||||
// GetSysVersionList 分页获取版本管理列表
|
||||
// @Tags SysVersion
|
||||
// @Summary 分页获取版本管理列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysVersionSearch true "分页获取版本管理列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /sysVersion/getSysVersionList [get]
|
||||
func (sysVersionApi *SysVersionApi) GetSysVersionList(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var pageInfo systemReq.SysVersionSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := sysVersionService.GetSysVersionInfoList(ctx, pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetSysVersionPublic 不需要鉴权的版本管理接口
|
||||
// @Tags SysVersion
|
||||
// @Summary 不需要鉴权的版本管理接口
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysVersion/getSysVersionPublic [get]
|
||||
func (sysVersionApi *SysVersionApi) GetSysVersionPublic(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 此接口不需要鉴权
|
||||
// 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
|
||||
sysVersionService.GetSysVersionPublic(ctx)
|
||||
response.OkWithDetailed(gin.H{
|
||||
"info": "不需要鉴权的版本管理接口信息",
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// ExportVersion 创建发版数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 创建发版数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ExportVersionRequest true "创建发版数据"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /sysVersion/exportVersion [post]
|
||||
func (sysVersionApi *SysVersionApi) ExportVersion(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var req systemReq.ExportVersionRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取选中的菜单数据
|
||||
var menuData []system.SysBaseMenu
|
||||
if len(req.MenuIds) > 0 {
|
||||
menuData, err = sysVersionService.GetMenusByIds(ctx, req.MenuIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取菜单数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中的API数据
|
||||
var apiData []system.SysApi
|
||||
if len(req.ApiIds) > 0 {
|
||||
apiData, err = sysVersionService.GetApisByIds(ctx, req.ApiIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取API数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取API数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中的字典数据
|
||||
var dictData []system.SysDictionary
|
||||
if len(req.DictIds) > 0 {
|
||||
dictData, err = sysVersionService.GetDictionariesByIds(ctx, req.DictIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取字典数据失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取字典数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 处理菜单数据,构建递归的children结构
|
||||
processedMenus := buildMenuTree(menuData)
|
||||
|
||||
// 处理API数据,清除ID和时间戳字段
|
||||
processedApis := make([]system.SysApi, 0, len(apiData))
|
||||
for _, api := range apiData {
|
||||
cleanApi := system.SysApi{
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
processedApis = append(processedApis, cleanApi)
|
||||
}
|
||||
|
||||
// 处理字典数据,清除ID和时间戳字段,包含字典详情
|
||||
processedDicts := make([]system.SysDictionary, 0, len(dictData))
|
||||
for _, dict := range dictData {
|
||||
cleanDict := system.SysDictionary{
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
}
|
||||
|
||||
// 处理字典详情数据,清除ID和时间戳字段
|
||||
cleanDetails := make([]system.SysDictionaryDetail, 0, len(dict.SysDictionaryDetails))
|
||||
for _, detail := range dict.SysDictionaryDetails {
|
||||
cleanDetail := system.SysDictionaryDetail{
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
// 不复制 ID, CreatedAt, UpdatedAt, SysDictionaryID
|
||||
}
|
||||
cleanDetails = append(cleanDetails, cleanDetail)
|
||||
}
|
||||
cleanDict.SysDictionaryDetails = cleanDetails
|
||||
|
||||
processedDicts = append(processedDicts, cleanDict)
|
||||
}
|
||||
|
||||
// 构建导出数据
|
||||
exportData := systemRes.ExportVersionResponse{
|
||||
Version: systemReq.VersionInfo{
|
||||
Name: req.VersionName,
|
||||
Code: req.VersionCode,
|
||||
Description: req.Description,
|
||||
ExportTime: time.Now().Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
Menus: processedMenus,
|
||||
Apis: processedApis,
|
||||
Dictionaries: processedDicts,
|
||||
}
|
||||
|
||||
// 转换为JSON
|
||||
jsonData, err := json.MarshalIndent(exportData, "", " ")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("JSON序列化失败!", zap.Error(err))
|
||||
response.FailWithMessage("JSON序列化失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 保存版本记录
|
||||
version := system.SysVersion{
|
||||
VersionName: utils.Pointer(req.VersionName),
|
||||
VersionCode: utils.Pointer(req.VersionCode),
|
||||
Description: utils.Pointer(req.Description),
|
||||
VersionData: utils.Pointer(string(jsonData)),
|
||||
}
|
||||
|
||||
err = sysVersionService.CreateSysVersion(ctx, &version)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存版本记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("保存版本记录失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("创建发版成功", c)
|
||||
}
|
||||
|
||||
// DownloadVersionJson 下载版本JSON数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 下载版本JSON数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query string true "版本ID"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "下载成功"
|
||||
// @Router /sysVersion/downloadVersionJson [get]
|
||||
func (sysVersionApi *SysVersionApi) DownloadVersionJson(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
if ID == "" {
|
||||
response.FailWithMessage("版本ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取版本记录
|
||||
version, err := sysVersionService.GetSysVersion(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取版本记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取版本记录失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建JSON数据
|
||||
var jsonData []byte
|
||||
if version.VersionData != nil && *version.VersionData != "" {
|
||||
jsonData = []byte(*version.VersionData)
|
||||
} else {
|
||||
// 如果没有存储的JSON数据,构建一个基本的结构
|
||||
basicData := systemRes.ExportVersionResponse{
|
||||
Version: systemReq.VersionInfo{
|
||||
Name: *version.VersionName,
|
||||
Code: *version.VersionCode,
|
||||
Description: *version.Description,
|
||||
ExportTime: version.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
Menus: []system.SysBaseMenu{},
|
||||
Apis: []system.SysApi{},
|
||||
}
|
||||
jsonData, _ = json.MarshalIndent(basicData, "", " ")
|
||||
}
|
||||
|
||||
// 设置下载响应头
|
||||
filename := fmt.Sprintf("version_%s_%s.json", *version.VersionCode, time.Now().Format("20060102150405"))
|
||||
c.Header("Content-Type", "application/json")
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
c.Header("Content-Length", strconv.Itoa(len(jsonData)))
|
||||
|
||||
c.Data(http.StatusOK, "application/json", jsonData)
|
||||
}
|
||||
|
||||
// ImportVersion 导入版本数据
|
||||
// @Tags SysVersion
|
||||
// @Summary 导入版本数据
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ImportVersionRequest true "版本JSON数据"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入成功"
|
||||
// @Router /sysVersion/importVersion [post]
|
||||
func (sysVersionApi *SysVersionApi) ImportVersion(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 获取JSON数据
|
||||
var importData systemReq.ImportVersionRequest
|
||||
err := c.ShouldBindJSON(&importData)
|
||||
if err != nil {
|
||||
response.FailWithMessage("解析JSON数据失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证数据格式
|
||||
if importData.VersionInfo.Name == "" || importData.VersionInfo.Code == "" {
|
||||
response.FailWithMessage("版本信息格式错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 导入菜单数据
|
||||
if len(importData.ExportMenu) > 0 {
|
||||
if err := sysVersionService.ImportMenus(ctx, importData.ExportMenu); err != nil {
|
||||
global.GVA_LOG.Error("导入菜单失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入菜单失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 导入API数据
|
||||
if len(importData.ExportApi) > 0 {
|
||||
if err := sysVersionService.ImportApis(importData.ExportApi); err != nil {
|
||||
global.GVA_LOG.Error("导入API失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入API失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 导入字典数据
|
||||
if len(importData.ExportDictionary) > 0 {
|
||||
if err := sysVersionService.ImportDictionaries(importData.ExportDictionary); err != nil {
|
||||
global.GVA_LOG.Error("导入字典失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入字典失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 创建导入记录
|
||||
jsonData, _ := json.Marshal(importData)
|
||||
version := system.SysVersion{
|
||||
VersionName: utils.Pointer(importData.VersionInfo.Name),
|
||||
VersionCode: utils.Pointer(fmt.Sprintf("%s_imported_%s", importData.VersionInfo.Code, time.Now().Format("20060102150405"))),
|
||||
Description: utils.Pointer(fmt.Sprintf("导入版本: %s", importData.VersionInfo.Description)),
|
||||
VersionData: utils.Pointer(string(jsonData)),
|
||||
}
|
||||
|
||||
err = sysVersionService.CreateSysVersion(ctx, &version)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("保存导入记录失败!", zap.Error(err))
|
||||
// 这里不返回错误,因为数据已经导入成功
|
||||
}
|
||||
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ captcha:
|
||||
open-captcha-timeout: 3600 # open-captcha大于0时才生效
|
||||
|
||||
# mysql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-react-admin.com/docs/first_master)
|
||||
mysql:
|
||||
path: ""
|
||||
port: ""
|
||||
@@ -111,7 +111,7 @@ mysql:
|
||||
log-zap: false
|
||||
|
||||
# pgsql connect configuration
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master)
|
||||
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-react-admin.com/docs/first_master)
|
||||
pgsql:
|
||||
path: ""
|
||||
port: ""
|
||||
@@ -211,7 +211,7 @@ tencent-cos:
|
||||
region: ap-shanghai
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
base-url: https://gin-react-admin.com
|
||||
path-prefix: git.echol.cn/loser/Go-Web-Template/server
|
||||
|
||||
# aws s3 configuration (minio compatible)
|
||||
@@ -223,13 +223,13 @@ aws-s3:
|
||||
disable-ssl: false
|
||||
secret-id: your-secret-id
|
||||
secret-key: your-secret-key
|
||||
base-url: https://gin.vue.admin
|
||||
base-url: https://gin-react-admin.com
|
||||
path-prefix: git.echol.cn/loser/Go-Web-Template/server
|
||||
|
||||
# cloudflare r2 configuration
|
||||
cloudflare-r2:
|
||||
bucket: xxxx0bucket
|
||||
base-url: https://gin.vue.admin.com
|
||||
base-url: https://gin-react-admin.com
|
||||
path: uploads
|
||||
account-id: xxx_account_id
|
||||
access-key-id: xxx_key_id
|
||||
|
||||
@@ -34,7 +34,7 @@ func RunServer() {
|
||||
mcpBaseURL := mcpTool.ResolveMCPServiceURL()
|
||||
|
||||
fmt.Printf(`
|
||||
欢迎使用 gin-vue-admin
|
||||
欢迎使用 Gin-React-Admin
|
||||
当前版本:%s
|
||||
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||
MCP 独立服务请手动启动: go run ./cmd/mcp -config ./cmd/mcp/config.yaml
|
||||
|
||||
3820
server/docs/docs.go
3820
server/docs/docs.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ const (
|
||||
// Version 当前版本号
|
||||
Version = "v2.9.1"
|
||||
// AppName 应用名称
|
||||
AppName = "Gin-Vue-Admin"
|
||||
AppName = "Gin-React-Admin"
|
||||
// Description 应用描述
|
||||
Description = "使用gin+vue进行极速开发的全栈开发基础平台"
|
||||
Description = "使用gin+react进行极速开发的全栈开发基础平台"
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
commonModel "git.echol.cn/loser/Go-Web-Template/server/model/common"
|
||||
sysModel "git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/service/system"
|
||||
@@ -46,11 +47,7 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
sysModel.SysParams{},
|
||||
sysModel.SysVersion{},
|
||||
sysModel.SysError{},
|
||||
sysModel.SysLoginLog{},
|
||||
sysModel.SysApiToken{},
|
||||
@@ -86,9 +83,6 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
|
||||
adapter.CasbinRule{},
|
||||
|
||||
|
||||
@@ -55,11 +55,7 @@ func RegisterTables() {
|
||||
system.SysBaseMenuParameter{},
|
||||
system.SysBaseMenuBtn{},
|
||||
system.SysAuthorityBtn{},
|
||||
system.SysExportTemplate{},
|
||||
system.Condition{},
|
||||
system.JoinTemplate{},
|
||||
system.SysParams{},
|
||||
system.SysVersion{},
|
||||
system.SysError{},
|
||||
system.SysApiToken{},
|
||||
system.SysLoginLog{},
|
||||
|
||||
@@ -79,26 +79,24 @@ func Routers() *gin.Engine {
|
||||
}
|
||||
|
||||
{
|
||||
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
|
||||
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
|
||||
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
|
||||
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
|
||||
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
|
||||
systemRouter.InitSysVersionRouter(PrivateGroup) // 发版相关路由
|
||||
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
|
||||
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
|
||||
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
|
||||
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
|
||||
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
|
||||
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
|
||||
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
|
||||
systemRouter.InitMcpRouter(PrivateGroup) // MCP 管理
|
||||
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
commonRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
commonRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
systemRouter.InitApiRouter(PrivateGroup, PublicGroup) // 注册功能api路由
|
||||
systemRouter.InitJwtRouter(PrivateGroup) // jwt相关路由
|
||||
systemRouter.InitUserRouter(PrivateGroup) // 注册用户路由
|
||||
systemRouter.InitMenuRouter(PrivateGroup) // 注册menu路由
|
||||
systemRouter.InitSystemRouter(PrivateGroup) // system相关路由
|
||||
systemRouter.InitCasbinRouter(PrivateGroup) // 权限相关路由
|
||||
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
|
||||
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
|
||||
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
|
||||
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
|
||||
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
|
||||
systemRouter.InitMcpRouter(PrivateGroup) // MCP 管理
|
||||
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
commonRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
commonRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
}
|
||||
|
||||
// 注册业务路由
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
// @Tag.Name SysUser
|
||||
// @Tag.Description 用户
|
||||
|
||||
// @title Gin-Vue-Admin Swagger API接口文档
|
||||
// @title Gin-React-Admin Swagger API接口文档
|
||||
// @version v2.9.1
|
||||
// @description 使用gin+vue进行极速开发的全栈开发基础平台
|
||||
// @description 使用gin+react进行极速开发的全栈开发基础平台
|
||||
// @securityDefinitions.apikey ApiKeyAuth
|
||||
// @in header
|
||||
// @name x-token
|
||||
|
||||
@@ -18,7 +18,7 @@ func requireMCPIntegration(t *testing.T) {
|
||||
// 测试 MCP 客户端连接
|
||||
func TestMcpClientConnection(t *testing.T) {
|
||||
requireMCPIntegration(t)
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("create client failed: %v", err)
|
||||
@@ -28,7 +28,7 @@ func TestMcpClientConnection(t *testing.T) {
|
||||
func TestTools(t *testing.T) {
|
||||
requireMCPIntegration(t)
|
||||
t.Run("currentTime", func(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
@@ -58,7 +58,7 @@ func TestTools(t *testing.T) {
|
||||
|
||||
t.Run("getNickname", func(t *testing.T) {
|
||||
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
@@ -102,7 +102,7 @@ func TestTools(t *testing.T) {
|
||||
|
||||
func TestGetTools(t *testing.T) {
|
||||
requireMCPIntegration(t)
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-react-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
|
||||
@@ -69,14 +69,14 @@ func (m *MenuCreator) New() mcp.Tool {
|
||||
),
|
||||
mcp.WithString("name",
|
||||
mcp.Required(),
|
||||
mcp.Description("路由name,用于Vue Router,如:userList"),
|
||||
mcp.Description("路由name,用于前端路由标识,如:userList"),
|
||||
),
|
||||
mcp.WithBoolean("hidden",
|
||||
mcp.Description("是否在菜单列表中隐藏"),
|
||||
),
|
||||
mcp.WithString("component",
|
||||
mcp.Required(),
|
||||
mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"),
|
||||
mcp.Description("对应的前端React组件路径,如:features/users/UserManagementPage"),
|
||||
),
|
||||
mcp.WithNumber("sort",
|
||||
mcp.Description("菜单排序号,数字越小越靠前"),
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SysExportTemplateSearch struct {
|
||||
system.SysExportTemplate
|
||||
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
request.PageInfo
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func DefaultMenu() []system.SysBaseMenu {
|
||||
ParentId: 0,
|
||||
Path: "dashboard",
|
||||
Name: "dashboard",
|
||||
Component: "view/dashboard/index.vue",
|
||||
Component: "features/dashboard/DashboardPage",
|
||||
Sort: 1,
|
||||
Meta: system.Meta{
|
||||
Title: "仪表盘",
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SysVersionSearch struct {
|
||||
CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"`
|
||||
VersionName *string `json:"versionName" form:"versionName"`
|
||||
VersionCode *string `json:"versionCode" form:"versionCode"`
|
||||
request.PageInfo
|
||||
}
|
||||
|
||||
// ExportVersionRequest 导出版本请求结构体
|
||||
type ExportVersionRequest struct {
|
||||
VersionName string `json:"versionName" binding:"required"` // 版本名称
|
||||
VersionCode string `json:"versionCode" binding:"required"` // 版本号
|
||||
Description string `json:"description"` // 版本描述
|
||||
MenuIds []uint `json:"menuIds"` // 选中的菜单ID列表
|
||||
ApiIds []uint `json:"apiIds"` // 选中的API ID列表
|
||||
DictIds []uint `json:"dictIds"` // 选中的字典ID列表
|
||||
}
|
||||
|
||||
// ImportVersionRequest 导入版本请求结构体
|
||||
type ImportVersionRequest struct {
|
||||
VersionInfo VersionInfo `json:"version" binding:"required"` // 版本信息
|
||||
ExportMenu []system.SysBaseMenu `json:"menus"` // 菜单数据,直接复用SysBaseMenu
|
||||
ExportApi []system.SysApi `json:"apis"` // API数据,直接复用SysApi
|
||||
ExportDictionary []system.SysDictionary `json:"dictionaries"` // 字典数据,直接复用SysDictionary
|
||||
}
|
||||
|
||||
// VersionInfo 版本信息结构体
|
||||
type VersionInfo struct {
|
||||
Name string `json:"name" binding:"required"` // 版本名称
|
||||
Code string `json:"code" binding:"required"` // 版本号
|
||||
Description string `json:"description"` // 版本描述
|
||||
ExportTime string `json:"exportTime"` // 导出时间
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system/request"
|
||||
)
|
||||
|
||||
// ExportVersionResponse 导出版本响应结构体
|
||||
type ExportVersionResponse struct {
|
||||
Version request.VersionInfo `json:"version"` // 版本信息
|
||||
Menus []system.SysBaseMenu `json:"menus"` // 菜单数据,直接复用SysBaseMenu
|
||||
Apis []system.SysApi `json:"apis"` // API数据,直接复用SysApi
|
||||
Dictionaries []system.SysDictionary `json:"dictionaries"` // 字典数据,直接复用SysDictionary
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// 自动生成模板SysExportTemplate
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
)
|
||||
|
||||
// 导出模板 结构体 SysExportTemplate
|
||||
type SysExportTemplate struct {
|
||||
global.GVA_MODEL
|
||||
DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称
|
||||
Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称
|
||||
TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称
|
||||
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识
|
||||
TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息
|
||||
SQL string `json:"sql" form:"sql" gorm:"column:sql;type:text;comment:自定义导出SQL;"` //自定义导出SQL
|
||||
ImportSQL string `json:"importSql" form:"importSql" gorm:"column:import_sql;type:text;comment:自定义导入SQL;"` //自定义导入SQL
|
||||
Limit *int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"`
|
||||
Order string `json:"order" form:"order" gorm:"column:order;comment:排序"`
|
||||
Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"`
|
||||
JoinTemplate []JoinTemplate `json:"joinTemplate" form:"joinTemplate" gorm:"foreignKey:TemplateID;references:TemplateID;comment:关联"`
|
||||
}
|
||||
|
||||
type JoinTemplate struct {
|
||||
global.GVA_MODEL
|
||||
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"`
|
||||
JOINS string `json:"joins" form:"joins" gorm:"column:joins;comment:关联"`
|
||||
Table string `json:"table" form:"table" gorm:"column:table;comment:关联表"`
|
||||
ON string `json:"on" form:"on" gorm:"column:on;comment:关联条件"`
|
||||
}
|
||||
|
||||
func (JoinTemplate) TableName() string {
|
||||
return "sys_export_template_join"
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
global.GVA_MODEL
|
||||
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"`
|
||||
From string `json:"from" form:"from" gorm:"column:from;comment:条件取的key"`
|
||||
Column string `json:"column" form:"column" gorm:"column:column;comment:作为查询条件的字段"`
|
||||
Operator string `json:"operator" form:"operator" gorm:"column:operator;comment:操作符"`
|
||||
}
|
||||
|
||||
func (Condition) TableName() string {
|
||||
return "sys_export_template_condition"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// 自动生成模板SysVersion
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
)
|
||||
|
||||
// 版本管理 结构体 SysVersion
|
||||
type SysVersion struct {
|
||||
global.GVA_MODEL
|
||||
VersionName *string `json:"versionName" form:"versionName" gorm:"comment:版本名称;column:version_name;size:255;" binding:"required"` //版本名称
|
||||
VersionCode *string `json:"versionCode" form:"versionCode" gorm:"comment:版本号;column:version_code;size:100;" binding:"required"` //版本号
|
||||
Description *string `json:"description" form:"description" gorm:"comment:版本描述;column:description;size:500;"` //版本描述
|
||||
VersionData *string `json:"versionData" form:"versionData" gorm:"comment:版本数据JSON;column:version_data;type:text;"` //版本数据
|
||||
}
|
||||
|
||||
// TableName 版本管理 SysVersion自定义表名 sys_versions
|
||||
func (SysVersion) TableName() string {
|
||||
return "sys_versions"
|
||||
}
|
||||
@@ -16,10 +16,8 @@ type RouterGroup struct {
|
||||
OperationRecordRouter
|
||||
DictionaryDetailRouter
|
||||
AuthorityBtnRouter
|
||||
SysExportTemplateRouter
|
||||
McpRouter
|
||||
SysParamsRouter
|
||||
SysVersionRouter
|
||||
SysErrorRouter
|
||||
LoginLogRouter
|
||||
ApiTokenRouter
|
||||
@@ -39,8 +37,6 @@ var (
|
||||
authorityMenuApi = api.ApiGroupApp.SystemApiGroup.AuthorityMenuApi
|
||||
operationRecordApi = api.ApiGroupApp.SystemApiGroup.OperationRecordApi
|
||||
dictionaryDetailApi = api.ApiGroupApp.SystemApiGroup.DictionaryDetailApi
|
||||
exportTemplateApi = api.ApiGroupApp.SystemApiGroup.SysExportTemplateApi
|
||||
mcpApi = api.ApiGroupApp.SystemApiGroup.McpApi
|
||||
sysVersionApi = api.ApiGroupApp.SystemApiGroup.SysVersionApi
|
||||
sysErrorApi = api.ApiGroupApp.SystemApiGroup.SysErrorApi
|
||||
)
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SysExportTemplateRouter struct {
|
||||
}
|
||||
|
||||
// InitSysExportTemplateRouter 初始化 导出模板 路由信息
|
||||
func (s *SysExportTemplateRouter) InitSysExportTemplateRouter(Router *gin.RouterGroup, pubRouter *gin.RouterGroup) {
|
||||
sysExportTemplateRouter := Router.Group("sysExportTemplate").Use(middleware.OperationRecord())
|
||||
sysExportTemplateRouterWithoutRecord := Router.Group("sysExportTemplate")
|
||||
sysExportTemplateRouterWithoutAuth := pubRouter.Group("sysExportTemplate")
|
||||
|
||||
{
|
||||
sysExportTemplateRouter.POST("createSysExportTemplate", exportTemplateApi.CreateSysExportTemplate) // 新建导出模板
|
||||
sysExportTemplateRouter.DELETE("deleteSysExportTemplate", exportTemplateApi.DeleteSysExportTemplate) // 删除导出模板
|
||||
sysExportTemplateRouter.DELETE("deleteSysExportTemplateByIds", exportTemplateApi.DeleteSysExportTemplateByIds) // 批量删除导出模板
|
||||
sysExportTemplateRouter.PUT("updateSysExportTemplate", exportTemplateApi.UpdateSysExportTemplate) // 更新导出模板
|
||||
sysExportTemplateRouter.POST("importExcel", exportTemplateApi.ImportExcel) // 导入excel模板数据
|
||||
}
|
||||
{
|
||||
sysExportTemplateRouterWithoutRecord.GET("findSysExportTemplate", exportTemplateApi.FindSysExportTemplate) // 根据ID获取导出模板
|
||||
sysExportTemplateRouterWithoutRecord.GET("getSysExportTemplateList", exportTemplateApi.GetSysExportTemplateList) // 获取导出模板列表
|
||||
sysExportTemplateRouterWithoutRecord.GET("exportExcel", exportTemplateApi.ExportExcel) // 获取导出token
|
||||
sysExportTemplateRouterWithoutRecord.GET("exportTemplate", exportTemplateApi.ExportTemplate) // 导出表格模板
|
||||
sysExportTemplateRouterWithoutRecord.GET("previewSQL", exportTemplateApi.PreviewSQL) // 预览SQL
|
||||
}
|
||||
{
|
||||
sysExportTemplateRouterWithoutAuth.GET("exportExcelByToken", exportTemplateApi.ExportExcelByToken) // 通过token导出表格
|
||||
sysExportTemplateRouterWithoutAuth.GET("exportTemplateByToken", exportTemplateApi.ExportTemplateByToken) // 通过token导出模板
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/Go-Web-Template/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SysVersionRouter struct{}
|
||||
|
||||
// InitSysVersionRouter 初始化 版本管理 路由信息
|
||||
func (s *SysVersionRouter) InitSysVersionRouter(Router *gin.RouterGroup) {
|
||||
sysVersionRouter := Router.Group("sysVersion").Use(middleware.OperationRecord())
|
||||
sysVersionRouterWithoutRecord := Router.Group("sysVersion")
|
||||
{
|
||||
sysVersionRouter.DELETE("deleteSysVersion", sysVersionApi.DeleteSysVersion) // 删除版本管理
|
||||
sysVersionRouter.DELETE("deleteSysVersionByIds", sysVersionApi.DeleteSysVersionByIds) // 批量删除版本管理
|
||||
sysVersionRouter.POST("exportVersion", sysVersionApi.ExportVersion) // 导出版本数据
|
||||
sysVersionRouter.POST("importVersion", sysVersionApi.ImportVersion) // 导入版本数据
|
||||
}
|
||||
{
|
||||
sysVersionRouterWithoutRecord.GET("findSysVersion", sysVersionApi.FindSysVersion) // 根据ID获取版本管理
|
||||
sysVersionRouterWithoutRecord.GET("getSysVersionList", sysVersionApi.GetSysVersionList) // 获取版本管理列表
|
||||
sysVersionRouterWithoutRecord.GET("downloadVersionJson", sysVersionApi.DownloadVersionJson) // 下载版本JSON数据
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,7 @@ type ServiceGroup struct {
|
||||
OperationRecordService
|
||||
DictionaryDetailService
|
||||
AuthorityBtnService
|
||||
SysExportTemplateService
|
||||
SysParamsService
|
||||
SysVersionService
|
||||
McpService
|
||||
SysErrorService
|
||||
LoginLogService
|
||||
|
||||
@@ -1,724 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/utils"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SysExportTemplateService struct {
|
||||
}
|
||||
|
||||
var SysExportTemplateServiceApp = new(SysExportTemplateService)
|
||||
|
||||
// CreateSysExportTemplate 创建导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) {
|
||||
err = global.GVA_DB.Create(sysExportTemplate).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplate 删除导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
||||
err = global.GVA_DB.Delete(&sysExportTemplate).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysExportTemplateByIds 批量删除导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplateByIds(ids request.IdsReq) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]system.SysExportTemplate{}, "id in ?", ids.Ids).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateSysExportTemplate 更新导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
conditions := sysExportTemplate.Conditions
|
||||
e := tx.Delete(&[]system.Condition{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
sysExportTemplate.Conditions = nil
|
||||
|
||||
joins := sysExportTemplate.JoinTemplate
|
||||
e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
sysExportTemplate.JoinTemplate = nil
|
||||
|
||||
e = tx.Updates(&sysExportTemplate).Error
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if len(conditions) > 0 {
|
||||
for i := range conditions {
|
||||
conditions[i].ID = 0
|
||||
}
|
||||
e = tx.Create(&conditions).Error
|
||||
}
|
||||
if len(joins) > 0 {
|
||||
for i := range joins {
|
||||
joins[i].ID = 0
|
||||
}
|
||||
e = tx.Create(&joins).Error
|
||||
}
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// GetSysExportTemplate 根据id获取导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetSysExportTemplateInfoList 分页获取导出模板记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateInfoList(info systemReq.SysExportTemplateSearch) (list []system.SysExportTemplate, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysExportTemplate{})
|
||||
var sysExportTemplates []system.SysExportTemplate
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Name != "" {
|
||||
db = db.Where("name LIKE ?", "%"+info.Name+"%")
|
||||
}
|
||||
if info.TableName != "" {
|
||||
db = db.Where("table_name = ?", info.TableName)
|
||||
}
|
||||
if info.TemplateID != "" {
|
||||
db = db.Where("template_id = ?", info.TemplateID)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Find(&sysExportTemplates).Error
|
||||
return sysExportTemplates, total, err
|
||||
}
|
||||
|
||||
// ExportExcel 导出Excel
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) {
|
||||
var params = values.Get("params")
|
||||
paramsValues, err := url.ParseQuery(params)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("解析 params 参数失败: %v", err)
|
||||
}
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
f := excelize.NewFile()
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
// Create a new sheet.
|
||||
index, err := f.NewSheet("Sheet1")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var tableTitle []string
|
||||
var selectKeyFmt []string
|
||||
for _, key := range columns {
|
||||
selectKeyFmt = append(selectKeyFmt, key)
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
selects := strings.Join(selectKeyFmt, ", ")
|
||||
var tableMap []map[string]interface{}
|
||||
db := global.GVA_DB
|
||||
if template.DBName != "" {
|
||||
db = global.MustGetGlobalDBByDBName(template.DBName)
|
||||
}
|
||||
|
||||
// 如果有自定义SQL,则优先使用自定义SQL
|
||||
if template.SQL != "" {
|
||||
// 将 url.Values 转换为 map[string]interface{} 以支持 GORM 的命名参数
|
||||
sqlParams := make(map[string]interface{})
|
||||
for k, v := range paramsValues {
|
||||
if len(v) > 0 {
|
||||
sqlParams[k] = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
// 执行原生 SQL,支持 @key 命名参数
|
||||
err = db.Raw(template.SQL, sqlParams).Scan(&tableMap).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
db = db.Joins(join.JOINS + " " + join.Table + " ON " + join.ON)
|
||||
}
|
||||
}
|
||||
|
||||
db = db.Select(selects).Table(template.TableName)
|
||||
|
||||
filterDeleted := false
|
||||
|
||||
filterParam := paramsValues.Get("filterDeleted")
|
||||
if filterParam == "true" {
|
||||
filterDeleted = true
|
||||
}
|
||||
|
||||
if filterDeleted {
|
||||
// 自动过滤主表的软删除
|
||||
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
||||
|
||||
// 过滤关联表的软删除(如果有)
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
// 检查关联表是否有deleted_at字段
|
||||
hasDeletedAt := sysExportTemplateService.hasDeletedAtColumn(join.Table)
|
||||
if hasDeletedAt {
|
||||
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(template.Conditions) > 0 {
|
||||
for _, condition := range template.Conditions {
|
||||
sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator)
|
||||
value := paramsValues.Get(condition.From)
|
||||
|
||||
if condition.Operator == "IN" || condition.Operator == "NOT IN" {
|
||||
sql = fmt.Sprintf("%s %s (?)", condition.Column, condition.Operator)
|
||||
}
|
||||
|
||||
if condition.Operator == "BETWEEN" {
|
||||
sql = fmt.Sprintf("%s BETWEEN ? AND ?", condition.Column)
|
||||
startValue := paramsValues.Get("start" + condition.From)
|
||||
endValue := paramsValues.Get("end" + condition.From)
|
||||
if startValue != "" && endValue != "" {
|
||||
db = db.Where(sql, startValue, endValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if value != "" {
|
||||
if condition.Operator == "LIKE" {
|
||||
value = "%" + value + "%"
|
||||
}
|
||||
db = db.Where(sql, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 通过参数传入limit
|
||||
limit := paramsValues.Get("limit")
|
||||
if limit != "" {
|
||||
l, e := strconv.Atoi(limit)
|
||||
if e == nil {
|
||||
db = db.Limit(l)
|
||||
}
|
||||
}
|
||||
// 模板的默认limit
|
||||
if limit == "" && template.Limit != nil && *template.Limit != 0 {
|
||||
db = db.Limit(*template.Limit)
|
||||
}
|
||||
|
||||
// 通过参数传入offset
|
||||
offset := paramsValues.Get("offset")
|
||||
if offset != "" {
|
||||
o, e := strconv.Atoi(offset)
|
||||
if e == nil {
|
||||
db = db.Offset(o)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前表的所有字段
|
||||
table := template.TableName
|
||||
orderColumns, err := db.Migrator().ColumnTypes(table)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// 创建一个 map 来存储字段名
|
||||
fields := make(map[string]bool)
|
||||
|
||||
for _, column := range orderColumns {
|
||||
fields[column.Name()] = true
|
||||
}
|
||||
|
||||
// 通过参数传入order
|
||||
order := paramsValues.Get("order")
|
||||
|
||||
if order == "" && template.Order != "" {
|
||||
// 如果没有order入参,这里会使用模板的默认排序
|
||||
order = template.Order
|
||||
}
|
||||
|
||||
if order != "" {
|
||||
checkOrderArr := strings.Split(order, " ")
|
||||
orderStr := ""
|
||||
// 检查请求的排序字段是否在字段列表中
|
||||
if _, ok := fields[checkOrderArr[0]]; !ok {
|
||||
return nil, "", fmt.Errorf("order by %s is not in the fields", order)
|
||||
}
|
||||
orderStr = checkOrderArr[0]
|
||||
if len(checkOrderArr) > 1 {
|
||||
if checkOrderArr[1] != "asc" && checkOrderArr[1] != "desc" {
|
||||
return nil, "", fmt.Errorf("order by %s is not secure", order)
|
||||
}
|
||||
orderStr = orderStr + " " + checkOrderArr[1]
|
||||
}
|
||||
db = db.Order(orderStr)
|
||||
}
|
||||
|
||||
err = db.Debug().Find(&tableMap).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
rows = append(rows, tableTitle)
|
||||
for _, exTable := range tableMap {
|
||||
var row []string
|
||||
for _, column := range columns {
|
||||
column = strings.ReplaceAll(column, "\"", "")
|
||||
column = strings.ReplaceAll(column, "`", "")
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
columnAs := strings.Split(column, " as ")
|
||||
if len(columnAs) > 1 {
|
||||
column = strings.TrimSpace(strings.Split(column, " as ")[1])
|
||||
} else {
|
||||
columnArr := strings.Split(column, ".")
|
||||
if len(columnArr) > 1 {
|
||||
column = strings.Split(column, ".")[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
// 需要对时间类型特殊处理
|
||||
if t, ok := exTable[column].(time.Time); ok {
|
||||
row = append(row, t.Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
row = append(row, fmt.Sprintf("%v", exTable[column]))
|
||||
}
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
for i, row := range rows {
|
||||
for j, colCell := range row {
|
||||
cell := fmt.Sprintf("%s%d", getColumnName(j+1), i+1)
|
||||
|
||||
var sErr error
|
||||
if v, err := strconv.ParseFloat(colCell, 64); err == nil {
|
||||
sErr = f.SetCellValue("Sheet1", cell, v)
|
||||
} else if v, err := strconv.ParseInt(colCell, 10, 64); err == nil {
|
||||
sErr = f.SetCellValue("Sheet1", cell, v)
|
||||
} else {
|
||||
sErr = f.SetCellValue("Sheet1", cell, colCell)
|
||||
}
|
||||
|
||||
if sErr != nil {
|
||||
return nil, "", sErr
|
||||
}
|
||||
}
|
||||
}
|
||||
f.SetActiveSheet(index)
|
||||
file, err = f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return file, template.Name, nil
|
||||
}
|
||||
|
||||
// PreviewSQL 预览最终生成的 SQL(不执行查询,仅返回 SQL 字符串)
|
||||
// Author [piexlmax](https://github.com/piexlmax) & [trae-ai]
|
||||
func (sysExportTemplateService *SysExportTemplateService) PreviewSQL(templateID string, values url.Values) (sqlPreview string, err error) {
|
||||
// 解析 params(与导出逻辑保持一致)
|
||||
var params = values.Get("params")
|
||||
paramsValues, _ := url.ParseQuery(params)
|
||||
|
||||
// 加载模板
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 解析模板列
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var selectKeyFmt []string
|
||||
for _, key := range columns {
|
||||
selectKeyFmt = append(selectKeyFmt, key)
|
||||
}
|
||||
selects := strings.Join(selectKeyFmt, ", ")
|
||||
|
||||
// 生成 FROM 与 JOIN 片段
|
||||
var sb strings.Builder
|
||||
sb.WriteString("SELECT ")
|
||||
sb.WriteString(selects)
|
||||
sb.WriteString(" FROM ")
|
||||
sb.WriteString(template.TableName)
|
||||
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.JOINS)
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.Table)
|
||||
sb.WriteString(" ON ")
|
||||
sb.WriteString(join.ON)
|
||||
}
|
||||
}
|
||||
|
||||
// WHERE 条件
|
||||
var wheres []string
|
||||
|
||||
// 软删除过滤
|
||||
filterDeleted := false
|
||||
if paramsValues != nil {
|
||||
filterParam := paramsValues.Get("filterDeleted")
|
||||
if filterParam == "true" {
|
||||
filterDeleted = true
|
||||
}
|
||||
}
|
||||
if filterDeleted {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
if sysExportTemplateService.hasDeletedAtColumn(join.Table) {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模板条件(保留与 ExportExcel 同步的解析规则)
|
||||
if len(template.Conditions) > 0 {
|
||||
for _, condition := range template.Conditions {
|
||||
op := strings.ToUpper(strings.TrimSpace(condition.Operator))
|
||||
col := strings.TrimSpace(condition.Column)
|
||||
|
||||
// 预览优先展示传入值,没有则展示占位符
|
||||
val := ""
|
||||
if paramsValues != nil {
|
||||
val = paramsValues.Get(condition.From)
|
||||
}
|
||||
|
||||
switch op {
|
||||
case "BETWEEN":
|
||||
startValue := ""
|
||||
endValue := ""
|
||||
if paramsValues != nil {
|
||||
startValue = paramsValues.Get("start" + condition.From)
|
||||
endValue = paramsValues.Get("end" + condition.From)
|
||||
}
|
||||
if startValue != "" && endValue != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s BETWEEN '%s' AND '%s'", col, startValue, endValue))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s BETWEEN {start%s} AND {end%s}", col, condition.From, condition.From))
|
||||
}
|
||||
case "IN", "NOT IN":
|
||||
if val != "" {
|
||||
// 逗号分隔值做简单展示
|
||||
parts := strings.Split(val, ",")
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimSpace(parts[i])
|
||||
}
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s ('%s')", col, op, strings.Join(parts, "','")))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s ({%s})", col, op, condition.From))
|
||||
}
|
||||
case "LIKE":
|
||||
if val != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s LIKE '%%%s%%'", col, val))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s LIKE {%%%s%%}", col, condition.From))
|
||||
}
|
||||
default:
|
||||
if val != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s '%s'", col, op, val))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s {%s}", col, op, condition.From))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(wheres) > 0 {
|
||||
sb.WriteString(" WHERE ")
|
||||
sb.WriteString(strings.Join(wheres, " AND "))
|
||||
}
|
||||
|
||||
// 排序
|
||||
order := ""
|
||||
if paramsValues != nil {
|
||||
order = paramsValues.Get("order")
|
||||
}
|
||||
if order == "" && template.Order != "" {
|
||||
order = template.Order
|
||||
}
|
||||
if order != "" {
|
||||
sb.WriteString(" ORDER BY ")
|
||||
sb.WriteString(order)
|
||||
}
|
||||
|
||||
// limit/offset(如果传入或默认值为0,则不生成)
|
||||
limitStr := ""
|
||||
offsetStr := ""
|
||||
if paramsValues != nil {
|
||||
limitStr = paramsValues.Get("limit")
|
||||
offsetStr = paramsValues.Get("offset")
|
||||
}
|
||||
|
||||
// 处理模板默认limit(仅当非0时)
|
||||
if limitStr == "" && template.Limit != nil && *template.Limit != 0 {
|
||||
limitStr = strconv.Itoa(*template.Limit)
|
||||
}
|
||||
|
||||
// 解析为数值,用于判断是否生成
|
||||
limitInt := 0
|
||||
offsetInt := 0
|
||||
if limitStr != "" {
|
||||
if v, e := strconv.Atoi(limitStr); e == nil {
|
||||
limitInt = v
|
||||
}
|
||||
}
|
||||
if offsetStr != "" {
|
||||
if v, e := strconv.Atoi(offsetStr); e == nil {
|
||||
offsetInt = v
|
||||
}
|
||||
}
|
||||
|
||||
if limitInt > 0 {
|
||||
sb.WriteString(" LIMIT ")
|
||||
sb.WriteString(strconv.Itoa(limitInt))
|
||||
if offsetInt > 0 {
|
||||
sb.WriteString(" OFFSET ")
|
||||
sb.WriteString(strconv.Itoa(offsetInt))
|
||||
}
|
||||
} else {
|
||||
// 当limit未设置或为0时,仅当offset>0才生成OFFSET
|
||||
if offsetInt > 0 {
|
||||
sb.WriteString(" OFFSET ")
|
||||
sb.WriteString(strconv.Itoa(offsetInt))
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
// ExportTemplate 导出Excel模板
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templateID string) (file *bytes.Buffer, name string, err error) {
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
f := excelize.NewFile()
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
// Create a new sheet.
|
||||
index, err := f.NewSheet("Sheet1")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
var templateInfoMap = make(map[string]string)
|
||||
|
||||
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
||||
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var tableTitle []string
|
||||
for _, key := range columns {
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
for i := range tableTitle {
|
||||
fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i])
|
||||
if fErr != nil {
|
||||
return nil, "", fErr
|
||||
}
|
||||
}
|
||||
f.SetActiveSheet(index)
|
||||
file, err = f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return file, template.Name, nil
|
||||
}
|
||||
|
||||
// 辅助函数:检查表是否有deleted_at列
|
||||
func (s *SysExportTemplateService) hasDeletedAtColumn(tableName string) bool {
|
||||
var count int64
|
||||
global.GVA_DB.Raw("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = 'deleted_at'", tableName).Count(&count)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// ImportExcel 导入Excel
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) {
|
||||
var template system.SysExportTemplate
|
||||
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
f, err := excelize.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := f.GetRows("Sheet1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
return errors.New("Excel data is not enough.\nIt should contain title row and data")
|
||||
}
|
||||
|
||||
var templateInfoMap = make(map[string]string)
|
||||
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := global.GVA_DB
|
||||
if template.DBName != "" {
|
||||
db = global.MustGetGlobalDBByDBName(template.DBName)
|
||||
}
|
||||
|
||||
items, err := sysExportTemplateService.parseExcelToMap(rows, templateInfoMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if template.ImportSQL != "" {
|
||||
return sysExportTemplateService.importBySQL(tx, template.ImportSQL, items)
|
||||
}
|
||||
return sysExportTemplateService.importByGORM(tx, template.TableName, items)
|
||||
})
|
||||
}
|
||||
|
||||
func (sysExportTemplateService *SysExportTemplateService) parseExcelToMap(rows [][]string, templateInfoMap map[string]string) ([]map[string]interface{}, error) {
|
||||
var titleKeyMap = make(map[string]string)
|
||||
for key, title := range templateInfoMap {
|
||||
titleKeyMap[title] = key
|
||||
}
|
||||
|
||||
excelTitle := rows[0]
|
||||
for i, str := range excelTitle {
|
||||
excelTitle[i] = strings.TrimSpace(str)
|
||||
}
|
||||
values := rows[1:]
|
||||
items := make([]map[string]interface{}, 0, len(values))
|
||||
for _, row := range values {
|
||||
var item = make(map[string]interface{})
|
||||
for ii, value := range row {
|
||||
if ii >= len(excelTitle) {
|
||||
continue
|
||||
}
|
||||
if _, ok := titleKeyMap[excelTitle[ii]]; !ok {
|
||||
continue // excel中多余的标题,在模板信息中没有对应的字段,因此key为空,必须跳过
|
||||
}
|
||||
key := titleKeyMap[excelTitle[ii]]
|
||||
item[key] = value
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (sysExportTemplateService *SysExportTemplateService) importBySQL(tx *gorm.DB, sql string, items []map[string]interface{}) error {
|
||||
for _, item := range items {
|
||||
if err := tx.Exec(sql, item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sysExportTemplateService *SysExportTemplateService) importByGORM(tx *gorm.DB, tableName string, items []map[string]interface{}) error {
|
||||
needCreated := tx.Migrator().HasColumn(tableName, "created_at")
|
||||
needUpdated := tx.Migrator().HasColumn(tableName, "updated_at")
|
||||
|
||||
for _, item := range items {
|
||||
if item["created_at"] == nil && needCreated {
|
||||
item["created_at"] = time.Now()
|
||||
}
|
||||
if item["updated_at"] == nil && needUpdated {
|
||||
item["updated_at"] = time.Now()
|
||||
}
|
||||
}
|
||||
return tx.Table(tableName).CreateInBatches(&items, 1000).Error
|
||||
}
|
||||
|
||||
func getColumnName(n int) string {
|
||||
columnName := ""
|
||||
for n > 0 {
|
||||
n--
|
||||
columnName = string(rune('A'+n%26)) + columnName
|
||||
n /= 26
|
||||
}
|
||||
return columnName
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/Go-Web-Template/server/config"
|
||||
"github.com/gookit/color"
|
||||
@@ -19,6 +20,10 @@ import (
|
||||
|
||||
type PgsqlInitHandler struct{}
|
||||
|
||||
func quotePgIdentifier(name string) string {
|
||||
return `"` + strings.ReplaceAll(name, `"`, `""`) + `"`
|
||||
}
|
||||
|
||||
func NewPgsqlInitHandler() *PgsqlInitHandler {
|
||||
return &PgsqlInitHandler{}
|
||||
}
|
||||
@@ -55,9 +60,13 @@ func (h PgsqlInitHandler) EnsureDB(ctx context.Context, conf *request.InitDB) (n
|
||||
dsn := conf.PgsqlEmptyDsn()
|
||||
var createSql string
|
||||
if conf.Template != "" {
|
||||
createSql = fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s;", c.Dbname, conf.Template)
|
||||
createSql = fmt.Sprintf(
|
||||
"CREATE DATABASE %s WITH TEMPLATE %s;",
|
||||
quotePgIdentifier(c.Dbname),
|
||||
quotePgIdentifier(conf.Template),
|
||||
)
|
||||
} else {
|
||||
createSql = fmt.Sprintf("CREATE DATABASE %s;", c.Dbname)
|
||||
createSql = fmt.Sprintf("CREATE DATABASE %s;", quotePgIdentifier(c.Dbname))
|
||||
}
|
||||
if err = createDatabase(dsn, "pgx", createSql); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
systemReq "git.echol.cn/loser/Go-Web-Template/server/model/system/request"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SysVersionService struct{}
|
||||
|
||||
// CreateSysVersion 创建版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) CreateSysVersion(ctx context.Context, sysVersion *system.SysVersion) (err error) {
|
||||
err = global.GVA_DB.Create(sysVersion).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysVersion 删除版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) DeleteSysVersion(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.Delete(&system.SysVersion{}, "id = ?", ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSysVersionByIds 批量删除版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) DeleteSysVersionByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.Where("id in ?", IDs).Delete(&system.SysVersion{}).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSysVersion 根据ID获取版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) GetSysVersion(ctx context.Context, ID string) (sysVersion system.SysVersion, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", ID).First(&sysVersion).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetSysVersionInfoList 分页获取版本管理记录
|
||||
// Author [yourname](https://github.com/yourname)
|
||||
func (sysVersionService *SysVersionService) GetSysVersionInfoList(ctx context.Context, info systemReq.SysVersionSearch) (list []system.SysVersion, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&system.SysVersion{})
|
||||
var sysVersions []system.SysVersion
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if len(info.CreatedAtRange) == 2 {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.CreatedAtRange[0], info.CreatedAtRange[1])
|
||||
}
|
||||
|
||||
if info.VersionName != nil && *info.VersionName != "" {
|
||||
db = db.Where("version_name LIKE ?", "%"+*info.VersionName+"%")
|
||||
}
|
||||
if info.VersionCode != nil && *info.VersionCode != "" {
|
||||
db = db.Where("version_code = ?", *info.VersionCode)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Find(&sysVersions).Error
|
||||
return sysVersions, total, err
|
||||
}
|
||||
func (sysVersionService *SysVersionService) GetSysVersionPublic(ctx context.Context) {
|
||||
// 此方法为获取数据源定义的数据
|
||||
// 请自行实现
|
||||
}
|
||||
|
||||
// GetMenusByIds 根据ID列表获取菜单数据
|
||||
func (sysVersionService *SysVersionService) GetMenusByIds(ctx context.Context, ids []uint) (menus []system.SysBaseMenu, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Preload("Parameters").Preload("MenuBtn").Find(&menus).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetApisByIds 根据ID列表获取API数据
|
||||
func (sysVersionService *SysVersionService) GetApisByIds(ctx context.Context, ids []uint) (apis []system.SysApi, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Find(&apis).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetDictionariesByIds 根据ID列表获取字典数据
|
||||
func (sysVersionService *SysVersionService) GetDictionariesByIds(ctx context.Context, ids []uint) (dictionaries []system.SysDictionary, err error) {
|
||||
err = global.GVA_DB.Where("id in ?", ids).Preload("SysDictionaryDetails").Find(&dictionaries).Error
|
||||
return
|
||||
}
|
||||
|
||||
// ImportMenus 导入菜单数据
|
||||
func (sysVersionService *SysVersionService) ImportMenus(ctx context.Context, menus []system.SysBaseMenu) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 递归创建菜单
|
||||
return sysVersionService.createMenusRecursively(tx, menus, 0)
|
||||
})
|
||||
}
|
||||
|
||||
// createMenusRecursively 递归创建菜单
|
||||
func (sysVersionService *SysVersionService) createMenusRecursively(tx *gorm.DB, menus []system.SysBaseMenu, parentId uint) error {
|
||||
for _, menu := range menus {
|
||||
// 检查菜单是否已存在
|
||||
var existingMenu system.SysBaseMenu
|
||||
if err := tx.Where("name = ? AND path = ?", menu.Name, menu.Path).First(&existingMenu).Error; err == nil {
|
||||
// 菜单已存在,使用现有菜单ID继续处理子菜单
|
||||
if len(menu.Children) > 0 {
|
||||
if err := sysVersionService.createMenusRecursively(tx, menu.Children, existingMenu.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 保存参数和按钮数据,稍后处理
|
||||
parameters := menu.Parameters
|
||||
menuBtns := menu.MenuBtn
|
||||
children := menu.Children
|
||||
|
||||
// 创建新菜单(不包含关联数据)
|
||||
newMenu := system.SysBaseMenu{
|
||||
ParentId: parentId,
|
||||
Path: menu.Path,
|
||||
Name: menu.Name,
|
||||
Hidden: menu.Hidden,
|
||||
Component: menu.Component,
|
||||
Sort: menu.Sort,
|
||||
Meta: menu.Meta,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newMenu).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建参数
|
||||
if len(parameters) > 0 {
|
||||
for _, param := range parameters {
|
||||
newParam := system.SysBaseMenuParameter{
|
||||
SysBaseMenuID: newMenu.ID,
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
}
|
||||
if err := tx.Create(&newParam).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建菜单按钮
|
||||
if len(menuBtns) > 0 {
|
||||
for _, btn := range menuBtns {
|
||||
newBtn := system.SysBaseMenuBtn{
|
||||
SysBaseMenuID: newMenu.ID,
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
}
|
||||
if err := tx.Create(&newBtn).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子菜单
|
||||
if len(children) > 0 {
|
||||
if err := sysVersionService.createMenusRecursively(tx, children, newMenu.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportApis 导入API数据
|
||||
func (sysVersionService *SysVersionService) ImportApis(apis []system.SysApi) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, api := range apis {
|
||||
// 检查API是否已存在
|
||||
var existingApi system.SysApi
|
||||
if err := tx.Where("path = ? AND method = ?", api.Path, api.Method).First(&existingApi).Error; err == nil {
|
||||
// API已存在,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建新API
|
||||
newApi := system.SysApi{
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newApi).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ImportDictionaries 导入字典数据
|
||||
func (sysVersionService *SysVersionService) ImportDictionaries(dictionaries []system.SysDictionary) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
for _, dict := range dictionaries {
|
||||
// 检查字典是否已存在
|
||||
var existingDict system.SysDictionary
|
||||
if err := tx.Where("type = ?", dict.Type).First(&existingDict).Error; err == nil {
|
||||
// 字典已存在,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建新字典
|
||||
newDict := system.SysDictionary{
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
SysDictionaryDetails: dict.SysDictionaryDetails,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newDict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -152,24 +152,13 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
|
||||
{ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/checkFileMd5", Description: "文件完整度验证"},
|
||||
{ApiGroup: "断点续传(插件版)", Method: "GET", Path: "/simpleUploader/mergeFileMd5", Description: "上传完成合并文件"},
|
||||
|
||||
{ApiGroup: "email", Method: "POST", Path: "/email/emailTest", Description: "发送测试邮件"},
|
||||
{ApiGroup: "email", Method: "POST", Path: "/email/sendEmail", Description: "发送邮件"},
|
||||
//{ApiGroup: "email", Method: "POST", Path: "/email/emailTest", Description: "发送测试邮件"},
|
||||
//{ApiGroup: "email", Method: "POST", Path: "/email/sendEmail", Description: "发送邮件"},
|
||||
|
||||
{ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/setAuthorityBtn", Description: "设置按钮权限"},
|
||||
{ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/getAuthorityBtn", Description: "获取已有按钮权限"},
|
||||
{ApiGroup: "按钮权限", Method: "POST", Path: "/authorityBtn/canRemoveAuthorityBtn", Description: "删除按钮"},
|
||||
|
||||
{ApiGroup: "导出模板", Method: "POST", Path: "/sysExportTemplate/createSysExportTemplate", Description: "新增导出模板"},
|
||||
{ApiGroup: "导出模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplate", Description: "删除导出模板"},
|
||||
{ApiGroup: "导出模板", Method: "DELETE", Path: "/sysExportTemplate/deleteSysExportTemplateByIds", Description: "批量删除导出模板"},
|
||||
{ApiGroup: "导出模板", Method: "PUT", Path: "/sysExportTemplate/updateSysExportTemplate", Description: "更新导出模板"},
|
||||
{ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/findSysExportTemplate", Description: "根据ID获取导出模板"},
|
||||
{ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/getSysExportTemplateList", Description: "获取导出模板列表"},
|
||||
{ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/exportExcel", Description: "导出Excel"},
|
||||
{ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/exportTemplate", Description: "下载模板"},
|
||||
{ApiGroup: "导出模板", Method: "GET", Path: "/sysExportTemplate/previewSQL", Description: "预览SQL"},
|
||||
{ApiGroup: "导出模板", Method: "POST", Path: "/sysExportTemplate/importExcel", Description: "导入Excel"},
|
||||
|
||||
{ApiGroup: "错误日志", Method: "POST", Path: "/sysError/createSysError", Description: "新建错误日志"},
|
||||
{ApiGroup: "错误日志", Method: "DELETE", Path: "/sysError/deleteSysError", Description: "删除错误日志"},
|
||||
{ApiGroup: "错误日志", Method: "DELETE", Path: "/sysError/deleteSysErrorByIds", Description: "批量删除错误日志"},
|
||||
@@ -188,14 +177,6 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
|
||||
{ApiGroup: "媒体库分类", Method: "GET", Path: "/attachmentCategory/getCategoryList", Description: "分类列表"},
|
||||
{ApiGroup: "媒体库分类", Method: "POST", Path: "/attachmentCategory/addCategory", Description: "添加/编辑分类"},
|
||||
{ApiGroup: "媒体库分类", Method: "POST", Path: "/attachmentCategory/deleteCategory", Description: "删除分类"},
|
||||
|
||||
{ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/findSysVersion", Description: "获取单一版本"},
|
||||
{ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/getSysVersionList", Description: "获取版本列表"},
|
||||
{ApiGroup: "版本控制", Method: "GET", Path: "/sysVersion/downloadVersionJson", Description: "下载版本json"},
|
||||
{ApiGroup: "版本控制", Method: "POST", Path: "/sysVersion/exportVersion", Description: "创建版本"},
|
||||
{ApiGroup: "版本控制", Method: "POST", Path: "/sysVersion/importVersion", Description: "同步版本"},
|
||||
{ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersion", Description: "删除版本"},
|
||||
{ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersionByIds", Description: "批量删除版本"},
|
||||
}
|
||||
if err := db.Create(&entities).Error; err != nil {
|
||||
return ctx, errors.Wrap(err, sysModel.SysApi{}.TableName()+"表数据初始化失败!")
|
||||
|
||||
@@ -161,17 +161,6 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error
|
||||
{Ptype: "p", V0: "888", V1: "/authorityBtn/getAuthorityBtn", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/authorityBtn/canRemoveAuthorityBtn", V2: "POST"},
|
||||
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/createSysExportTemplate", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplate", V2: "DELETE"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/deleteSysExportTemplateByIds", V2: "DELETE"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/updateSysExportTemplate", V2: "PUT"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/findSysExportTemplate", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/getSysExportTemplateList", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportExcel", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/exportTemplate", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/previewSQL", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysExportTemplate/importExcel", V2: "POST"},
|
||||
|
||||
{Ptype: "p", V0: "888", V1: "/sysError/createSysError", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysError/deleteSysError", V2: "DELETE"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysError/deleteSysErrorByIds", V2: "DELETE"},
|
||||
@@ -191,14 +180,6 @@ func (i *initCasbin) InitializeData(ctx context.Context) (context.Context, error
|
||||
{Ptype: "p", V0: "888", V1: "/attachmentCategory/addCategory", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/attachmentCategory/deleteCategory", V2: "POST"},
|
||||
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/findSysVersion", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/getSysVersionList", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/downloadVersionJson", V2: "GET"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/exportVersion", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/importVersion", V2: "POST"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/deleteSysVersion", V2: "DELETE"},
|
||||
{Ptype: "p", V0: "888", V1: "/sysVersion/deleteSysVersionByIds", V2: "DELETE"},
|
||||
|
||||
{Ptype: "p", V0: "8881", V1: "/user/admin_register", V2: "POST"},
|
||||
{Ptype: "p", V0: "8881", V1: "/api/createApi", V2: "POST"},
|
||||
{Ptype: "p", V0: "8881", V1: "/api/getApiList", V2: "POST"},
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
sysModel "git.echol.cn/loser/Go-Web-Template/server/model/system"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/service/system"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type initExcelTemplate struct{}
|
||||
|
||||
const initOrderExcelTemplate = initOrderDictDetail + 1
|
||||
|
||||
// auto run
|
||||
func init() {
|
||||
system.RegisterInit(initOrderExcelTemplate, &initExcelTemplate{})
|
||||
}
|
||||
|
||||
func (i *initExcelTemplate) InitializerName() string {
|
||||
return "sys_export_templates"
|
||||
}
|
||||
|
||||
func (i *initExcelTemplate) MigrateTable(ctx context.Context) (context.Context, error) {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return ctx, system.ErrMissingDBContext
|
||||
}
|
||||
return ctx, db.AutoMigrate(&sysModel.SysExportTemplate{})
|
||||
}
|
||||
|
||||
func (i *initExcelTemplate) TableCreated(ctx context.Context) bool {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return db.Migrator().HasTable(&sysModel.SysExportTemplate{})
|
||||
}
|
||||
|
||||
func (i *initExcelTemplate) InitializeData(ctx context.Context) (context.Context, error) {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return ctx, system.ErrMissingDBContext
|
||||
}
|
||||
|
||||
entities := []sysModel.SysExportTemplate{
|
||||
{
|
||||
Name: "api",
|
||||
TableName: "sys_apis",
|
||||
TemplateID: "api",
|
||||
TemplateInfo: `{
|
||||
"path":"路径",
|
||||
"method":"方法(大写)",
|
||||
"description":"方法介绍",
|
||||
"api_group":"方法分组"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
if err := db.Create(&entities).Error; err != nil {
|
||||
return ctx, errors.Wrap(err, "sys_export_templates"+"表数据初始化失败!")
|
||||
}
|
||||
next := context.WithValue(ctx, i.InitializerName(), entities)
|
||||
return next, nil
|
||||
}
|
||||
|
||||
func (i *initExcelTemplate) DataInserted(ctx context.Context) bool {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if errors.Is(db.First(&sysModel.SysExportTemplate{}).Error, gorm.ErrRecordNotFound) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -53,14 +53,14 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
|
||||
|
||||
// 定义所有菜单
|
||||
allMenus := []SysBaseMenu{
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}},
|
||||
{MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "common", Name: "common", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "公共能力", Icon: "folder-opened"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "编程辅助", Icon: "tools"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "features/dashboard/DashboardPage", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "DashboardOutlined"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "features/discovery/ModuleLandingPage:about", Sort: 9, Meta: Meta{Title: "关于系统", Icon: "InfoCircleOutlined"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "features/discovery/ModuleLandingPage:superAdmin", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "LockOutlined"}},
|
||||
{MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "features/person/ProfilePage", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "UserOutlined"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "common", Name: "common", Component: "features/discovery/ModuleLandingPage:common", Sort: 6, Meta: Meta{Title: "公共能力", Icon: "AppstoreOutlined"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "features/discovery/ModuleLandingPage:systemTools", Sort: 5, Meta: Meta{Title: "编程辅助", Icon: "ToolOutlined"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://echol.cn", Name: "https://echol.cn", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "features/server/ServerStatePage", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "CloudServerOutlined"}},
|
||||
}
|
||||
|
||||
// 先创建父级菜单(ParentId = 0 的菜单)
|
||||
@@ -77,28 +77,25 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
|
||||
// 定义子菜单,并设置正确的ParentId
|
||||
childMenus := []SysBaseMenu{
|
||||
// superAdmin子菜单
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysParams", Name: "sysParams", Component: "view/superAdmin/params/sysParams.vue", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "compass"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 8, Meta: Meta{Title: "系统配置", Icon: "operation"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "apiToken", Name: "apiToken", Component: "view/systemTools/apiToken/index.vue", Sort: 9, Meta: Meta{Title: "API Token", Icon: "key"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "loginLog", Name: "loginLog", Component: "view/systemTools/loginLog/index.vue", Sort: 10, Meta: Meta{Title: "登录日志", Icon: "monitor"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysVersion", Name: "sysVersion", Component: "view/systemTools/version/version.vue", Sort: 11, Meta: Meta{Title: "版本管理", Icon: "server"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysError", Name: "sysError", Component: "view/systemTools/sysError/sysError.vue", Sort: 12, Meta: Meta{Title: "错误日志", Icon: "warn"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "authority", Name: "authority", Component: "features/roles/RoleManagementPage", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "TeamOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "menu", Name: "menu", Component: "features/menus/MenuManagementPage", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "AppstoreOutlined", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "api", Name: "api", Component: "features/apis/ApiManagementPage", Sort: 3, Meta: Meta{Title: "API管理", Icon: "ApiOutlined", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "user", Name: "user", Component: "features/users/UserManagementPage", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "UserOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "dictionary", Name: "dictionary", Component: "features/dictionaries/DictionaryManagementPage", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "BookOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "operation", Name: "operation", Component: "features/logs/OperationLogPage", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "ProfileOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysParams", Name: "sysParams", Component: "features/params/ParamsManagementPage", Sort: 7, Meta: Meta{Title: "参数管理", Icon: "SettingOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "system", Name: "system", Component: "features/system/SystemConfigPage", Sort: 8, Meta: Meta{Title: "系统配置", Icon: "SettingOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "apiToken", Name: "apiToken", Component: "features/tokens/ApiTokenPage", Sort: 9, Meta: Meta{Title: "API Token", Icon: "LockOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "loginLog", Name: "loginLog", Component: "features/logs/LoginLogPage", Sort: 10, Meta: Meta{Title: "登录日志", Icon: "FileTextOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["superAdmin"], Path: "sysError", Name: "sysError", Component: "features/errors/ErrorLogPage", Sort: 11, Meta: Meta{Title: "错误日志", Icon: "BugOutlined"}},
|
||||
|
||||
// common子菜单
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 1, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 2, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "upload", Name: "upload", Component: "features/media/MediaLibraryPage", Sort: 1, Meta: Meta{Title: "媒体库(上传下载)", Icon: "UploadOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["common"], Path: "breakpoint", Name: "breakpoint", Component: "features/breakpoint/BreakpointPage", Sort: 2, Meta: Meta{Title: "断点续传", Icon: "DeploymentUnitOutlined"}},
|
||||
|
||||
// systemTools子菜单
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 1, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 2, Meta: Meta{Title: "导出模板", Icon: "reading"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "view/systemTools/mcpTest/index.vue", Sort: 3, Meta: Meta{Title: "MCP Tools管理", Icon: "partly-cloudy"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "view/systemTools/mcpTool/index.vue", Sort: 4, Meta: Meta{Title: "MCP Tools模板", Icon: "magnet"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTest", Name: "mcpTest", Component: "features/mcp/McpTestPage", Sort: 1, Meta: Meta{Title: "MCP Tools管理", Icon: "ToolOutlined"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["systemTools"], Path: "mcpTool", Name: "mcpTool", Component: "features/mcp/McpToolPage", Sort: 2, Meta: Meta{Title: "MCP Tools模板", Icon: "ToolOutlined"}},
|
||||
}
|
||||
|
||||
// 创建子菜单
|
||||
|
||||
Reference in New Issue
Block a user