🎨 优化项目结构 && 完善ai配置
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -29,4 +29,5 @@ uploads
|
||||
.idea
|
||||
.claude
|
||||
log
|
||||
node_modules
|
||||
node_modules
|
||||
st
|
||||
@@ -1,11 +1,9 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app/request"
|
||||
commonRequest "git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
@@ -17,216 +15,163 @@ type AiPresetApi struct{}
|
||||
|
||||
var aiPresetService = service.ServiceGroupApp.AppServiceGroup.AiPresetService
|
||||
|
||||
// CreateAiPreset 创建AI预设
|
||||
// CreateAiPreset 创建预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 创建AI预设
|
||||
// @Summary 创建预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateAiPresetRequest true "预设信息"
|
||||
// @Success 200 {object} response.Response{data=app.AiPreset,msg=string} "创建成功"
|
||||
// @Router /app/preset [post]
|
||||
// @Param data body app.AiPreset true "预设信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /aiPreset/createAiPreset [post]
|
||||
func (a *AiPresetApi) CreateAiPreset(c *gin.Context) {
|
||||
var req request.CreateAiPresetRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var preset app.AiPreset
|
||||
err := c.ShouldBindJSON(&preset)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
preset.UserID = utils.GetUserID(c)
|
||||
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if id := utils.GetUserID(c); id > 0 {
|
||||
userId = id
|
||||
if err := aiPresetService.CreateAiPreset(&preset); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
preset, err := aiPresetService.CreateAiPreset(userId, &req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建预设失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(preset, c)
|
||||
}
|
||||
|
||||
// DeleteAiPreset 删除AI预设
|
||||
// DeleteAiPreset 删除预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 删除AI预设
|
||||
// @Summary 删除预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "预设ID"
|
||||
// @Param data body request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /app/preset/:id [delete]
|
||||
// @Router /aiPreset/deleteAiPreset [delete]
|
||||
func (a *AiPresetApi) DeleteAiPreset(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if uid := utils.GetUserID(c); uid > 0 {
|
||||
userId = uid
|
||||
}
|
||||
|
||||
err := aiPresetService.DeleteAiPreset(uint(id), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除预设失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateAiPreset 更新AI预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 更新AI预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.UpdateAiPresetRequest true "预设信息"
|
||||
// @Success 200 {object} response.Response{data=app.AiPreset,msg=string} "更新成功"
|
||||
// @Router /app/preset [put]
|
||||
func (a *AiPresetApi) UpdateAiPreset(c *gin.Context) {
|
||||
var req request.UpdateAiPresetRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindJSON(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if id := utils.GetUserID(c); id > 0 {
|
||||
userId = id
|
||||
if err := aiPresetService.DeleteAiPreset(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
preset, err := aiPresetService.UpdateAiPreset(userId, &req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新预设失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(preset, c)
|
||||
}
|
||||
|
||||
// GetAiPreset 获取AI预设详情
|
||||
// UpdateAiPreset 更新预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 获取AI预设详情
|
||||
// @Summary 更新预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "预设ID"
|
||||
// @Success 200 {object} response.Response{data=app.AiPreset,msg=string} "获取成功"
|
||||
// @Router /app/preset/:id [get]
|
||||
func (a *AiPresetApi) GetAiPreset(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if uid := utils.GetUserID(c); uid > 0 {
|
||||
userId = uid
|
||||
}
|
||||
|
||||
preset, err := aiPresetService.GetAiPreset(uint(id), userId)
|
||||
// @Param data body app.AiPreset true "预设信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /aiPreset/updateAiPreset [put]
|
||||
func (a *AiPresetApi) UpdateAiPreset(c *gin.Context) {
|
||||
var preset app.AiPreset
|
||||
err := c.ShouldBindJSON(&preset)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取预设失败", c)
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
response.OkWithData(preset, c)
|
||||
if err := aiPresetService.UpdateAiPreset(&preset, userID); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAiPresetList 获取AI预设列表
|
||||
// FindAiPreset 查询预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 获取AI预设列表
|
||||
// @Summary 查询预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "分页信息"
|
||||
// @Param data query request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{data=app.AiPreset,msg=string} "查询成功"
|
||||
// @Router /aiPreset/findAiPreset [get]
|
||||
func (a *AiPresetApi) FindAiPreset(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindQuery(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
if preset, err := aiPresetService.GetAiPreset(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithData(preset, c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAiPresetList 获取预设列表
|
||||
// @Tags AiPreset
|
||||
// @Summary 获取预设列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "分页参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /app/preset/list [get]
|
||||
// @Router /aiPreset/getAiPresetList [get]
|
||||
func (a *AiPresetApi) GetAiPresetList(c *gin.Context) {
|
||||
var pageInfo commonRequest.PageInfo
|
||||
var pageInfo request.PageInfo
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if id := utils.GetUserID(c); id > 0 {
|
||||
userId = id
|
||||
if list, total, err := aiPresetService.GetAiPresetList(pageInfo, userID); 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)
|
||||
}
|
||||
list, total, err := aiPresetService.GetAiPresetList(userId, pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取预设列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取预设列表失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// ImportAiPreset 导入AI预设(支持SillyTavern格式)
|
||||
// ImportAiPreset 导入预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 导入AI预设
|
||||
// @Summary 导入预设(支持SillyTavern格式)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ImportAiPresetRequest true "导入数据"
|
||||
// @Success 200 {object} response.Response{data=app.AiPreset,msg=string} "导入成功"
|
||||
// @Router /app/preset/import [post]
|
||||
// @Param data body app.AiPreset true "预设JSON"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入成功"
|
||||
// @Router /aiPreset/importAiPreset [post]
|
||||
func (a *AiPresetApi) ImportAiPreset(c *gin.Context) {
|
||||
var req request.ImportAiPresetRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var preset app.AiPreset
|
||||
err := c.ShouldBindJSON(&preset)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
preset.UserID = utils.GetUserID(c)
|
||||
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if id := utils.GetUserID(c); id > 0 {
|
||||
userId = id
|
||||
if err := aiPresetService.CreateAiPreset(&preset); err != nil {
|
||||
global.GVA_LOG.Error("导入失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
preset, err := aiPresetService.ImportAiPreset(userId, &req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("导入预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入预设失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(preset, c)
|
||||
}
|
||||
|
||||
// ExportAiPreset 导出AI预设
|
||||
// @Tags AiPreset
|
||||
// @Summary 导出AI预设
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "预设ID"
|
||||
// @Success 200 {object} map[string]interface{} "导出数据"
|
||||
// @Router /app/preset/:id/export [get]
|
||||
func (a *AiPresetApi) ExportAiPreset(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
// 尝试获取用户ID,如果没有则使用0(公开访问)
|
||||
userId := uint(0)
|
||||
if uid := utils.GetUserID(c); uid > 0 {
|
||||
userId = uid
|
||||
}
|
||||
|
||||
data, err := aiPresetService.ExportAiPreset(uint(id), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("导出预设失败!", zap.Error(err))
|
||||
response.FailWithMessage("导出预设失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, data)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package app
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
@@ -10,122 +11,141 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type PresetBindingApi struct{}
|
||||
type AiPresetBindingApi struct{}
|
||||
|
||||
var presetBindingService = service.ServiceGroupApp.AppServiceGroup.PresetBindingService
|
||||
var aiPresetBindingService = service.ServiceGroupApp.AppServiceGroup.AiPresetBindingService
|
||||
|
||||
// CreateBinding 创建预设绑定
|
||||
// @Tags App
|
||||
// @Summary 创建预设绑定
|
||||
// CreateAiPresetBinding 创建绑定
|
||||
// @Tags AiPresetBinding
|
||||
// @Summary 创建绑定
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateBindingRequest true "绑定信息"
|
||||
// @Param data body app.AiPresetBinding true "绑定信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /app/binding [post]
|
||||
func (a *PresetBindingApi) CreateBinding(c *gin.Context) {
|
||||
var req request.CreateBindingRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err := presetBindingService.CreateBinding(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建绑定失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateBinding 更新预设绑定
|
||||
// @Tags App
|
||||
// @Summary 更新预设绑定
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.UpdateBindingRequest true "绑定信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /app/binding [put]
|
||||
func (a *PresetBindingApi) UpdateBinding(c *gin.Context) {
|
||||
var req request.UpdateBindingRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err := presetBindingService.UpdateBinding(&req)
|
||||
// @Router /aiPresetBinding/createAiPresetBinding [post]
|
||||
func (a *AiPresetBindingApi) CreateAiPresetBinding(c *gin.Context) {
|
||||
var binding app.AiPresetBinding
|
||||
err := c.ShouldBindJSON(&binding)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
binding.UserID = utils.GetUserID(c)
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
if err := aiPresetBindingService.CreateAiPresetBinding(&binding); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBinding 删除预设绑定
|
||||
// @Tags App
|
||||
// @Summary 删除预设绑定
|
||||
// DeleteAiPresetBinding 删除绑定
|
||||
// @Tags AiPresetBinding
|
||||
// @Summary 删除绑定
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "绑定ID"
|
||||
// @Param data body request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /app/binding/:id [delete]
|
||||
func (a *PresetBindingApi) DeleteBinding(c *gin.Context) {
|
||||
id, err := utils.StringToUint(c.Param("id"))
|
||||
if err != nil {
|
||||
response.FailWithMessage("无效的ID", c)
|
||||
return
|
||||
}
|
||||
|
||||
err = presetBindingService.DeleteBinding(id)
|
||||
// @Router /aiPresetBinding/deleteAiPresetBinding [delete]
|
||||
func (a *AiPresetBindingApi) DeleteAiPresetBinding(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindJSON(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
if err := aiPresetBindingService.DeleteAiPresetBinding(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetBindingList 获取绑定列表
|
||||
// @Tags App
|
||||
// UpdateAiPresetBinding 更新绑定
|
||||
// @Tags AiPresetBinding
|
||||
// @Summary 更新绑定
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body app.AiPresetBinding true "绑定信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /aiPresetBinding/updateAiPresetBinding [put]
|
||||
func (a *AiPresetBindingApi) UpdateAiPresetBinding(c *gin.Context) {
|
||||
var binding app.AiPresetBinding
|
||||
err := c.ShouldBindJSON(&binding)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
if err := aiPresetBindingService.UpdateAiPresetBinding(&binding, userID); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// FindAiPresetBinding 查询绑定
|
||||
// @Tags AiPresetBinding
|
||||
// @Summary 查询绑定
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{data=app.AiPresetBinding,msg=string} "查询成功"
|
||||
// @Router /aiPresetBinding/findAiPresetBinding [get]
|
||||
func (a *AiPresetBindingApi) FindAiPresetBinding(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindQuery(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
if binding, err := aiPresetBindingService.GetAiPresetBinding(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithData(binding, c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAiPresetBindingList 获取绑定列表
|
||||
// @Tags AiPresetBinding
|
||||
// @Summary 获取绑定列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param providerId query uint false "提供商ID"
|
||||
// @Param presetId query uint false "预设ID"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /app/binding/list [get]
|
||||
func (a *PresetBindingApi) GetBindingList(c *gin.Context) {
|
||||
var req request.GetBindingListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 10
|
||||
}
|
||||
|
||||
list, total, err := presetBindingService.GetBindingList(&req)
|
||||
// @Param data query request.PageInfo true "分页参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /aiPresetBinding/getAiPresetBindingList [get]
|
||||
func (a *AiPresetBindingApi) GetAiPresetBindingList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
}, "获取成功", c)
|
||||
if list, total, err := aiPresetBindingService.GetAiPresetBindingList(pageInfo, userID); 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -15,171 +15,137 @@ type AiProviderApi struct{}
|
||||
|
||||
var aiProviderService = service.ServiceGroupApp.AppServiceGroup.AiProviderService
|
||||
|
||||
// CreateAiProvider 创建AI提供商
|
||||
// CreateAiProvider 创建提供商
|
||||
// @Tags AiProvider
|
||||
// @Summary 创建AI提供商
|
||||
// @Summary 创建提供商
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateAiProviderRequest true "提供商信息"
|
||||
// @Success 200 {object} response.Response{data=app.AiProvider,msg=string} "创建成功"
|
||||
// @Router /app/provider [post]
|
||||
// @Param data body app.AiProvider true "提供商信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /aiProvider/createAiProvider [post]
|
||||
func (a *AiProviderApi) CreateAiProvider(c *gin.Context) {
|
||||
var req request.CreateAiProviderRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var provider app.AiProvider
|
||||
err := c.ShouldBindJSON(&provider)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
provider.UserID = utils.GetUserID(c)
|
||||
|
||||
provider, err := aiProviderService.CreateAiProvider(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建提供商失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建提供商失败", c)
|
||||
return
|
||||
if err := aiProviderService.CreateAiProvider(&provider); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
response.OkWithData(provider, c)
|
||||
}
|
||||
|
||||
// DeleteAiProvider 删除AI提供商
|
||||
// DeleteAiProvider 删除提供商
|
||||
// @Tags AiProvider
|
||||
// @Summary 删除AI提供商
|
||||
// @Summary 删除提供商
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "提供商ID"
|
||||
// @Param data body request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /app/provider/:id [delete]
|
||||
// @Router /aiProvider/deleteAiProvider [delete]
|
||||
func (a *AiProviderApi) DeleteAiProvider(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
|
||||
err := aiProviderService.DeleteAiProvider(uint(id))
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindJSON(&reqId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除提供商失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除提供商失败", c)
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
if err := aiProviderService.DeleteAiProvider(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAiProvider 更新AI提供商
|
||||
// UpdateAiProvider 更新提供商
|
||||
// @Tags AiProvider
|
||||
// @Summary 更新AI提供商
|
||||
// @Summary 更新提供商
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.UpdateAiProviderRequest true "提供商信息"
|
||||
// @Success 200 {object} response.Response{data=app.AiProvider,msg=string} "更新成功"
|
||||
// @Router /app/provider [put]
|
||||
// @Param data body app.AiProvider true "提供商信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /aiProvider/updateAiProvider [put]
|
||||
func (a *AiProviderApi) UpdateAiProvider(c *gin.Context) {
|
||||
var req request.UpdateAiProviderRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var provider app.AiProvider
|
||||
err := c.ShouldBindJSON(&provider)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
provider, err := aiProviderService.UpdateAiProvider(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新提供商失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新提供商失败", c)
|
||||
return
|
||||
if err := aiProviderService.UpdateAiProvider(&provider, userID); err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
response.OkWithData(provider, c)
|
||||
}
|
||||
|
||||
// GetAiProvider 获取AI提供商详情
|
||||
// FindAiProvider 查询提供商
|
||||
// @Tags AiProvider
|
||||
// @Summary 获取AI提供商详情
|
||||
// @Summary 查询提供商
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "提供商ID"
|
||||
// @Success 200 {object} response.Response{data=app.AiProvider,msg=string} "获取成功"
|
||||
// @Router /app/provider/:id [get]
|
||||
func (a *AiProviderApi) GetAiProvider(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
|
||||
provider, err := aiProviderService.GetAiProvider(uint(id))
|
||||
// @Param data query request.GetById true "ID"
|
||||
// @Success 200 {object} response.Response{data=app.AiProvider,msg=string} "查询成功"
|
||||
// @Router /aiProvider/findAiProvider [get]
|
||||
func (a *AiProviderApi) FindAiProvider(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindQuery(&reqId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取提供商失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取提供商失败", c)
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
response.OkWithData(provider, c)
|
||||
if provider, err := aiProviderService.GetAiProvider(reqId.Uint(), userID); err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
} else {
|
||||
response.OkWithData(provider, c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAiProviderList 获取AI提供商列表
|
||||
// GetAiProviderList 获取提供商列表
|
||||
// @Tags AiProvider
|
||||
// @Summary 获取AI提供商列表
|
||||
// @Summary 获取提供商列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=[]app.AiProvider,msg=string} "获取成功"
|
||||
// @Router /app/provider/list [get]
|
||||
// @Param data query request.PageInfo true "分页参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /aiProvider/getAiProviderList [get]
|
||||
func (a *AiProviderApi) GetAiProviderList(c *gin.Context) {
|
||||
list, err := aiProviderService.GetAiProviderList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取提供商列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取提供商列表失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// TestConnection 测试连接
|
||||
// @Tags AiProvider
|
||||
// @Summary 测试AI提供商连接
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.TestConnectionRequest true "连接信息"
|
||||
// @Success 200 {object} response.Response{data=response.TestConnectionResponse,msg=string} "测试成功"
|
||||
// @Router /app/provider/test [post]
|
||||
func (a *AiProviderApi) TestConnection(c *gin.Context) {
|
||||
var req request.TestConnectionRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
var pageInfo request.PageInfo
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
result, err := aiProviderService.TestConnection(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("测试连接失败!", zap.Error(err))
|
||||
response.FailWithMessage("测试连接失败", c)
|
||||
return
|
||||
if list, total, err := aiProviderService.GetAiProviderList(pageInfo, userID); 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)
|
||||
}
|
||||
|
||||
response.OkWithData(result, c)
|
||||
}
|
||||
|
||||
// GetModels 获取模型列表
|
||||
// @Tags AiProvider
|
||||
// @Summary 获取AI提供商的模型列表
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetModelsRequest true "提供商信息"
|
||||
// @Success 200 {object} response.Response{data=[]response.ModelInfo,msg=string} "获取成功"
|
||||
// @Router /app/provider/models [post]
|
||||
func (a *AiProviderApi) GetModels(c *gin.Context) {
|
||||
var req request.GetModelsRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
models, err := aiProviderService.GetModels(&req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取模型列表失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(models, c)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package app
|
||||
|
||||
type ApiGroup struct {
|
||||
AiPresetApi AiPresetApi
|
||||
AiProviderApi AiProviderApi
|
||||
AiProxyApi AiProxyApi
|
||||
PresetBindingApi PresetBindingApi
|
||||
AiProxyApi
|
||||
AiPresetApi
|
||||
AiProviderApi
|
||||
AiPresetBindingApi
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package v1
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/api/v1/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/api/v1/example"
|
||||
"git.echol.cn/loser/ai_proxy/server/api/v1/system"
|
||||
)
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
type ApiGroup struct {
|
||||
SystemApiGroup system.ApiGroup
|
||||
AppApiGroup app.ApiGroup
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
AppApiGroup app.ApiGroup
|
||||
}
|
||||
|
||||
15
server/api/v1/example/enter.go
Normal file
15
server/api/v1/example/enter.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package example
|
||||
|
||||
import "git.echol.cn/loser/ai_proxy/server/service"
|
||||
|
||||
type ApiGroup struct {
|
||||
CustomerApi
|
||||
FileUploadAndDownloadApi
|
||||
AttachmentCategoryApi
|
||||
}
|
||||
|
||||
var (
|
||||
customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
|
||||
fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
|
||||
attachmentCategoryService = service.ServiceGroupApp.ExampleServiceGroup.AttachmentCategoryService
|
||||
)
|
||||
82
server/api/v1/example/exa_attachment_category.go
Normal file
82
server/api/v1/example/exa_attachment_category.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
common "git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AttachmentCategoryApi struct{}
|
||||
|
||||
// GetCategoryList
|
||||
// @Tags GetCategoryList
|
||||
// @Summary 媒体库分类列表
|
||||
// @Security AttachmentCategory
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=example.ExaAttachmentCategory,msg=string} "媒体库分类列表"
|
||||
// @Router /attachmentCategory/getCategoryList [get]
|
||||
func (a *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
|
||||
res, err := attachmentCategoryService.GetCategoryList()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取分类列表失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取分类列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(res, c)
|
||||
}
|
||||
|
||||
// AddCategory
|
||||
// @Tags AddCategory
|
||||
// @Summary 添加媒体库分类
|
||||
// @Security AttachmentCategory
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaAttachmentCategory true "媒体库分类数据"// @Success 200 {object} response.Response{msg=string} "添加媒体库分类"
|
||||
// @Router /attachmentCategory/addCategory [post]
|
||||
func (a *AttachmentCategoryApi) AddCategory(c *gin.Context) {
|
||||
var req example.ExaAttachmentCategory
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
global.GVA_LOG.Error("参数错误!", zap.Error(err))
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := attachmentCategoryService.AddCategory(&req); err != nil {
|
||||
global.GVA_LOG.Error("创建/更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建/更新失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建/更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteCategory
|
||||
// @Tags DeleteCategory
|
||||
// @Summary 删除分类
|
||||
// @Security AttachmentCategory
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.GetById true "分类id"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除分类"
|
||||
// @Router /attachmentCategory/deleteCategory [post]
|
||||
func (a *AttachmentCategoryApi) DeleteCategory(c *gin.Context) {
|
||||
var req common.GetById
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.ID == 0 {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := attachmentCategoryService.DeleteCategory(&req.ID); err != nil {
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
156
server/api/v1/example/exa_breakpoint_continue.go
Normal file
156
server/api/v1/example/exa_breakpoint_continue.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
exampleRes "git.echol.cn/loser/ai_proxy/server/model/example/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// BreakpointContinue
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 断点续传到服务器
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "an example for breakpoint resume, 断点续传示例"
|
||||
// @Success 200 {object} response.Response{msg=string} "断点续传到服务器"
|
||||
// @Router /fileUploadAndDownload/breakpointContinue [post]
|
||||
func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
|
||||
fileMd5 := c.Request.FormValue("fileMd5")
|
||||
fileName := c.Request.FormValue("fileName")
|
||||
chunkMd5 := c.Request.FormValue("chunkMd5")
|
||||
chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber"))
|
||||
chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal"))
|
||||
_, FileHeader, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("接收文件失败", c)
|
||||
return
|
||||
}
|
||||
f, err := FileHeader.Open()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件读取失败!", zap.Error(err))
|
||||
response.FailWithMessage("文件读取失败", c)
|
||||
return
|
||||
}
|
||||
defer func(f multipart.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}(f)
|
||||
cen, _ := io.ReadAll(f)
|
||||
if !utils.CheckMd5(cen, chunkMd5) {
|
||||
global.GVA_LOG.Error("检查md5失败!", zap.Error(err))
|
||||
response.FailWithMessage("检查md5失败", c)
|
||||
return
|
||||
}
|
||||
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查找或创建记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("查找或创建记录失败", c)
|
||||
return
|
||||
}
|
||||
pathC, err := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("断点续传失败!", zap.Error(err))
|
||||
response.FailWithMessage("断点续传失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathC, chunkNumber); err != nil {
|
||||
global.GVA_LOG.Error("创建文件记录失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建文件记录失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("切片创建成功", c)
|
||||
}
|
||||
|
||||
// FindFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 查找文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "Find the file, 查找文件"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/findFile [get]
|
||||
func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
fileName := c.Query("fileName")
|
||||
chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal"))
|
||||
file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查找失败!", zap.Error(err))
|
||||
response.FailWithMessage("查找失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// BreakpointContinueFinish
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 创建文件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件完成"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
|
||||
// @Router /fileUploadAndDownload/findFile [post]
|
||||
func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
|
||||
fileMd5 := c.Query("fileMd5")
|
||||
fileName := c.Query("fileName")
|
||||
filePath, err := utils.MakeFile(fileName, fileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("文件创建失败!", zap.Error(err))
|
||||
response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveChunk
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除切片
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "删除缓存切片"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除切片"
|
||||
// @Router /fileUploadAndDownload/removeChunk [post]
|
||||
func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
|
||||
var file example.ExaFile
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
// 路径穿越拦截
|
||||
if strings.Contains(file.FilePath, "..") || strings.Contains(file.FilePath, "../") || strings.Contains(file.FilePath, "./") || strings.Contains(file.FilePath, ".\\") {
|
||||
response.FailWithMessage("非法路径,禁止删除", c)
|
||||
return
|
||||
}
|
||||
err = utils.RemoveChunk(file.FileMd5)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("缓存切片删除失败!", zap.Error(err))
|
||||
return
|
||||
}
|
||||
err = fileUploadAndDownloadService.DeleteFileChunk(file.FileMd5, file.FilePath)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("缓存切片删除成功", c)
|
||||
}
|
||||
176
server/api/v1/example/exa_customer.go
Normal file
176
server/api/v1/example/exa_customer.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
exampleRes "git.echol.cn/loser/ai_proxy/server/model/example/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CustomerApi struct{}
|
||||
|
||||
// CreateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 创建客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建客户"
|
||||
// @Router /customer/customer [post]
|
||||
func (e *CustomerApi) CreateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customer.SysUserID = utils.GetUserID(c)
|
||||
customer.SysUserAuthorityID = utils.GetUserAuthorityId(c)
|
||||
err = customerService.CreateExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 删除客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除客户"
|
||||
// @Router /customer/customer [delete]
|
||||
func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.DeleteExaCustomer(customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 更新客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaCustomer true "客户ID, 客户信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新客户信息"
|
||||
// @Router /customer/customer [put]
|
||||
func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindJSON(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer, utils.CustomerVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = customerService.UpdateExaCustomer(&customer)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// GetExaCustomer
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 获取单一客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query example.ExaCustomer true "客户ID"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情"
|
||||
// @Router /customer/customer [get]
|
||||
func (e *CustomerApi) GetExaCustomer(c *gin.Context) {
|
||||
var customer example.ExaCustomer
|
||||
err := c.ShouldBindQuery(&customer)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(customer.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := customerService.GetExaCustomer(customer.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetExaCustomerList
|
||||
// @Tags ExaCustomer
|
||||
// @Summary 分页获取权限客户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /customer/customerList [get]
|
||||
func (e *CustomerApi) GetExaCustomerList(c *gin.Context) {
|
||||
var pageInfo request.PageInfo
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(pageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: customerList,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
135
server/api/v1/example/exa_file_upload_download.go
Normal file
135
server/api/v1/example/exa_file_upload_download.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package example
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example/request"
|
||||
exampleRes "git.echol.cn/loser/ai_proxy/server/model/example/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type FileUploadAndDownloadApi struct{}
|
||||
|
||||
// UploadFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 上传文件示例
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param file formData file true "上传文件示例"
|
||||
// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
|
||||
// @Router /fileUploadAndDownload/upload [post]
|
||||
func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
noSave := c.DefaultQuery("noSave", "0")
|
||||
_, header, err := c.Request.FormFile("file")
|
||||
classId, _ := strconv.Atoi(c.DefaultPostForm("classId", "0"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("接收文件失败", c)
|
||||
return
|
||||
}
|
||||
file, err = fileUploadAndDownloadService.UploadFile(header, noSave, classId) // 文件上传后拿到文件路径
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("上传文件失败!", zap.Error(err))
|
||||
response.FailWithMessage("上传文件失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
|
||||
}
|
||||
|
||||
// EditFileName 编辑文件名或者备注
|
||||
func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = fileUploadAndDownloadService.EditFileName(file)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("编辑失败!", zap.Error(err))
|
||||
response.FailWithMessage("编辑失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("编辑成功", c)
|
||||
}
|
||||
|
||||
// DeleteFile
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 删除文件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除文件"
|
||||
// @Router /fileUploadAndDownload/deleteFile [post]
|
||||
func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
|
||||
var file example.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := fileUploadAndDownloadService.DeleteFile(file); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetFileList
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 分页文件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExaAttachmentCategorySearch true "页码, 每页大小, 分类id"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /fileUploadAndDownload/getFileList [post]
|
||||
func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
|
||||
var pageInfo request.ExaAttachmentCategorySearch
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// ImportURL
|
||||
// @Tags ExaFileUploadAndDownload
|
||||
// @Summary 导入URL
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body example.ExaFileUploadAndDownload true "对象"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入URL"
|
||||
// @Router /fileUploadAndDownload/importURL [post]
|
||||
func (b *FileUploadAndDownloadApi) ImportURL(c *gin.Context) {
|
||||
var file []example.ExaFileUploadAndDownload
|
||||
err := c.ShouldBindJSON(&file)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := fileUploadAndDownloadService.ImportURL(&file); err != nil {
|
||||
global.GVA_LOG.Error("导入URL失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入URL失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("导入URL成功", c)
|
||||
}
|
||||
115
server/api/v1/system/auto_code_history.go
Normal file
115
server/api/v1/system/auto_code_history.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
common "git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
request "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeHistoryApi struct{}
|
||||
|
||||
// First
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取meta信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息"
|
||||
// @Router /autoCode/getMeta [post]
|
||||
func (a *AutoCodeHistoryApi) First(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := autoCodeHistoryService.First(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除回滚记录"
|
||||
// @Router /autoCode/delSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// RollBack
|
||||
// @Tags AutoCode
|
||||
// @Summary 回滚自动生成代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoHistoryRollBack true "请求参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
|
||||
// @Router /autoCode/rollback [post]
|
||||
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
|
||||
var info request.SysAutoHistoryRollBack
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeHistoryService.RollBack(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("回滚成功", c)
|
||||
}
|
||||
|
||||
// GetList
|
||||
// @Tags AutoCode
|
||||
// @Summary 查询回滚记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.PageInfo true "请求参数"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /autoCode/getSysHistory [post]
|
||||
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
|
||||
var info common.PageInfo
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := autoCodeHistoryService.GetList(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: info.Page,
|
||||
PageSize: info.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
144
server/api/v1/system/auto_code_mcp.go
Normal file
144
server/api/v1/system/auto_code_mcp.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/mcp/client"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 自动McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoMcpTool true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/mcp [post]
|
||||
func (a *AutoCodeTemplateApi) MCP(c *gin.Context) {
|
||||
var info request.AutoMcpTool
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
toolFilePath, err := autoCodeTemplateService.CreateMcp(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建失败", c)
|
||||
global.GVA_LOG.Error(err.Error())
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功,MCP Tool路径:"+toolFilePath, c)
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 自动McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoMcpTool true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/mcpList [post]
|
||||
func (a *AutoCodeTemplateApi) MCPList(c *gin.Context) {
|
||||
|
||||
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
|
||||
|
||||
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
|
||||
defer testClient.Close()
|
||||
toolsRequest := mcp.ListToolsRequest{}
|
||||
|
||||
list, err := testClient.ListTools(c.Request.Context(), toolsRequest)
|
||||
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建失败", c)
|
||||
global.GVA_LOG.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
mcpServerConfig := map[string]interface{}{
|
||||
"mcpServers": map[string]interface{}{
|
||||
global.GVA_CONFIG.MCP.Name: map[string]string{
|
||||
"url": baseUrl,
|
||||
},
|
||||
},
|
||||
}
|
||||
response.OkWithData(gin.H{
|
||||
"mcpServerConfig": mcpServerConfig,
|
||||
"list": list,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags mcp
|
||||
// @Summary 测试McpTool
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body object true "调用MCP Tool的参数"
|
||||
// @Success 200 {object} response.Response "{"success":true,"data":{},"msg":"测试成功"}"
|
||||
// @Router /autoCode/mcpTest [post]
|
||||
func (a *AutoCodeTemplateApi) MCPTest(c *gin.Context) {
|
||||
// 定义接口请求结构
|
||||
var testRequest struct {
|
||||
Name string `json:"name" binding:"required"` // 工具名称
|
||||
Arguments map[string]interface{} `json:"arguments" binding:"required"` // 工具参数
|
||||
}
|
||||
|
||||
// 绑定JSON请求体
|
||||
if err := c.ShouldBindJSON(&testRequest); err != nil {
|
||||
response.FailWithMessage("参数解析失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建MCP客户端
|
||||
baseUrl := fmt.Sprintf("http://127.0.0.1:%d%s", global.GVA_CONFIG.System.Addr, global.GVA_CONFIG.MCP.SSEPath)
|
||||
testClient, err := client.NewClient(baseUrl, "testClient", "v1.0.0", global.GVA_CONFIG.MCP.Name)
|
||||
if err != nil {
|
||||
response.FailWithMessage("创建MCP客户端失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
defer testClient.Close()
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 初始化MCP连接
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: "testClient",
|
||||
Version: "v1.0.0",
|
||||
}
|
||||
|
||||
_, err = testClient.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
response.FailWithMessage("初始化MCP连接失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 构建工具调用请求
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = testRequest.Name
|
||||
request.Params.Arguments = testRequest.Arguments
|
||||
|
||||
// 调用工具
|
||||
result, err := testClient.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
response.FailWithMessage("工具调用失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理响应结果
|
||||
if len(result.Content) == 0 {
|
||||
response.FailWithMessage("工具未返回任何内容", c)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
response.OkWithData(result.Content, c)
|
||||
}
|
||||
100
server/api/v1/system/auto_code_package.go
Normal file
100
server/api/v1/system/auto_code_package.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
common "git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AutoCodePackageApi struct{}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 创建package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAutoCodePackageCreate true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/createPackage [post]
|
||||
func (a *AutoCodePackageApi) Create(c *gin.Context) {
|
||||
var info request.SysAutoCodePackageCreate
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
if err := utils.Verify(info, utils.AutoPackageVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if strings.Contains(info.PackageName, "\\") || strings.Contains(info.PackageName, "/") || strings.Contains(info.PackageName, "..") {
|
||||
response.FailWithMessage("包名不合法", c)
|
||||
return
|
||||
} // PackageName可能导致路径穿越的问题 / 和 \ 都要防止
|
||||
err := autoCodePackageService.Create(c.Request.Context(), &info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @Tags AutoCode
|
||||
// @Summary 删除package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body common.GetById true "创建package"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
|
||||
// @Router /autoCode/delPackage [post]
|
||||
func (a *AutoCodePackageApi) Delete(c *gin.Context) {
|
||||
var info common.GetById
|
||||
_ = c.ShouldBindJSON(&info)
|
||||
err := autoCodePackageService.Delete(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// All
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getPackage [post]
|
||||
func (a *AutoCodePackageApi) All(c *gin.Context) {
|
||||
data, err := autoCodePackageService.All(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"pkgs": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
// Templates
|
||||
// @Tags AutoCodePackage
|
||||
// @Summary 获取package
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
|
||||
// @Router /autoCode/getTemplates [get]
|
||||
func (a *AutoCodePackageApi) Templates(c *gin.Context) {
|
||||
data, err := autoCodePackageService.Templates(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(data, "获取成功", c)
|
||||
}
|
||||
218
server/api/v1/system/auto_code_plugin.go
Normal file
218
server/api/v1/system/auto_code_plugin.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/plugin/plugin-tool/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodePluginApi struct{}
|
||||
|
||||
// Install
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 安装插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept multipart/form-data
|
||||
// @Produce application/json
|
||||
// @Param plug formData file true "this is a test file"
|
||||
// @Success 200 {object} response.Response{data=[]interface{},msg=string} "安装插件成功"
|
||||
// @Router /autoCode/installPlugin [post]
|
||||
func (a *AutoCodePluginApi) Install(c *gin.Context) {
|
||||
header, err := c.FormFile("plug")
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
web, server, err := autoCodePluginService.Install(header)
|
||||
webStr := "web插件安装成功"
|
||||
serverStr := "server插件安装成功"
|
||||
if web == -1 {
|
||||
webStr = "web端插件未成功安装,请按照文档自行解压安装,如果为纯后端插件请忽略此条提示"
|
||||
}
|
||||
if server == -1 {
|
||||
serverStr = "server端插件未成功安装,请按照文档自行解压安装,如果为纯前端插件请忽略此条提示"
|
||||
}
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData([]interface{}{
|
||||
gin.H{
|
||||
"code": web,
|
||||
"msg": webStr,
|
||||
},
|
||||
gin.H{
|
||||
"code": server,
|
||||
"msg": serverStr,
|
||||
}}, c)
|
||||
}
|
||||
|
||||
// Packaged
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param plugName query string true "插件名称"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/pubPlug [post]
|
||||
func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
|
||||
plugName := c.Query("plugName")
|
||||
zipPath, err := autoCodePluginService.PubPlug(plugName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("打包失败!", zap.Error(err))
|
||||
response.FailWithMessage("打包失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage(fmt.Sprintf("打包成功,文件路径为:%s", zipPath), c)
|
||||
}
|
||||
|
||||
// InitMenu
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initMenu [post]
|
||||
func (a *AutoCodePluginApi) InitMenu(c *gin.Context) {
|
||||
var menuInfo request.InitMenu
|
||||
err := c.ShouldBindJSON(&menuInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitMenu(menuInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化Menu失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化Menu失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// InitAPI
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initAPI [post]
|
||||
func (a *AutoCodePluginApi) InitAPI(c *gin.Context) {
|
||||
var apiInfo request.InitApi
|
||||
err := c.ShouldBindJSON(&apiInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitAPI(apiInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化API失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化API失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// InitDictionary
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 打包插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
|
||||
// @Router /autoCode/initDictionary [post]
|
||||
func (a *AutoCodePluginApi) InitDictionary(c *gin.Context) {
|
||||
var dictInfo request.InitDictionary
|
||||
err := c.ShouldBindJSON(&dictInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodePluginService.InitDictionary(dictInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建初始化Dictionary失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建初始化Dictionary失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("文件变更成功", c)
|
||||
}
|
||||
|
||||
// GetPluginList
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 获取插件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=[]systemRes.PluginInfo} "获取插件列表成功"
|
||||
// @Router /autoCode/getPluginList [get]
|
||||
func (a *AutoCodePluginApi) GetPluginList(c *gin.Context) {
|
||||
serverDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin")
|
||||
webDir := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin")
|
||||
|
||||
serverEntries, _ := os.ReadDir(serverDir)
|
||||
webEntries, _ := os.ReadDir(webDir)
|
||||
|
||||
configMap := make(map[string]string)
|
||||
|
||||
for _, entry := range serverEntries {
|
||||
if entry.IsDir() {
|
||||
configMap[entry.Name()] = "server"
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range webEntries {
|
||||
if entry.IsDir() {
|
||||
if val, ok := configMap[entry.Name()]; ok {
|
||||
if val == "server" {
|
||||
configMap[entry.Name()] = "full"
|
||||
}
|
||||
} else {
|
||||
configMap[entry.Name()] = "web"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var list []systemRes.PluginInfo
|
||||
for k, v := range configMap {
|
||||
apis, menus, dicts := utils.GetPluginData(k)
|
||||
list = append(list, systemRes.PluginInfo{
|
||||
PluginName: k,
|
||||
PluginType: v,
|
||||
Apis: apis,
|
||||
Menus: menus,
|
||||
Dictionaries: dicts,
|
||||
})
|
||||
}
|
||||
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
// Remove
|
||||
// @Tags AutoCodePlugin
|
||||
// @Summary 删除插件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param pluginName query string true "插件名称"
|
||||
// @Param pluginType query string true "插件类型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除插件成功"
|
||||
// @Router /autoCode/removePlugin [post]
|
||||
func (a *AutoCodePluginApi) Remove(c *gin.Context) {
|
||||
pluginName := c.Query("pluginName")
|
||||
pluginType := c.Query("pluginType")
|
||||
err := autoCodePluginService.Remove(pluginName, pluginType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
121
server/api/v1/system/auto_code_template.go
Normal file
121
server/api/v1/system/auto_code_template.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeTemplateApi struct{}
|
||||
|
||||
// Preview
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 预览创建后的代码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "预览创建代码"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
|
||||
// @Router /autoCode/preview [post]
|
||||
func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
info.PackageT = utils.FirstUpper(info.Package)
|
||||
autoCode, err := autoCodeTemplateService.Preview(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error(err.Error(), zap.Error(err))
|
||||
response.FailWithMessage("预览失败:"+err.Error(), c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// Create
|
||||
// @Tags AutoCodeTemplate
|
||||
// @Summary 自动代码模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "创建自动代码"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/createTemp [post]
|
||||
func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
|
||||
var info request.AutoCode
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(info, utils.AutoCodeVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = info.Pretreatment()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = autoCodeTemplateService.Create(c.Request.Context(), info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
} else {
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// AddFunc
|
||||
// @Tags AddFunc
|
||||
// @Summary 增加方法
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.AutoCode true "增加方法"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/addFunc [post]
|
||||
func (a *AutoCodeTemplateApi) AddFunc(c *gin.Context) {
|
||||
var info request.AutoFunc
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
var tempMap map[string]string
|
||||
if info.IsPreview {
|
||||
info.Router = "填充router"
|
||||
info.FuncName = "填充funcName"
|
||||
info.Method = "填充method"
|
||||
info.Description = "填充description"
|
||||
tempMap, err = autoCodeTemplateService.GetApiAndServer(info)
|
||||
} else {
|
||||
err = autoCodeTemplateService.AddFunc(info)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("注入失败!", zap.Error(err))
|
||||
response.FailWithMessage("注入失败", c)
|
||||
} else {
|
||||
if info.IsPreview {
|
||||
response.OkWithDetailed(tempMap, "注入成功", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("注入成功", c)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,57 @@
|
||||
package system
|
||||
|
||||
import "git.echol.cn/loser/ai_proxy/server/service"
|
||||
|
||||
type ApiGroup struct {
|
||||
UserApi UserApi
|
||||
ApiApi ApiApi
|
||||
DBApi
|
||||
JwtApi
|
||||
BaseApi
|
||||
SystemApi
|
||||
CasbinApi
|
||||
AutoCodeApi
|
||||
SystemApiApi
|
||||
AuthorityApi
|
||||
DictionaryApi
|
||||
AuthorityMenuApi
|
||||
OperationRecordApi
|
||||
DictionaryDetailApi
|
||||
AuthorityBtnApi
|
||||
SysExportTemplateApi
|
||||
AutoCodePluginApi
|
||||
AutoCodePackageApi
|
||||
AutoCodeHistoryApi
|
||||
AutoCodeTemplateApi
|
||||
SysParamsApi
|
||||
SysVersionApi
|
||||
SysErrorApi
|
||||
LoginLogApi
|
||||
ApiTokenApi
|
||||
SkillsApi
|
||||
}
|
||||
|
||||
var (
|
||||
apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||
menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||||
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||
initDBService = service.ServiceGroupApp.SystemServiceGroup.InitDBService
|
||||
casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
|
||||
baseMenuService = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
|
||||
authorityService = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
|
||||
dictionaryService = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
authorityBtnService = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
|
||||
systemConfigService = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
|
||||
sysParamsService = service.ServiceGroupApp.SystemServiceGroup.SysParamsService
|
||||
operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
autoCodeService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
|
||||
autoCodePluginService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePlugin
|
||||
autoCodePackageService = service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
|
||||
autoCodeHistoryService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistory
|
||||
autoCodeTemplateService = service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
|
||||
sysVersionService = service.ServiceGroupApp.SystemServiceGroup.SysVersionService
|
||||
sysErrorService = service.ServiceGroupApp.SystemServiceGroup.SysErrorService
|
||||
loginLogService = service.ServiceGroupApp.SystemServiceGroup.LoginLogService
|
||||
apiTokenService = service.ServiceGroupApp.SystemServiceGroup.ApiTokenService
|
||||
skillsService = service.ServiceGroupApp.SystemServiceGroup.SkillsService
|
||||
)
|
||||
|
||||
@@ -2,148 +2,322 @@ package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ApiApi struct{}
|
||||
type SystemApiApi struct{}
|
||||
|
||||
var apiService = service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
|
||||
// CreateApi 创建API
|
||||
// @Tags System
|
||||
// @Summary 创建API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CreateApiRequest true "API信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /v1/system/api [post]
|
||||
func (a *ApiApi) CreateApi(c *gin.Context) {
|
||||
var req request.CreateApiRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err := apiService.CreateApi(&req)
|
||||
// CreateApi
|
||||
// @Tags SysApi
|
||||
// @Summary 创建基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建基础api"
|
||||
// @Router /api/createApi [post]
|
||||
func (s *SystemApiApi) CreateApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建API失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = utils.Verify(api, utils.ApiVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.CreateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateApi 更新API
|
||||
// @Tags System
|
||||
// @Summary 更新API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.UpdateApiRequest true "API信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /v1/system/api [put]
|
||||
func (a *ApiApi) UpdateApi(c *gin.Context) {
|
||||
var req request.UpdateApiRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err := apiService.UpdateApi(&req)
|
||||
// SyncApi
|
||||
// @Tags SysApi
|
||||
// @Summary 同步API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "同步API"
|
||||
// @Router /api/syncApi [get]
|
||||
func (s *SystemApiApi) SyncApi(c *gin.Context) {
|
||||
newApis, deleteApis, ignoreApis, err := apiService.SyncApi()
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
global.GVA_LOG.Error("同步失败!", zap.Error(err))
|
||||
response.FailWithMessage("同步失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
response.OkWithData(gin.H{
|
||||
"newApis": newApis,
|
||||
"deleteApis": deleteApis,
|
||||
"ignoreApis": ignoreApis,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// DeleteApi 删除API
|
||||
// @Tags System
|
||||
// @Summary 删除API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "API ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /v1/system/api/:id [delete]
|
||||
func (a *ApiApi) DeleteApi(c *gin.Context) {
|
||||
id := utils.GetUintParam(c, "id")
|
||||
// GetApiGroups
|
||||
// @Tags SysApi
|
||||
// @Summary 获取API分组
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "获取API分组"
|
||||
// @Router /api/getApiGroups [get]
|
||||
func (s *SystemApiApi) GetApiGroups(c *gin.Context) {
|
||||
groups, apiGroupMap, err := apiService.GetApiGroups()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(gin.H{
|
||||
"groups": groups,
|
||||
"apiGroupMap": apiGroupMap,
|
||||
}, c)
|
||||
}
|
||||
|
||||
err := apiService.DeleteApi(id)
|
||||
// IgnoreApi
|
||||
// @Tags IgnoreApi
|
||||
// @Summary 忽略API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "同步API"
|
||||
// @Router /api/ignoreApi [post]
|
||||
func (s *SystemApiApi) IgnoreApi(c *gin.Context) {
|
||||
var ignoreApi system.SysIgnoreApi
|
||||
err := c.ShouldBindJSON(&ignoreApi)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.IgnoreApi(ignoreApi)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
|
||||
response.FailWithMessage("忽略失败", c)
|
||||
return
|
||||
}
|
||||
response.Ok(c)
|
||||
}
|
||||
|
||||
// EnterSyncApi
|
||||
// @Tags SysApi
|
||||
// @Summary 确认同步API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "确认同步API"
|
||||
// @Router /api/enterSyncApi [post]
|
||||
func (s *SystemApiApi) EnterSyncApi(c *gin.Context) {
|
||||
var syncApi systemRes.SysSyncApis
|
||||
err := c.ShouldBindJSON(&syncApi)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.EnterSyncApi(syncApi)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("忽略失败!", zap.Error(err))
|
||||
response.FailWithMessage("忽略失败", c)
|
||||
return
|
||||
}
|
||||
response.Ok(c)
|
||||
}
|
||||
|
||||
// DeleteApi
|
||||
// @Tags SysApi
|
||||
// @Summary 删除api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除api"
|
||||
// @Router /api/deleteApi [post]
|
||||
func (s *SystemApiApi) DeleteApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(api.GVA_MODEL, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.DeleteApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetApiList 获取API列表
|
||||
// @Tags System
|
||||
// @Summary 获取API列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Param path query string false "API路径"
|
||||
// @Param apiGroup query string false "API分组"
|
||||
// @Param method query string false "请求方法"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /v1/system/api/list [get]
|
||||
func (a *ApiApi) GetApiList(c *gin.Context) {
|
||||
var req request.GetApiListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 10
|
||||
}
|
||||
|
||||
list, total, err := apiService.GetApiList(&req)
|
||||
// GetApiList
|
||||
// @Tags SysApi
|
||||
// @Summary 分页获取API列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SearchApiParams true "分页获取API列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取API列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /api/getApiList [post]
|
||||
func (s *SystemApiApi) GetApiList(c *gin.Context) {
|
||||
var pageInfo systemReq.SearchApiParams
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetApiById 根据ID获取API
|
||||
// @Tags System
|
||||
// @Summary 根据ID获取API
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "API ID"
|
||||
// @Success 200 {object} response.Response{data=response.ApiInfo} "获取成功"
|
||||
// @Router /v1/system/api/:id [get]
|
||||
func (a *ApiApi) GetApiById(c *gin.Context) {
|
||||
id := utils.GetUintParam(c, "id")
|
||||
|
||||
info, err := apiService.GetApiById(id)
|
||||
// GetApiById
|
||||
// @Tags SysApi
|
||||
// @Summary 根据id获取api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "根据id获取api"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAPIResponse} "根据id获取api,返回包括api详情"
|
||||
// @Router /api/getApiById [post]
|
||||
func (s *SystemApiApi) GetApiById(c *gin.Context) {
|
||||
var idInfo request.GetById
|
||||
err := c.ShouldBindJSON(&idInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(info, c)
|
||||
err = utils.Verify(idInfo, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
api, err := apiService.GetApiById(idInfo.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c)
|
||||
}
|
||||
|
||||
// UpdateApi
|
||||
// @Tags SysApi
|
||||
// @Summary 修改基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
|
||||
// @Success 200 {object} response.Response{msg=string} "修改基础api"
|
||||
// @Router /api/updateApi [post]
|
||||
func (s *SystemApiApi) UpdateApi(c *gin.Context) {
|
||||
var api system.SysApi
|
||||
err := c.ShouldBindJSON(&api)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(api, utils.ApiVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.UpdateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// GetAllApis
|
||||
// @Tags SysApi
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAPIListResponse,msg=string} "获取所有的Api 不分页,返回包括api列表"
|
||||
// @Router /api/getAllApis [post]
|
||||
func (s *SystemApiApi) GetAllApis(c *gin.Context) {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
apis, err := apiService.GetAllApis(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
|
||||
}
|
||||
|
||||
// DeleteApisByIds
|
||||
// @Tags SysApi
|
||||
// @Summary 删除选中Api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除选中Api"
|
||||
// @Router /api/deleteApisByIds [delete]
|
||||
func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
|
||||
var ids request.IdsReq
|
||||
err := c.ShouldBindJSON(&ids)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiService.DeleteApisByIds(ids)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// FreshCasbin
|
||||
// @Tags SysApi
|
||||
// @Summary 刷新casbin缓存
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "刷新成功"
|
||||
// @Router /api/freshCasbin [get]
|
||||
func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
|
||||
err := casbinService.FreshCasbin()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("刷新失败!", zap.Error(err))
|
||||
response.FailWithMessage("刷新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("刷新成功", c)
|
||||
}
|
||||
|
||||
81
server/api/v1/system/sys_api_token.go
Normal file
81
server/api/v1/system/sys_api_token.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
sysReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ApiTokenApi struct{}
|
||||
|
||||
// CreateApiToken 签发Token
|
||||
func (s *ApiTokenApi) CreateApiToken(c *gin.Context) {
|
||||
var req struct {
|
||||
UserID uint `json:"userId"`
|
||||
AuthorityID uint `json:"authorityId"`
|
||||
Days int `json:"days"` // -1为永久, 其他为天数
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
token := system.SysApiToken{
|
||||
UserID: req.UserID,
|
||||
AuthorityID: req.AuthorityID,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
jwtStr, err := apiTokenService.CreateApiToken(token, req.Days)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("签发失败!", zap.Error(err))
|
||||
response.FailWithMessage("签发失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithDetailed(gin.H{"token": jwtStr}, "签发成功", c)
|
||||
}
|
||||
|
||||
// GetApiTokenList 获取列表
|
||||
func (s *ApiTokenApi) GetApiTokenList(c *gin.Context) {
|
||||
var pageInfo sysReq.SysApiTokenSearch
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := apiTokenService.GetApiTokenList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// DeleteApiToken 作废Token
|
||||
func (s *ApiTokenApi) DeleteApiToken(c *gin.Context) {
|
||||
var req system.SysApiToken
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = apiTokenService.DeleteApiToken(req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("作废失败!", zap.Error(err))
|
||||
response.FailWithMessage("作废失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("作废成功", c)
|
||||
}
|
||||
202
server/api/v1/system/sys_authority.go
Normal file
202
server/api/v1/system/sys_authority.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthorityApi struct{}
|
||||
|
||||
// CreateAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 创建角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情"
|
||||
// @Router /authority/createAuthority [post]
|
||||
func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
|
||||
var authority, authBack system.SysAuthority
|
||||
var err error
|
||||
|
||||
if err = c.ShouldBindJSON(&authority); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if err = utils.Verify(authority, utils.AuthorityVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
if *authority.ParentId == 0 && global.GVA_CONFIG.System.UseStrictAuth {
|
||||
authority.ParentId = utils.Pointer(utils.GetUserAuthorityId(c))
|
||||
}
|
||||
|
||||
if authBack, err = authorityService.CreateAuthority(authority); err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = casbinService.FreshCasbin()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建成功,权限刷新失败。", zap.Error(err))
|
||||
response.FailWithMessage("创建成功,权限刷新失败。"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
|
||||
}
|
||||
|
||||
// CopyAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 拷贝角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "拷贝角色,返回包括系统角色详情"
|
||||
// @Router /authority/copyAuthority [post]
|
||||
func (a *AuthorityApi) CopyAuthority(c *gin.Context) {
|
||||
var copyInfo systemRes.SysAuthorityCopyResponse
|
||||
err := c.ShouldBindJSON(©Info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(copyInfo, utils.OldAuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(copyInfo.Authority, utils.AuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
authBack, err := authorityService.CopyAuthority(adminAuthorityID, copyInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("拷贝失败!", zap.Error(err))
|
||||
response.FailWithMessage("拷贝失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c)
|
||||
}
|
||||
|
||||
// DeleteAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 删除角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "删除角色"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除角色"
|
||||
// @Router /authority/deleteAuthority [post]
|
||||
func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
|
||||
var authority system.SysAuthority
|
||||
var err error
|
||||
if err = c.ShouldBindJSON(&authority); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err = utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
// 删除角色之前需要判断是否有用户正在使用此角色
|
||||
if err = authorityService.DeleteAuthority(&authority); err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
_ = casbinService.FreshCasbin()
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 更新角色信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "更新角色信息,返回包括系统角色详情"
|
||||
// @Router /authority/updateAuthority [put]
|
||||
func (a *AuthorityApi) UpdateAuthority(c *gin.Context) {
|
||||
var auth system.SysAuthority
|
||||
err := c.ShouldBindJSON(&auth)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(auth, utils.AuthorityVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
authority, err := authorityService.UpdateAuthority(auth)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
|
||||
}
|
||||
|
||||
// GetAuthorityList
|
||||
// @Tags Authority
|
||||
// @Summary 分页获取角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取角色列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /authority/getAuthorityList [post]
|
||||
func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
list, err := authorityService.GetAuthorityInfoList(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
// SetDataAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 设置角色资源权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysAuthority true "设置角色资源权限"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置角色资源权限"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
|
||||
var auth system.SysAuthority
|
||||
err := c.ShouldBindJSON(&auth)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(auth, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
err = authorityService.SetDataAuthority(adminAuthorityID, auth)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
80
server/api/v1/system/sys_authority_btn.go
Normal file
80
server/api/v1/system/sys_authority_btn.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthorityBtnApi struct{}
|
||||
|
||||
// GetAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 获取权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
|
||||
// @Success 200 {object} response.Response{data=response.SysAuthorityBtnRes,msg=string} "返回列表成功"
|
||||
// @Router /authorityBtn/getAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
|
||||
var req request.SysAuthorityBtnReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
res, err := authorityBtnService.GetAuthorityBtn(req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(res, "查询成功", c)
|
||||
}
|
||||
|
||||
// SetAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 设置权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
|
||||
// @Success 200 {object} response.Response{msg=string} "返回列表成功"
|
||||
// @Router /authorityBtn/setAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) {
|
||||
var req request.SysAuthorityBtnReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = authorityBtnService.SetAuthorityBtn(req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("分配失败!", zap.Error(err))
|
||||
response.FailWithMessage("分配失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("分配成功", c)
|
||||
}
|
||||
|
||||
// CanRemoveAuthorityBtn
|
||||
// @Tags AuthorityBtn
|
||||
// @Summary 设置权限按钮
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /authorityBtn/canRemoveAuthorityBtn [post]
|
||||
func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) {
|
||||
id := c.Query("id")
|
||||
err := authorityBtnService.CanRemoveAuthorityBtn(id)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
117
server/api/v1/system/sys_auto_code.go
Normal file
117
server/api/v1/system/sys_auto_code.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AutoCodeApi struct{}
|
||||
|
||||
// GetDB
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前所有数据库
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库"
|
||||
// @Router /autoCode/getDB [get]
|
||||
func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
|
||||
businessDB := c.Query("businessDB")
|
||||
dbs, err := autoCodeService.Database(businessDB).GetDB(businessDB)
|
||||
var dbList []map[string]interface{}
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
var item = make(map[string]interface{})
|
||||
item["aliasName"] = db.AliasName
|
||||
item["dbName"] = db.Dbname
|
||||
item["disable"] = db.Disable
|
||||
item["dbtype"] = db.Type
|
||||
dbList = append(dbList, item)
|
||||
}
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"dbs": dbs, "dbList": dbList}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTables
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前数据库所有表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表"
|
||||
// @Router /autoCode/getTables [get]
|
||||
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
|
||||
dbName := c.Query("dbName")
|
||||
businessDB := c.Query("businessDB")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询table失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetColumn
|
||||
// @Tags AutoCode
|
||||
// @Summary 获取当前表所有字段
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段"
|
||||
// @Router /autoCode/getColumn [get]
|
||||
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
|
||||
businessDB := c.Query("businessDB")
|
||||
dbName := c.Query("dbName")
|
||||
if dbName == "" {
|
||||
dbName = *global.GVA_ACTIVE_DBNAME
|
||||
if businessDB != "" {
|
||||
for _, db := range global.GVA_CONFIG.DBList {
|
||||
if db.AliasName == businessDB {
|
||||
dbName = db.Dbname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tableName := c.Query("tableName")
|
||||
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
} else {
|
||||
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
func (autoApi *AutoCodeApi) LLMAuto(c *gin.Context) {
|
||||
var llm common.JSONMap
|
||||
if err := c.ShouldBindJSON(&llm); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
data, err := autoCodeService.LLMAuto(c.Request.Context(), llm)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("大模型生成失败!", zap.Error(err))
|
||||
response.FailWithMessage("大模型生成失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(data, c)
|
||||
}
|
||||
70
server/api/v1/system/sys_captcha.go
Normal file
70
server/api/v1/system/sys_captcha.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码
|
||||
// var store = captcha.NewDefaultRedisStore()
|
||||
var store = base64Captcha.DefaultMemStore
|
||||
|
||||
type BaseApi struct{}
|
||||
|
||||
// Captcha
|
||||
// @Tags Base
|
||||
// @Summary 生成验证码
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度,是否开启验证码"
|
||||
// @Router /base/captcha [post]
|
||||
func (b *BaseApi) Captcha(c *gin.Context) {
|
||||
// 判断验证码是否开启
|
||||
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
|
||||
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
|
||||
key := c.ClientIP()
|
||||
v, ok := global.BlackCache.Get(key)
|
||||
if !ok {
|
||||
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
|
||||
}
|
||||
|
||||
var oc bool
|
||||
if openCaptcha == 0 || openCaptcha < interfaceToInt(v) {
|
||||
oc = true
|
||||
}
|
||||
// 字符,公式,验证码配置
|
||||
// 生成默认数字的driver
|
||||
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
|
||||
// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis
|
||||
cp := base64Captcha.NewCaptcha(driver, store)
|
||||
id, b64s, _, err := cp.Generate()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("验证码获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysCaptchaResponse{
|
||||
CaptchaId: id,
|
||||
PicPath: b64s,
|
||||
CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
|
||||
OpenCaptcha: oc,
|
||||
}, "验证码获取成功", c)
|
||||
}
|
||||
|
||||
// 类型转换
|
||||
func interfaceToInt(v interface{}) (i int) {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
i = v
|
||||
default:
|
||||
i = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
69
server/api/v1/system/sys_casbin.go
Normal file
69
server/api/v1/system/sys_casbin.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CasbinApi struct{}
|
||||
|
||||
// UpdateCasbin
|
||||
// @Tags Casbin
|
||||
// @Summary 更新角色api权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新角色api权限"
|
||||
// @Router /casbin/UpdateCasbin [post]
|
||||
func (cas *CasbinApi) UpdateCasbin(c *gin.Context) {
|
||||
var cmr request.CasbinInReceive
|
||||
err := c.ShouldBindJSON(&cmr)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(cmr, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
err = casbinService.UpdateCasbin(adminAuthorityID, cmr.AuthorityId, cmr.CasbinInfos)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// GetPolicyPathByAuthorityId
|
||||
// @Tags Casbin
|
||||
// @Summary 获取权限列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
|
||||
// @Success 200 {object} response.Response{data=systemRes.PolicyPathResponse,msg=string} "获取权限列表,返回包括casbin详情列表"
|
||||
// @Router /casbin/getPolicyPathByAuthorityId [post]
|
||||
func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) {
|
||||
var casbin request.CasbinInReceive
|
||||
err := c.ShouldBindJSON(&casbin)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(casbin, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId)
|
||||
response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c)
|
||||
}
|
||||
191
server/api/v1/system/sys_dictionary.go
Normal file
191
server/api/v1/system/sys_dictionary.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type DictionaryApi struct{}
|
||||
|
||||
// CreateSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 创建SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建SysDictionary"
|
||||
// @Router /sysDictionary/createSysDictionary [post]
|
||||
func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.CreateSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 删除SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysDictionary"
|
||||
// @Router /sysDictionary/deleteSysDictionary [delete]
|
||||
func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.DeleteSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 更新SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionary true "SysDictionary模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新SysDictionary"
|
||||
// @Router /sysDictionary/updateSysDictionary [put]
|
||||
func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindJSON(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.UpdateSysDictionary(&dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 用id查询SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionary true "ID或字典英名"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionary"
|
||||
// @Router /sysDictionary/findSysDictionary [get]
|
||||
func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindQuery(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err))
|
||||
response.FailWithMessage("字典未创建或未开启", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysDictionaryList
|
||||
// @Tags SysDictionary
|
||||
// @Summary 分页获取SysDictionary列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionarySearch true "字典 name 或者 type"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysDictionary/getSysDictionaryList [get]
|
||||
func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
|
||||
var dictionary request.SysDictionarySearch
|
||||
err := c.ShouldBindQuery(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, err := dictionaryService.GetSysDictionaryInfoList(c, dictionary)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(list, "获取成功", c)
|
||||
}
|
||||
|
||||
// ExportSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 导出字典JSON(包含字典详情)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionary true "字典ID"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "导出字典JSON"
|
||||
// @Router /sysDictionary/exportSysDictionary [get]
|
||||
func (s *DictionaryApi) ExportSysDictionary(c *gin.Context) {
|
||||
var dictionary system.SysDictionary
|
||||
err := c.ShouldBindQuery(&dictionary)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if dictionary.ID == 0 {
|
||||
response.FailWithMessage("字典ID不能为空", c)
|
||||
return
|
||||
}
|
||||
exportData, err := dictionaryService.ExportSysDictionary(dictionary.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("导出失败!", zap.Error(err))
|
||||
response.FailWithMessage("导出失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(exportData, "导出成功", c)
|
||||
}
|
||||
|
||||
// ImportSysDictionary
|
||||
// @Tags SysDictionary
|
||||
// @Summary 导入字典JSON(包含字典详情)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ImportSysDictionaryRequest true "字典JSON数据"
|
||||
// @Success 200 {object} response.Response{msg=string} "导入字典"
|
||||
// @Router /sysDictionary/importSysDictionary [post]
|
||||
func (s *DictionaryApi) ImportSysDictionary(c *gin.Context) {
|
||||
var req request.ImportSysDictionaryRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryService.ImportSysDictionary(req.Json)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("导入失败!", zap.Error(err))
|
||||
response.FailWithMessage("导入失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("导入成功", c)
|
||||
}
|
||||
267
server/api/v1/system/sys_dictionary_detail.go
Normal file
267
server/api/v1/system/sys_dictionary_detail.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type DictionaryDetailApi struct{}
|
||||
|
||||
// CreateSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 创建SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
|
||||
func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryDetailService.CreateSysDictionaryDetail(detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 删除SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
|
||||
func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryDetailService.DeleteSysDictionaryDetail(detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 更新SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
|
||||
func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindJSON(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = dictionaryDetailService.UpdateSysDictionaryDetail(&detail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysDictionaryDetail
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 用id查询SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionaryDetail"
|
||||
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
|
||||
func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
|
||||
var detail system.SysDictionaryDetail
|
||||
err := c.ShouldBindQuery(&detail)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(detail, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysDictionaryDetailList
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 分页获取SysDictionaryDetail列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
|
||||
func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
|
||||
var pageInfo request.SysDictionaryDetailSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetDictionaryTreeList
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 获取字典详情树形结构
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param sysDictionaryID query int true "字典ID"
|
||||
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情树形结构"
|
||||
// @Router /sysDictionaryDetail/getDictionaryTreeList [get]
|
||||
func (s *DictionaryDetailApi) GetDictionaryTreeList(c *gin.Context) {
|
||||
sysDictionaryID := c.Query("sysDictionaryID")
|
||||
if sysDictionaryID == "" {
|
||||
response.FailWithMessage("字典ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id uint
|
||||
if idUint64, err := strconv.ParseUint(sysDictionaryID, 10, 32); err != nil {
|
||||
response.FailWithMessage("字典ID格式错误", c)
|
||||
return
|
||||
} else {
|
||||
id = uint(idUint64)
|
||||
}
|
||||
|
||||
list, err := dictionaryDetailService.GetDictionaryTreeList(id)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetDictionaryTreeListByType
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 根据字典类型获取字典详情树形结构
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param type query string true "字典类型"
|
||||
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情树形结构"
|
||||
// @Router /sysDictionaryDetail/getDictionaryTreeListByType [get]
|
||||
func (s *DictionaryDetailApi) GetDictionaryTreeListByType(c *gin.Context) {
|
||||
dictType := c.Query("type")
|
||||
if dictType == "" {
|
||||
response.FailWithMessage("字典类型不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := dictionaryDetailService.GetDictionaryTreeListByType(dictType)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetDictionaryDetailsByParent
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 根据父级ID获取字典详情
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.GetDictionaryDetailsByParentRequest true "查询参数"
|
||||
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情列表"
|
||||
// @Router /sysDictionaryDetail/getDictionaryDetailsByParent [get]
|
||||
func (s *DictionaryDetailApi) GetDictionaryDetailsByParent(c *gin.Context) {
|
||||
var req request.GetDictionaryDetailsByParentRequest
|
||||
err := c.ShouldBindQuery(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := dictionaryDetailService.GetDictionaryDetailsByParent(req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"list": list}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetDictionaryPath
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 获取字典详情的完整路径
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id query uint true "字典详情ID"
|
||||
// @Success 200 {object} response.Response{data=[]system.SysDictionaryDetail,msg=string} "获取字典详情路径"
|
||||
// @Router /sysDictionaryDetail/getDictionaryPath [get]
|
||||
func (s *DictionaryDetailApi) GetDictionaryPath(c *gin.Context) {
|
||||
idStr := c.Query("id")
|
||||
if idStr == "" {
|
||||
response.FailWithMessage("字典详情ID不能为空", c)
|
||||
return
|
||||
}
|
||||
|
||||
var id uint
|
||||
if idUint64, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||||
response.FailWithMessage("字典详情ID格式错误", c)
|
||||
return
|
||||
} else {
|
||||
id = uint(idUint64)
|
||||
}
|
||||
|
||||
path, err := dictionaryDetailService.GetDictionaryPath(id)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"path": path}, "获取成功", c)
|
||||
}
|
||||
199
server/api/v1/system/sys_error.go
Normal file
199
server/api/v1/system/sys_error.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SysErrorApi struct{}
|
||||
|
||||
// CreateSysError 创建错误日志
|
||||
// @Tags SysError
|
||||
// @Summary 创建错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysError true "创建错误日志"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /sysError/createSysError [post]
|
||||
func (sysErrorApi *SysErrorApi) CreateSysError(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var sysError system.SysError
|
||||
err := c.ShouldBindJSON(&sysError)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysErrorService.CreateSysError(ctx, &sysError)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysError 删除错误日志
|
||||
// @Tags SysError
|
||||
// @Summary 删除错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysError true "删除错误日志"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /sysError/deleteSysError [delete]
|
||||
func (sysErrorApi *SysErrorApi) DeleteSysError(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
err := sysErrorService.DeleteSysError(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysErrorByIds 批量删除错误日志
|
||||
// @Tags SysError
|
||||
// @Summary 批量删除错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /sysError/deleteSysErrorByIds [delete]
|
||||
func (sysErrorApi *SysErrorApi) DeleteSysErrorByIds(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := sysErrorService.DeleteSysErrorByIds(ctx, IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysError 更新错误日志
|
||||
// @Tags SysError
|
||||
// @Summary 更新错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysError true "更新错误日志"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /sysError/updateSysError [put]
|
||||
func (sysErrorApi *SysErrorApi) UpdateSysError(c *gin.Context) {
|
||||
// 从ctx获取标准context进行业务行为
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var sysError system.SysError
|
||||
err := c.ShouldBindJSON(&sysError)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysErrorService.UpdateSysError(ctx, sysError)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysError 用id查询错误日志
|
||||
// @Tags SysError
|
||||
// @Summary 用id查询错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query uint true "用id查询错误日志"
|
||||
// @Success 200 {object} response.Response{data=system.SysError,msg=string} "查询成功"
|
||||
// @Router /sysError/findSysError [get]
|
||||
func (sysErrorApi *SysErrorApi) FindSysError(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
ID := c.Query("ID")
|
||||
resysError, err := sysErrorService.GetSysError(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resysError, c)
|
||||
}
|
||||
|
||||
// GetSysErrorList 分页获取错误日志列表
|
||||
// @Tags SysError
|
||||
// @Summary 分页获取错误日志列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysErrorSearch true "分页获取错误日志列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /sysError/getSysErrorList [get]
|
||||
func (sysErrorApi *SysErrorApi) GetSysErrorList(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var pageInfo systemReq.SysErrorSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := sysErrorService.GetSysErrorInfoList(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)
|
||||
}
|
||||
|
||||
// GetSysErrorSolution 触发错误日志的异步处理
|
||||
// @Tags SysError
|
||||
// @Summary 根据ID触发处理:标记为处理中,1分钟后自动改为处理完成
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id query string true "错误日志ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "处理已提交"
|
||||
// @Router /sysError/getSysErrorSolution [get]
|
||||
func (sysErrorApi *SysErrorApi) GetSysErrorSolution(c *gin.Context) {
|
||||
// 创建业务用Context
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 兼容 id 与 ID 两种参数
|
||||
ID := c.Query("id")
|
||||
if ID == "" {
|
||||
response.FailWithMessage("缺少参数: id", c)
|
||||
return
|
||||
}
|
||||
|
||||
err := sysErrorService.GetSysErrorSolution(ctx, ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("处理触发失败!", zap.Error(err))
|
||||
response.FailWithMessage("处理触发失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("已提交至AI处理", c)
|
||||
}
|
||||
456
server/api/v1/system/sys_export_template.go
Normal file
456
server/api/v1/system/sys_export_template.go
Normal file
@@ -0,0 +1,456 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/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)
|
||||
}
|
||||
}
|
||||
59
server/api/v1/system/sys_initdb.go
Normal file
59
server/api/v1/system/sys_initdb.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type DBApi struct{}
|
||||
|
||||
// InitDB
|
||||
// @Tags InitDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Param data body request.InitDB true "初始化数据库参数"
|
||||
// @Success 200 {object} response.Response{data=string} "初始化用户数据库"
|
||||
// @Router /init/initdb [post]
|
||||
func (i *DBApi) InitDB(c *gin.Context) {
|
||||
if global.GVA_DB != nil {
|
||||
global.GVA_LOG.Error("已存在数据库配置!")
|
||||
response.FailWithMessage("已存在数据库配置", c)
|
||||
return
|
||||
}
|
||||
var dbInfo request.InitDB
|
||||
if err := c.ShouldBindJSON(&dbInfo); err != nil {
|
||||
global.GVA_LOG.Error("参数校验不通过!", zap.Error(err))
|
||||
response.FailWithMessage("参数校验不通过", c)
|
||||
return
|
||||
}
|
||||
if err := initDBService.InitDB(dbInfo); err != nil {
|
||||
global.GVA_LOG.Error("自动创建数据库失败!", zap.Error(err))
|
||||
response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("自动创建数据库成功", c)
|
||||
}
|
||||
|
||||
// CheckDB
|
||||
// @Tags CheckDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "初始化用户数据库"
|
||||
// @Router /init/checkdb [post]
|
||||
func (i *DBApi) CheckDB(c *gin.Context) {
|
||||
var (
|
||||
message = "前往初始化数据库"
|
||||
needInit = true
|
||||
)
|
||||
|
||||
if global.GVA_DB != nil {
|
||||
message = "数据库无需初始化"
|
||||
needInit = false
|
||||
}
|
||||
global.GVA_LOG.Info(message)
|
||||
response.OkWithDetailed(gin.H{"needInit": needInit}, message, c)
|
||||
}
|
||||
33
server/api/v1/system/sys_jwt_blacklist.go
Normal file
33
server/api/v1/system/sys_jwt_blacklist.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type JwtApi struct{}
|
||||
|
||||
// JsonInBlacklist
|
||||
// @Tags Jwt
|
||||
// @Summary jwt加入黑名单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单"
|
||||
// @Router /jwt/jsonInBlacklist [post]
|
||||
func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
|
||||
token := utils.GetToken(c)
|
||||
jwt := system.JwtBlacklist{Jwt: token}
|
||||
err := jwtService.JsonInBlacklist(jwt)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("jwt作废失败!", zap.Error(err))
|
||||
response.FailWithMessage("jwt作废失败", c)
|
||||
return
|
||||
}
|
||||
utils.ClearToken(c)
|
||||
response.OkWithMessage("jwt作废成功", c)
|
||||
}
|
||||
82
server/api/v1/system/sys_login_log.go
Normal file
82
server/api/v1/system/sys_login_log.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type LoginLogApi struct{}
|
||||
|
||||
func (s *LoginLogApi) DeleteLoginLog(c *gin.Context) {
|
||||
var loginLog system.SysLoginLog
|
||||
err := c.ShouldBindJSON(&loginLog)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = loginLogService.DeleteLoginLog(loginLog)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (s *LoginLogApi) DeleteLoginLogByIds(c *gin.Context) {
|
||||
var SDS request.IdsReq
|
||||
err := c.ShouldBindJSON(&SDS)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = loginLogService.DeleteLoginLogByIds(SDS)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
func (s *LoginLogApi) FindLoginLog(c *gin.Context) {
|
||||
var loginLog system.SysLoginLog
|
||||
err := c.ShouldBindQuery(&loginLog)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
reLoginLog, err := loginLogService.GetLoginLog(loginLog.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(reLoginLog, "查询成功", c)
|
||||
}
|
||||
|
||||
func (s *LoginLogApi) GetLoginLogList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysLoginLogSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := loginLogService.GetLoginLogInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
265
server/api/v1/system/sys_menu.go
Normal file
265
server/api/v1/system/sys_menu.go
Normal file
@@ -0,0 +1,265 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthorityMenuApi struct{}
|
||||
|
||||
// GetMenu
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表"
|
||||
// @Router /menu/getMenu [post]
|
||||
func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
|
||||
menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
if menus == nil {
|
||||
menus = []system.SysMenu{}
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetBaseMenuTree
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取用户动态路由
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body request.Empty true "空"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单列表"
|
||||
// @Router /menu/getBaseMenuTree [post]
|
||||
func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) {
|
||||
authority := utils.GetUserAuthorityId(c)
|
||||
menus, err := menuService.GetBaseMenuTree(authority)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// AddMenuAuthority
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 增加menu和角色关联关系
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系"
|
||||
// @Router /menu/addMenuAuthority [post]
|
||||
func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
|
||||
var authorityMenu systemReq.AddMenuAuthorityInfo
|
||||
err := c.ShouldBindJSON(&authorityMenu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
adminAuthorityID := utils.GetUserAuthorityId(c)
|
||||
if err := menuService.AddMenuAuthority(authorityMenu.Menus, adminAuthorityID, authorityMenu.AuthorityId); err != nil {
|
||||
global.GVA_LOG.Error("添加失败!", zap.Error(err))
|
||||
response.FailWithMessage("添加失败", c)
|
||||
} else {
|
||||
response.OkWithMessage("添加成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// GetMenuAuthority
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取指定角色menu
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetAuthorityId true "角色ID"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取指定角色menu"
|
||||
// @Router /menu/getMenuAuthority [post]
|
||||
func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) {
|
||||
var param request.GetAuthorityId
|
||||
err := c.ShouldBindJSON(¶m)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(param, utils.AuthorityIdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
menus, err := menuService.GetMenuAuthority(¶m)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
|
||||
}
|
||||
|
||||
// AddBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 新增菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {object} response.Response{msg=string} "新增菜单"
|
||||
// @Router /menu/addBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
|
||||
var menu system.SysBaseMenu
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(menu, utils.MenuVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = menuService.AddBaseMenu(menu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("添加失败!", zap.Error(err))
|
||||
response.FailWithMessage("添加失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("添加成功", c)
|
||||
}
|
||||
|
||||
// DeleteBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 删除菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除菜单"
|
||||
// @Router /menu/deleteBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
|
||||
var menu request.GetById
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(menu, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = baseMenuService.DeleteBaseMenu(menu.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateBaseMenu
|
||||
// @Tags Menu
|
||||
// @Summary 更新菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新菜单"
|
||||
// @Router /menu/updateBaseMenu [post]
|
||||
func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) {
|
||||
var menu system.SysBaseMenu
|
||||
err := c.ShouldBindJSON(&menu)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(menu, utils.MenuVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(menu.Meta, utils.MenuMetaVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = baseMenuService.UpdateBaseMenu(menu)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// GetBaseMenuById
|
||||
// @Tags Menu
|
||||
// @Summary 根据id获取菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "菜单id"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysBaseMenuResponse,msg=string} "根据id获取菜单,返回包括系统菜单列表"
|
||||
// @Router /menu/getBaseMenuById [post]
|
||||
func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
|
||||
var idInfo request.GetById
|
||||
err := c.ShouldBindJSON(&idInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(idInfo, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
menu, err := baseMenuService.GetBaseMenuById(idInfo.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetMenuList
|
||||
// @Tags Menu
|
||||
// @Summary 分页获取基础menu列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取基础menu列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /menu/getMenuList [post]
|
||||
func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
menuList, err := menuService.GetInfoList(authorityID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(menuList, "获取成功", c)
|
||||
}
|
||||
124
server/api/v1/system/sys_operation_record.go
Normal file
124
server/api/v1/system/sys_operation_record.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type OperationRecordApi struct{}
|
||||
|
||||
// DeleteSysOperationRecord
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysOperationRecord true "SysOperationRecord模型"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除SysOperationRecord"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
|
||||
func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
err := c.ShouldBindJSON(&sysOperationRecord)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = operationRecordService.DeleteSysOperationRecord(sysOperationRecord)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysOperationRecordByIds
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 批量删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除SysOperationRecord"
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除SysOperationRecord"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete]
|
||||
func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
|
||||
var IDS request.IdsReq
|
||||
err := c.ShouldBindJSON(&IDS)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = operationRecordService.DeleteSysOperationRecordByIds(IDS)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// FindSysOperationRecord
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 用id查询SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysOperationRecord true "Id"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysOperationRecord"
|
||||
// @Router /sysOperationRecord/findSysOperationRecord [get]
|
||||
func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
|
||||
var sysOperationRecord system.SysOperationRecord
|
||||
err := c.ShouldBindQuery(&sysOperationRecord)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(sysOperationRecord, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c)
|
||||
}
|
||||
|
||||
// GetSysOperationRecordList
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 分页获取SysOperationRecord列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /sysOperationRecord/getSysOperationRecordList [get]
|
||||
func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysOperationRecordSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
171
server/api/v1/system/sys_params.go
Normal file
171
server/api/v1/system/sys_params.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SysParamsApi struct{}
|
||||
|
||||
// CreateSysParams 创建参数
|
||||
// @Tags SysParams
|
||||
// @Summary 创建参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "创建参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /sysParams/createSysParams [post]
|
||||
func (sysParamsApi *SysParamsApi) CreateSysParams(c *gin.Context) {
|
||||
var sysParams system.SysParams
|
||||
err := c.ShouldBindJSON(&sysParams)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysParamsService.CreateSysParams(&sysParams)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysParams 删除参数
|
||||
// @Tags SysParams
|
||||
// @Summary 删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "删除参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /sysParams/deleteSysParams [delete]
|
||||
func (sysParamsApi *SysParamsApi) DeleteSysParams(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := sysParamsService.DeleteSysParams(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteSysParamsByIds 批量删除参数
|
||||
// @Tags SysParams
|
||||
// @Summary 批量删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /sysParams/deleteSysParamsByIds [delete]
|
||||
func (sysParamsApi *SysParamsApi) DeleteSysParamsByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := sysParamsService.DeleteSysParamsByIds(IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateSysParams 更新参数
|
||||
// @Tags SysParams
|
||||
// @Summary 更新参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysParams true "更新参数"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /sysParams/updateSysParams [put]
|
||||
func (sysParamsApi *SysParamsApi) UpdateSysParams(c *gin.Context) {
|
||||
var sysParams system.SysParams
|
||||
err := c.ShouldBindJSON(&sysParams)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = sysParamsService.UpdateSysParams(sysParams)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindSysParams 用id查询参数
|
||||
// @Tags SysParams
|
||||
// @Summary 用id查询参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query system.SysParams true "用id查询参数"
|
||||
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "查询成功"
|
||||
// @Router /sysParams/findSysParams [get]
|
||||
func (sysParamsApi *SysParamsApi) FindSysParams(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
resysParams, err := sysParamsService.GetSysParams(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(resysParams, c)
|
||||
}
|
||||
|
||||
// GetSysParamsList 分页获取参数列表
|
||||
// @Tags SysParams
|
||||
// @Summary 分页获取参数列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysParamsSearch true "分页获取参数列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /sysParams/getSysParamsList [get]
|
||||
func (sysParamsApi *SysParamsApi) GetSysParamsList(c *gin.Context) {
|
||||
var pageInfo systemReq.SysParamsSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := sysParamsService.GetSysParamsInfoList(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)
|
||||
}
|
||||
|
||||
// GetSysParam 根据key获取参数value
|
||||
// @Tags SysParams
|
||||
// @Summary 根据key获取参数value
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param key query string true "key"
|
||||
// @Success 200 {object} response.Response{data=system.SysParams,msg=string} "获取成功"
|
||||
// @Router /sysParams/getSysParam [get]
|
||||
func (sysParamsApi *SysParamsApi) GetSysParam(c *gin.Context) {
|
||||
k := c.Query("key")
|
||||
params, err := sysParamsService.GetSysParam(k)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(params, "获取成功", c)
|
||||
}
|
||||
219
server/api/v1/system/sys_skills.go
Normal file
219
server/api/v1/system/sys_skills.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SkillsApi struct{}
|
||||
|
||||
func (s *SkillsApi) GetTools(c *gin.Context) {
|
||||
data, err := skillsService.Tools(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取工具列表失败", zap.Error(err))
|
||||
response.FailWithMessage("获取工具列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"tools": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetSkillList(c *gin.Context) {
|
||||
var req request.SkillToolRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
data, err := skillsService.List(c.Request.Context(), req.Tool)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取技能列表失败", zap.Error(err))
|
||||
response.FailWithMessage("获取技能列表失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"skills": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetSkillDetail(c *gin.Context) {
|
||||
var req request.SkillDetailRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
data, err := skillsService.Detail(c.Request.Context(), req.Tool, req.Skill)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取技能详情失败", zap.Error(err))
|
||||
response.FailWithMessage("获取技能详情失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"detail": data}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveSkill(c *gin.Context) {
|
||||
var req request.SkillSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.Save(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存技能失败", zap.Error(err))
|
||||
response.FailWithMessage("保存技能失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateScript(c *gin.Context) {
|
||||
var req request.SkillScriptCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateScript(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("创建脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetScript(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetScript(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("读取脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveScript(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveScript(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存脚本失败", zap.Error(err))
|
||||
response.FailWithMessage("保存脚本失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateResource(c *gin.Context) {
|
||||
var req request.SkillResourceCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateResource(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建资源失败", zap.Error(err))
|
||||
response.FailWithMessage("创建资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetResource(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetResource(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取资源失败", zap.Error(err))
|
||||
response.FailWithMessage("读取资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveResource(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveResource(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存资源失败", zap.Error(err))
|
||||
response.FailWithMessage("保存资源失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateReference(c *gin.Context) {
|
||||
var req request.SkillReferenceCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateReference(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建参考失败", zap.Error(err))
|
||||
response.FailWithMessage("创建参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetReference(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetReference(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取参考失败", zap.Error(err))
|
||||
response.FailWithMessage("读取参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveReference(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveReference(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存参考失败", zap.Error(err))
|
||||
response.FailWithMessage("保存参考失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateTemplate(c *gin.Context) {
|
||||
var req request.SkillTemplateCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
fileName, content, err := skillsService.CreateTemplate(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建模板失败", zap.Error(err))
|
||||
response.FailWithMessage("创建模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"fileName": fileName, "content": content}, "创建成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetTemplate(c *gin.Context) {
|
||||
var req request.SkillFileRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, err := skillsService.GetTemplate(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取模板失败", zap.Error(err))
|
||||
response.FailWithMessage("读取模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveTemplate(c *gin.Context) {
|
||||
var req request.SkillFileSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveTemplate(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存模板失败", zap.Error(err))
|
||||
response.FailWithMessage("保存模板失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) GetGlobalConstraint(c *gin.Context) {
|
||||
var req request.SkillToolRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
content, exists, err := skillsService.GetGlobalConstraint(c.Request.Context(), req.Tool)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("读取全局约束失败", zap.Error(err))
|
||||
response.FailWithMessage("读取全局约束失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"content": content, "exists": exists}, "获取成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) SaveGlobalConstraint(c *gin.Context) {
|
||||
var req request.SkillGlobalConstraintSaveRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.SaveGlobalConstraint(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("保存全局约束失败", zap.Error(err))
|
||||
response.FailWithMessage("保存全局约束失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
89
server/api/v1/system/sys_system.go
Normal file
89
server/api/v1/system/sys_system.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SystemApi struct{}
|
||||
|
||||
// GetSystemConfig
|
||||
// @Tags System
|
||||
// @Summary 获取配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysConfigResponse,msg=string} "获取配置文件内容,返回包括系统配置"
|
||||
// @Router /system/getSystemConfig [post]
|
||||
func (s *SystemApi) GetSystemConfig(c *gin.Context) {
|
||||
config, err := systemConfigService.GetSystemConfig()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c)
|
||||
}
|
||||
|
||||
// SetSystemConfig
|
||||
// @Tags System
|
||||
// @Summary 设置配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body system.System true "设置配置文件内容"
|
||||
// @Success 200 {object} response.Response{data=string} "设置配置文件内容"
|
||||
// @Router /system/setSystemConfig [post]
|
||||
func (s *SystemApi) SetSystemConfig(c *gin.Context) {
|
||||
var sys system.System
|
||||
err := c.ShouldBindJSON(&sys)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = systemConfigService.SetSystemConfig(sys)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// ReloadSystem
|
||||
// @Tags System
|
||||
// @Summary 重载系统
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "重载系统"
|
||||
// @Router /system/reloadSystem [post]
|
||||
func (s *SystemApi) ReloadSystem(c *gin.Context) {
|
||||
// 触发系统重载事件
|
||||
err := utils.GlobalSystemEvents.TriggerReload()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("重载系统失败!", zap.Error(err))
|
||||
response.FailWithMessage("重载系统失败:"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("重载系统成功", c)
|
||||
}
|
||||
|
||||
// GetServerInfo
|
||||
// @Tags System
|
||||
// @Summary 获取服务器信息
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取服务器信息"
|
||||
// @Router /system/getServerInfo [post]
|
||||
func (s *SystemApi) GetServerInfo(c *gin.Context) {
|
||||
server, err := systemConfigService.GetServerInfo()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"server": server}, "获取成功", c)
|
||||
}
|
||||
@@ -1,199 +1,516 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type UserApi struct{}
|
||||
|
||||
var userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||
|
||||
// Login 用户登录
|
||||
// @Tags System
|
||||
// @Summary 用户登录
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.LoginRequest true "用户名, 密码"
|
||||
// @Success 200 {object} response.Response{data=response.LoginResponse} "登录成功"
|
||||
// @Router /v1/system/user/login [post]
|
||||
func (u *UserApi) Login(c *gin.Context) {
|
||||
var req request.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := userService.Login(&req)
|
||||
// Login
|
||||
// @Tags Base
|
||||
// @Summary 用户登录
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Login true "用户名, 密码, 验证码"
|
||||
// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
|
||||
// @Router /base/login [post]
|
||||
func (b *BaseApi) Login(c *gin.Context) {
|
||||
var l systemReq.Login
|
||||
err := c.ShouldBindJSON(&l)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(l, utils.LoginVerify)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("登录失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(resp, c)
|
||||
key := c.ClientIP()
|
||||
// 判断验证码是否开启
|
||||
openCaptcha := global.GVA_CONFIG.Captcha.OpenCaptcha // 是否开启防爆次数
|
||||
openCaptchaTimeOut := global.GVA_CONFIG.Captcha.OpenCaptchaTimeOut // 缓存超时时间
|
||||
v, ok := global.BlackCache.Get(key)
|
||||
if !ok {
|
||||
global.BlackCache.Set(key, 1, time.Second*time.Duration(openCaptchaTimeOut))
|
||||
}
|
||||
|
||||
var oc bool = openCaptcha == 0 || openCaptcha < interfaceToInt(v)
|
||||
if oc && (l.Captcha == "" || l.CaptchaId == "" || !store.Verify(l.CaptchaId, l.Captcha, true)) {
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("验证码错误", c)
|
||||
// 记录登录失败日志
|
||||
loginLogService.CreateLoginLog(system.SysLoginLog{
|
||||
Username: l.Username,
|
||||
Ip: c.ClientIP(),
|
||||
Agent: c.Request.UserAgent(),
|
||||
Status: false,
|
||||
ErrorMessage: "验证码错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
u := &system.SysUser{Username: l.Username, Password: l.Password}
|
||||
user, err := userService.Login(u)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("用户名不存在或者密码错误", c)
|
||||
// 记录登录失败日志
|
||||
loginLogService.CreateLoginLog(system.SysLoginLog{
|
||||
Username: l.Username,
|
||||
Ip: c.ClientIP(),
|
||||
Agent: c.Request.UserAgent(),
|
||||
Status: false,
|
||||
ErrorMessage: "用户名不存在或者密码错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
if user.Enable != 1 {
|
||||
global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
|
||||
// 验证码次数+1
|
||||
global.BlackCache.Increment(key, 1)
|
||||
response.FailWithMessage("用户被禁止登录", c)
|
||||
// 记录登录失败日志
|
||||
loginLogService.CreateLoginLog(system.SysLoginLog{
|
||||
Username: l.Username,
|
||||
Ip: c.ClientIP(),
|
||||
Agent: c.Request.UserAgent(),
|
||||
Status: false,
|
||||
ErrorMessage: "用户被禁止登录",
|
||||
UserID: user.ID,
|
||||
})
|
||||
return
|
||||
}
|
||||
b.TokenNext(c, *user)
|
||||
}
|
||||
|
||||
// Register 用户注册
|
||||
// @Tags System
|
||||
// @Summary 用户注册
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.RegisterRequest true "用户信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "注册成功"
|
||||
// @Router /v1/system/user/register [post]
|
||||
func (u *UserApi) Register(c *gin.Context) {
|
||||
var req request.RegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
// TokenNext 登录以后签发jwt
|
||||
func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
|
||||
token, claims, err := utils.LoginToken(&user)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取token失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取token失败", c)
|
||||
return
|
||||
}
|
||||
// 记录登录成功日志
|
||||
loginLogService.CreateLoginLog(system.SysLoginLog{
|
||||
Username: user.Username,
|
||||
Ip: c.ClientIP(),
|
||||
Agent: c.Request.UserAgent(),
|
||||
Status: true,
|
||||
UserID: user.ID,
|
||||
ErrorMessage: "登录成功",
|
||||
})
|
||||
if !global.GVA_CONFIG.System.UseMultipoint {
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := userService.Register(&req)
|
||||
if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
|
||||
if err := utils.SetRedisJWT(token, user.Username); err != nil {
|
||||
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置登录状态失败", c)
|
||||
return
|
||||
}
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
} else if err != nil {
|
||||
global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置登录状态失败", c)
|
||||
} else {
|
||||
var blackJWT system.JwtBlacklist
|
||||
blackJWT.Jwt = jwtStr
|
||||
if err := jwtService.JsonInBlacklist(blackJWT); err != nil {
|
||||
response.FailWithMessage("jwt作废失败", c)
|
||||
return
|
||||
}
|
||||
if err := utils.SetRedisJWT(token, user.GetUsername()); err != nil {
|
||||
response.FailWithMessage("设置登录状态失败", c)
|
||||
return
|
||||
}
|
||||
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithDetailed(systemRes.LoginResponse{
|
||||
User: user,
|
||||
Token: token,
|
||||
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||
}, "登录成功", c)
|
||||
}
|
||||
}
|
||||
|
||||
// Register
|
||||
// @Tags SysUser
|
||||
// @Summary 用户注册账号
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID"
|
||||
// @Success 200 {object} response.Response{data=systemRes.SysUserResponse,msg=string} "用户注册账号,返回包括用户信息"
|
||||
// @Router /user/admin_register [post]
|
||||
func (b *BaseApi) Register(c *gin.Context) {
|
||||
var r systemReq.Register
|
||||
err := c.ShouldBindJSON(&r)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(r, utils.RegisterVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
var authorities []system.SysAuthority
|
||||
for _, v := range r.AuthorityIds {
|
||||
authorities = append(authorities, system.SysAuthority{
|
||||
AuthorityId: v,
|
||||
})
|
||||
}
|
||||
user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable, Phone: r.Phone, Email: r.Email}
|
||||
userReturn, err := userService.Register(*user)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("注册失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("注册成功", c)
|
||||
response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户信息
|
||||
// @Tags System
|
||||
// @Summary 获取用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=response.UserInfo} "获取成功"
|
||||
// @Router /v1/system/user/info [get]
|
||||
func (u *UserApi) GetUserInfo(c *gin.Context) {
|
||||
userID := utils.GetUserID(c)
|
||||
|
||||
info, err := userService.GetUserInfo(userID)
|
||||
// ChangePassword
|
||||
// @Tags SysUser
|
||||
// @Summary 用户修改密码
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.ChangePasswordReq true "用户名, 原密码, 新密码"
|
||||
// @Success 200 {object} response.Response{msg=string} "用户修改密码"
|
||||
// @Router /user/changePassword [post]
|
||||
func (b *BaseApi) ChangePassword(c *gin.Context) {
|
||||
var req systemReq.ChangePasswordReq
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(info, c)
|
||||
}
|
||||
|
||||
// GetUserList 获取用户列表
|
||||
// @Tags System
|
||||
// @Summary 获取用户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param page query int false "页码"
|
||||
// @Param pageSize query int false "每页数量"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult} "获取成功"
|
||||
// @Router /v1/system/user/list [get]
|
||||
func (u *UserApi) GetUserList(c *gin.Context) {
|
||||
page := utils.GetIntQuery(c, "page", 1)
|
||||
pageSize := utils.GetIntQuery(c, "pageSize", 10)
|
||||
|
||||
list, total, err := userService.GetUserList(page, pageSize)
|
||||
err = utils.Verify(req, utils.ChangePasswordVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
uid := utils.GetUserID(c)
|
||||
u := &system.SysUser{GVA_MODEL: global.GVA_MODEL{ID: uid}, Password: req.Password}
|
||||
err = userService.ChangePassword(u, req.NewPassword)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// GetUserList
|
||||
// @Tags SysUser
|
||||
// @Summary 分页获取用户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.GetUserList true "页码, 每页大小"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取用户列表,返回包括列表,总数,页码,每页数量"
|
||||
// @Router /user/getUserList [post]
|
||||
func (b *BaseApi) GetUserList(c *gin.Context) {
|
||||
var pageInfo systemReq.GetUserList
|
||||
err := c.ShouldBindJSON(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(pageInfo, utils.PageInfoVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := userService.GetUserInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
// @Tags System
|
||||
// @Summary 更新用户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.UpdateUserRequest true "用户信息"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /v1/system/user [put]
|
||||
func (u *UserApi) UpdateUser(c *gin.Context) {
|
||||
var req request.UpdateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err := userService.UpdateUser(&req)
|
||||
// SetUserAuthority
|
||||
// @Tags SysUser
|
||||
// @Summary 更改用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
|
||||
// @Router /user/setUserAuthority [post]
|
||||
func (b *BaseApi) SetUserAuthority(c *gin.Context) {
|
||||
var sua systemReq.SetUserAuth
|
||||
err := c.ShouldBindJSON(&sua)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMessage("更新成功", c)
|
||||
if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil {
|
||||
response.FailWithMessage(UserVerifyErr.Error(), c)
|
||||
return
|
||||
}
|
||||
userID := utils.GetUserID(c)
|
||||
err = userService.SetUserAuthority(userID, sua.AuthorityId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
claims := utils.GetUserInfo(c)
|
||||
claims.AuthorityId = sua.AuthorityId
|
||||
token, err := utils.NewJWT().CreateToken(*claims)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
c.Header("new-token", token)
|
||||
c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt.Unix(), 10))
|
||||
utils.SetToken(c, token, int(claims.ExpiresAt.Unix()-time.Now().Unix()))
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
// @Tags System
|
||||
// @Summary 删除用户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id path uint true "用户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /v1/system/user/:id [delete]
|
||||
func (u *UserApi) DeleteUser(c *gin.Context) {
|
||||
userID := utils.GetUintParam(c, "id")
|
||||
|
||||
err := userService.DeleteUser(userID)
|
||||
// SetUserAuthorities
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置用户权限"
|
||||
// @Router /user/setUserAuthorities [post]
|
||||
func (b *BaseApi) SetUserAuthorities(c *gin.Context) {
|
||||
var sua systemReq.SetUserAuthorities
|
||||
err := c.ShouldBindJSON(&sua)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
err = userService.SetUserAuthorities(authorityID, sua.ID, sua.AuthorityIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("修改失败!", zap.Error(err))
|
||||
response.FailWithMessage("修改失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("修改成功", c)
|
||||
}
|
||||
|
||||
// DeleteUser
|
||||
// @Tags SysUser
|
||||
// @Summary 删除用户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.GetById true "用户ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除用户"
|
||||
// @Router /user/deleteUser [delete]
|
||||
func (b *BaseApi) DeleteUser(c *gin.Context) {
|
||||
var reqId request.GetById
|
||||
err := c.ShouldBindJSON(&reqId)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(reqId, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
jwtId := utils.GetUserID(c)
|
||||
if jwtId == uint(reqId.ID) {
|
||||
response.FailWithMessage("删除失败, 无法删除自己。", c)
|
||||
return
|
||||
}
|
||||
err = userService.DeleteUser(reqId.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// GetAPIKey 获取API密钥
|
||||
// @Tags System
|
||||
// @Summary 获取API密钥
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=string} "获取成功"
|
||||
// @Router /v1/system/user/apikey [get]
|
||||
func (u *UserApi) GetAPIKey(c *gin.Context) {
|
||||
userID := utils.GetUserID(c)
|
||||
// SetUserInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
|
||||
// @Router /user/setUserInfo [put]
|
||||
func (b *BaseApi) SetUserInfo(c *gin.Context) {
|
||||
var user systemReq.ChangeUserInfo
|
||||
err := c.ShouldBindJSON(&user)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = utils.Verify(user, utils.IdVerify)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if len(user.AuthorityIds) != 0 {
|
||||
authorityID := utils.GetUserAuthorityId(c)
|
||||
err = userService.SetUserAuthorities(authorityID, user.ID, user.AuthorityIds)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = userService.SetUserInfo(system.SysUser{
|
||||
GVA_MODEL: global.GVA_MODEL{
|
||||
ID: user.ID,
|
||||
},
|
||||
NickName: user.NickName,
|
||||
HeaderImg: user.HeaderImg,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Enable: user.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
apiKey, err := userService.GetAPIKey(userID)
|
||||
// SetSelfInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
|
||||
// @Router /user/SetSelfInfo [put]
|
||||
func (b *BaseApi) SetSelfInfo(c *gin.Context) {
|
||||
var user systemReq.ChangeUserInfo
|
||||
err := c.ShouldBindJSON(&user)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
user.ID = utils.GetUserID(c)
|
||||
err = userService.SetSelfInfo(system.SysUser{
|
||||
GVA_MODEL: global.GVA_MODEL{
|
||||
ID: user.ID,
|
||||
},
|
||||
NickName: user.NickName,
|
||||
HeaderImg: user.HeaderImg,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Enable: user.Enable,
|
||||
})
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// SetSelfSetting
|
||||
// @Tags SysUser
|
||||
// @Summary 设置用户配置
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body map[string]interface{} true "用户配置数据"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户配置"
|
||||
// @Router /user/SetSelfSetting [put]
|
||||
func (b *BaseApi) SetSelfSetting(c *gin.Context) {
|
||||
var req common.JSONMap
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(gin.H{"apiKey": apiKey}, c)
|
||||
err = userService.SetSelfSetting(req, utils.GetUserID(c))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// RegenerateAPIKey 重新生成API密钥
|
||||
// @Tags System
|
||||
// @Summary 重新生成API密钥
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=string} "生成成功"
|
||||
// @Router /v1/system/user/apikey/regenerate [post]
|
||||
func (u *UserApi) RegenerateAPIKey(c *gin.Context) {
|
||||
userID := utils.GetUserID(c)
|
||||
// GetUserInfo
|
||||
// @Tags SysUser
|
||||
// @Summary 获取用户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取用户信息"
|
||||
// @Router /user/getUserInfo [get]
|
||||
func (b *BaseApi) GetUserInfo(c *gin.Context) {
|
||||
uuid := utils.GetUserUuid(c)
|
||||
ReqUser, err := userService.GetUserInfo(uuid)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
|
||||
}
|
||||
|
||||
apiKey, err := userService.RegenerateAPIKey(userID)
|
||||
// ResetPassword
|
||||
// @Tags SysUser
|
||||
// @Summary 重置用户密码
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body system.SysUser true "ID"
|
||||
// @Success 200 {object} response.Response{msg=string} "重置用户密码"
|
||||
// @Router /user/resetPassword [post]
|
||||
func (b *BaseApi) ResetPassword(c *gin.Context) {
|
||||
var rps systemReq.ResetPassword
|
||||
err := c.ShouldBindJSON(&rps)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithData(gin.H{"apiKey": apiKey}, c)
|
||||
err = userService.ResetPassword(rps.ID, rps.Password)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("重置失败!", zap.Error(err))
|
||||
response.FailWithMessage("重置失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("重置成功", c)
|
||||
}
|
||||
|
||||
486
server/api/v1/system/sys_version.go
Normal file
486
server/api/v1/system/sys_version.go
Normal file
@@ -0,0 +1,486 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
systemReq "git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/ai_proxy/server/model/system/response"
|
||||
"git.echol.cn/loser/ai_proxy/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)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
aliyun-oss:
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
access-key-id: LTAI5tB3Mn5Y7mVo8h3zkf46
|
||||
access-key-secret: FtuHdFy4NFdVItEiNBnTun3Ddi8BMK
|
||||
bucket-name: lckt
|
||||
bucket-url: https://lckt.oss-cn-hangzhou.aliyuncs.com
|
||||
base-path: st
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
access-key-id: LTAI5tB3Mn5Y7mVo8h3zkf46
|
||||
access-key-secret: FtuHdFy4NFdVItEiNBnTun3Ddi8BMK
|
||||
bucket-name: lckt
|
||||
bucket-url: https://lckt.oss-cn-hangzhou.aliyuncs.com
|
||||
base-path: st
|
||||
autocode:
|
||||
web: web/src
|
||||
root: /Users/en/GolandProjects/st
|
||||
@@ -180,19 +180,19 @@ oracle:
|
||||
# singular: false
|
||||
# log-zap: false
|
||||
pgsql:
|
||||
prefix: ""
|
||||
port: "5432"
|
||||
config: sslmode=disable TimeZone=Asia/Shanghai
|
||||
db-name: ai_proxy
|
||||
username: postgres
|
||||
password: e5zse3Adrja7PNfA
|
||||
path: 219.152.55.29
|
||||
engine: ""
|
||||
log-mode: error
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: true
|
||||
prefix: ""
|
||||
port: "5432"
|
||||
config: sslmode=disable TimeZone=Asia/Shanghai
|
||||
db-name: ai_proxy2
|
||||
username: postgres
|
||||
password: e5zse3Adrja7PNfA
|
||||
path: 219.152.55.29
|
||||
engine: ""
|
||||
log-mode: error
|
||||
max-idle-conns: 10
|
||||
max-open-conns: 100
|
||||
singular: false
|
||||
log-zap: true
|
||||
qiniu:
|
||||
zone: ZoneHuaDong
|
||||
bucket: ""
|
||||
@@ -205,7 +205,7 @@ redis:
|
||||
name: "sys-cache"
|
||||
addr: 219.152.55.29:6379
|
||||
password: "THBA@6688"
|
||||
db: 7
|
||||
db: 12
|
||||
useCluster: false
|
||||
clusterAddrs:
|
||||
- 172.21.0.3:7000
|
||||
@@ -215,12 +215,12 @@ redis-list:
|
||||
- name: app-cache
|
||||
addr: 219.152.55.29:6379
|
||||
password: "THBA@6688"
|
||||
db: 6
|
||||
db: 11
|
||||
useCluster: false
|
||||
clusterAddrs:
|
||||
- 172.21.0.3:7000
|
||||
- 172.21.0.4:7001
|
||||
- 172.21.0.2:7002
|
||||
- 172.21.0.3:7000
|
||||
- 172.21.0.4:7001
|
||||
- 172.21.0.2:7002
|
||||
sqlite:
|
||||
prefix: ""
|
||||
port: ""
|
||||
@@ -239,7 +239,7 @@ system:
|
||||
db-type: pgsql
|
||||
oss-type: aliyun-oss
|
||||
router-prefix: ""
|
||||
addr: 8889
|
||||
addr: 8989
|
||||
iplimit-count: 15000
|
||||
iplimit-time: 3600
|
||||
use-multipoint: false
|
||||
|
||||
@@ -12,5 +12,4 @@ type System struct {
|
||||
UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo
|
||||
UseStrictAuth bool `mapstructure:"use-strict-auth" json:"use-strict-auth" yaml:"use-strict-auth"` // 使用树形角色分配模式
|
||||
DisableAutoMigrate bool `mapstructure:"disable-auto-migrate" json:"disable-auto-migrate" yaml:"disable-auto-migrate"` // 自动迁移数据库表结构,生产环境建议设为false,手动迁移
|
||||
DataDir string `mapstructure:"data-dir" json:"data-dir" yaml:"data-dir"` // 数据目录
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Zap struct {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
astutil "git.echol.cn/loser/ai_proxy/server/utils/ast"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils/stacktrace"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type ZapCore struct {
|
||||
@@ -65,12 +70,60 @@ func (z *ZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||
// 先写入原日志目标
|
||||
err := z.Core.Write(entry, fields)
|
||||
|
||||
// 捕捉 Error 及以上级别日志(简化版本,仅记录到日志文件)
|
||||
// 捕捉 Error 及以上级别日志并入库,且可提取 zap.Error(err) 的错误内容
|
||||
if entry.Level >= zapcore.ErrorLevel {
|
||||
// 避免与 GORM zap 写入互相递归:跳过由 gorm logger writer 触发的日志
|
||||
if strings.Contains(entry.Caller.File, "gorm_logger_writer.go") {
|
||||
return err
|
||||
}
|
||||
|
||||
form := "后端"
|
||||
level := entry.Level.String()
|
||||
// 生成基础信息
|
||||
info := entry.Message
|
||||
|
||||
// 提取 zap.Error(err) 内容
|
||||
var errStr string
|
||||
for i := 0; i < len(fields); i++ {
|
||||
f := fields[i]
|
||||
if f.Type == zapcore.ErrorType || f.Key == "error" || f.Key == "err" {
|
||||
if f.Interface != nil {
|
||||
errStr = fmt.Sprintf("%v", f.Interface)
|
||||
} else if f.String != "" {
|
||||
errStr = f.String
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if errStr != "" {
|
||||
info = fmt.Sprintf("%s | 错误: %s", info, errStr)
|
||||
}
|
||||
|
||||
// 附加来源与堆栈信息
|
||||
if entry.Caller.File != "" {
|
||||
info = fmt.Sprintf("%s \n 源文件:%s:%d", info, entry.Caller.File, entry.Caller.Line)
|
||||
}
|
||||
stack := entry.Stack
|
||||
if stack != "" {
|
||||
info = fmt.Sprintf("%s \n 调用栈:%s", info, stack)
|
||||
// 解析最终业务调用方,并提取其方法源码
|
||||
if frame, ok := stacktrace.FindFinalCaller(stack); ok {
|
||||
fnName, fnSrc, sLine, eLine, exErr := astutil.ExtractFuncSourceByPosition(frame.File, frame.Line)
|
||||
if exErr == nil {
|
||||
info = fmt.Sprintf("%s \n 最终调用方法:%s:%d (%s lines %d-%d)\n----- 产生日志的方法代码如下 -----\n%s", info, frame.File, frame.Line, fnName, sLine, eLine, fnSrc)
|
||||
} else {
|
||||
info = fmt.Sprintf("%s \n 最终调用方法:%s:%d (%s) | extract_err=%v", info, frame.File, frame.Line, fnName, exErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用后台上下文,避免依赖 gin.Context
|
||||
ctx := context.Background()
|
||||
_ = service.ServiceGroupApp.SystemServiceGroup.SysErrorService.CreateSysError(ctx, &system.SysError{
|
||||
Form: &form,
|
||||
Info: &info,
|
||||
Level: level,
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/initialize"
|
||||
"git.echol.cn/loser/ai_proxy/server/service/system"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -24,16 +25,21 @@ func RunServer() {
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
// 从db加载jwt数据
|
||||
if global.GVA_DB != nil {
|
||||
system.LoadAll()
|
||||
}
|
||||
|
||||
Router := initialize.Routers()
|
||||
|
||||
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
|
||||
|
||||
fmt.Printf(`
|
||||
当前版本:%s
|
||||
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||
默认MCP SSE地址:http://127.0.0.1%s%s
|
||||
默认MCP Message地址:http://127.0.0.1%s%s
|
||||
默认前端文件运行地址:http://127.0.0.1:8080
|
||||
`, address, address, global.GVA_CONFIG.MCP.SSEPath, address, global.GVA_CONFIG.MCP.MessagePath)
|
||||
`, global.Version, address, address, global.GVA_CONFIG.MCP.SSEPath, address, global.GVA_CONFIG.MCP.MessagePath)
|
||||
initServer(address, Router, 10*time.Minute, 10*time.Minute)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ package global
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/qiniu/qmgo"
|
||||
|
||||
@@ -14,12 +14,17 @@ require (
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/goccy/go-json v0.10.4
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/mark3labs/mcp-go v0.41.1
|
||||
github.com/mholt/archives v0.1.1
|
||||
github.com/minio/minio-go/v7 v7.0.84
|
||||
github.com/mojocn/base64Captcha v1.3.8
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2
|
||||
github.com/qiniu/qmgo v1.1.9
|
||||
@@ -34,12 +39,14 @@ require (
|
||||
github.com/swaggo/swag v1.16.4
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.60
|
||||
github.com/unrolled/secure v1.17.0
|
||||
github.com/xuri/excelize/v2 v2.9.0
|
||||
go.mongodb.org/mongo-driver v1.17.2
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/text v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/driver/sqlserver v1.5.4
|
||||
@@ -50,9 +57,14 @@ require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.7 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
@@ -62,6 +74,7 @@ require (
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
@@ -77,13 +90,15 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
@@ -97,56 +112,70 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.8.0 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/mozillazg/go-httpheader v0.4.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.4 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.7.17 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/thoas/go-funk v0.7.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 // indirect
|
||||
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/image v0.23.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/plugin/dbresolver v1.5.3 // indirect
|
||||
modernc.org/fileutil v1.3.0 // indirect
|
||||
modernc.org/libc v1.61.9 // indirect
|
||||
|
||||
283
server/go.sum
283
server/go.sum
@@ -1,3 +1,20 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
@@ -21,16 +38,22 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
|
||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
|
||||
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
@@ -38,6 +61,12 @@ github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xW
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
@@ -55,10 +84,15 @@ github.com/casbin/gorm-adapter/v3 v3.32.0 h1:Au+IOILBIE9clox5BJhI2nA3p9t7Ep1ePlu
|
||||
github.com/casbin/gorm-adapter/v3 v3.32.0/go.mod h1:Zre/H8p17mpv5U3EaWgPoxLILLdXO3gHW5aoQQpUDZI=
|
||||
github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc=
|
||||
github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
@@ -72,12 +106,17 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dzwvip/gorm-oracle v0.1.2 h1:811aFDY7oDfKWHc0Z0lHdXzzr89EmKBSwc/jLJ8GU5g=
|
||||
github.com/dzwvip/gorm-oracle v0.1.2/go.mod h1:TbF7idnO9UgGpJ0qJpDZby1/wGquzP5GYof88ScBITE=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
@@ -96,6 +135,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
@@ -141,36 +182,75 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible h1:XQVXdk+WAJ4fSNB6mMRuYNvFWou7BZs6SZB925hPrnk=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
@@ -203,13 +283,20 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -226,8 +313,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
@@ -238,6 +325,8 @@ github.com/mark3labs/mcp-go v0.41.1 h1:w78eWfiQam2i8ICL7AL0WFiq7KHNJQ6UB53ZVtH4K
|
||||
github.com/mark3labs/mcp-go v0.41.1/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY=
|
||||
github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
|
||||
github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
|
||||
@@ -245,6 +334,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E=
|
||||
github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
|
||||
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
|
||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
@@ -254,6 +345,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
|
||||
github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4=
|
||||
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
@@ -262,8 +357,16 @@ github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiY
|
||||
github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
@@ -277,6 +380,7 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2 h1:URwgZpxySdiwu2yQpHk93X4LXWHyFRp1x3Vmlk/YWvo=
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o=
|
||||
@@ -287,14 +391,21 @@ github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
|
||||
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
@@ -309,6 +420,8 @@ github.com/sijms/go-ora/v2 v2.7.17 h1:M/pYIqjaMUeBxyzOWp2oj4ntF6fHSBloJWGNH9vbms
|
||||
github.com/sijms/go-ora/v2 v2.7.17/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/songzhibin97/gkit v1.2.13 h1:paY0XJkdRuy9/8k9nTnbdrzo8pC22jIIFldUkOQv5nU=
|
||||
github.com/songzhibin97/gkit v1.2.13/go.mod h1:38CreNR27eTGaG1UMGihrXqI4xc3nGfYxLVKKVx6Ngg=
|
||||
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
|
||||
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
@@ -346,6 +459,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.60 h1:/e/tmvRmfKexr/QQIBzWhOkZWsmY3EK72NrI6G/Tv0o=
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.60/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y=
|
||||
github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
@@ -356,6 +471,9 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
|
||||
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
@@ -366,6 +484,16 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
|
||||
github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE=
|
||||
github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE=
|
||||
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 h1:hOh7aVDrvGJRxzXrQbDY8E+02oaI//5cHL+97oYpEPw=
|
||||
github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
@@ -376,6 +504,10 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
|
||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
|
||||
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -384,9 +516,14 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
|
||||
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
@@ -403,19 +540,56 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
@@ -430,20 +604,41 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -482,7 +677,9 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -497,21 +694,79 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -543,6 +798,11 @@ gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
||||
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00=
|
||||
@@ -569,3 +829,6 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
112
server/initialize/ensure_tables.go
Normal file
112
server/initialize/ensure_tables.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
sysModel "git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service/system"
|
||||
adapter "github.com/casbin/gorm-adapter/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const initOrderEnsureTables = system.InitOrderExternal - 1
|
||||
|
||||
type ensureTables struct{}
|
||||
|
||||
// auto run
|
||||
func init() {
|
||||
system.RegisterInit(initOrderEnsureTables, &ensureTables{})
|
||||
}
|
||||
|
||||
func (e *ensureTables) InitializerName() string {
|
||||
return "ensure_tables_created"
|
||||
}
|
||||
func (e *ensureTables) InitializeData(ctx context.Context) (next context.Context, err error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (e *ensureTables) DataInserted(ctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error) {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return ctx, system.ErrMissingDBContext
|
||||
}
|
||||
tables := []interface{}{
|
||||
sysModel.SysApi{},
|
||||
sysModel.SysUser{},
|
||||
sysModel.SysBaseMenu{},
|
||||
sysModel.SysAuthority{},
|
||||
sysModel.JwtBlacklist{},
|
||||
sysModel.SysDictionary{},
|
||||
sysModel.SysAutoCodeHistory{},
|
||||
sysModel.SysOperationRecord{},
|
||||
sysModel.SysDictionaryDetail{},
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysAutoCodePackage{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
sysModel.SysParams{},
|
||||
sysModel.SysVersion{},
|
||||
sysModel.SysError{},
|
||||
sysModel.SysLoginLog{},
|
||||
sysModel.SysApiToken{},
|
||||
adapter.CasbinRule{},
|
||||
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
}
|
||||
for _, t := range tables {
|
||||
_ = db.AutoMigrate(&t)
|
||||
// 视图 authority_menu 会被当成表来创建,引发冲突错误(更新版本的gorm似乎不会)
|
||||
// 由于 AutoMigrate() 基本无需考虑错误,因此显式忽略
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (e *ensureTables) TableCreated(ctx context.Context) bool {
|
||||
db, ok := ctx.Value("db").(*gorm.DB)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
tables := []interface{}{
|
||||
sysModel.SysApi{},
|
||||
sysModel.SysUser{},
|
||||
sysModel.SysBaseMenu{},
|
||||
sysModel.SysAuthority{},
|
||||
sysModel.JwtBlacklist{},
|
||||
sysModel.SysDictionary{},
|
||||
sysModel.SysAutoCodeHistory{},
|
||||
sysModel.SysOperationRecord{},
|
||||
sysModel.SysDictionaryDetail{},
|
||||
sysModel.SysBaseMenuParameter{},
|
||||
sysModel.SysBaseMenuBtn{},
|
||||
sysModel.SysAuthorityBtn{},
|
||||
sysModel.SysAutoCodePackage{},
|
||||
sysModel.SysExportTemplate{},
|
||||
sysModel.Condition{},
|
||||
sysModel.JoinTemplate{},
|
||||
|
||||
adapter.CasbinRule{},
|
||||
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
}
|
||||
yes := true
|
||||
for _, t := range tables {
|
||||
yes = yes && db.Migrator().HasTable(t)
|
||||
}
|
||||
return yes
|
||||
}
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"os"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/example"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -44,15 +43,34 @@ func RegisterTables() {
|
||||
db := global.GVA_DB
|
||||
err := db.AutoMigrate(
|
||||
|
||||
// System tables (管理后台表)
|
||||
system.SysApi{},
|
||||
system.SysIgnoreApi{},
|
||||
system.SysUser{},
|
||||
system.SysBaseMenu{},
|
||||
system.JwtBlacklist{},
|
||||
system.SysAuthority{},
|
||||
system.SysDictionary{},
|
||||
system.SysOperationRecord{},
|
||||
system.SysAutoCodeHistory{},
|
||||
system.SysDictionaryDetail{},
|
||||
system.SysBaseMenuParameter{},
|
||||
system.SysBaseMenuBtn{},
|
||||
system.SysAuthorityBtn{},
|
||||
system.SysAutoCodePackage{},
|
||||
system.SysExportTemplate{},
|
||||
system.Condition{},
|
||||
system.JoinTemplate{},
|
||||
system.SysParams{},
|
||||
system.SysVersion{},
|
||||
system.SysError{},
|
||||
system.SysApiToken{},
|
||||
system.SysLoginLog{},
|
||||
|
||||
// App tables (前台应用表)
|
||||
app.AiPreset{},
|
||||
app.AiProvider{},
|
||||
app.AiPresetBinding{},
|
||||
app.AiRequestLog{},
|
||||
example.ExaFile{},
|
||||
example.ExaCustomer{},
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("register table failed", zap.Error(err))
|
||||
@@ -66,47 +84,4 @@ func RegisterTables() {
|
||||
os.Exit(0)
|
||||
}
|
||||
global.GVA_LOG.Info("register table success")
|
||||
|
||||
// 初始化默认管理员账号
|
||||
initDefaultAdmin()
|
||||
}
|
||||
|
||||
// initDefaultAdmin 初始化默认管理员账号
|
||||
func initDefaultAdmin() {
|
||||
var count int64
|
||||
err := global.GVA_DB.Model(&system.SysUser{}).Where("username = ?", "root").Count(&count).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("check admin user failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果已存在 root 用户,则跳过
|
||||
if count > 0 {
|
||||
global.GVA_LOG.Info("default admin user already exists, skip initialization")
|
||||
return
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("root123"), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("hash password failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建默认管理员账号
|
||||
adminUser := system.SysUser{
|
||||
Username: "root",
|
||||
Password: string(hashedPassword),
|
||||
Nickname: "超级管理员",
|
||||
Email: "admin@example.com",
|
||||
Role: "admin",
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
if err := global.GVA_DB.Create(&adminUser).Error; err != nil {
|
||||
global.GVA_LOG.Error("create default admin user failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
global.GVA_LOG.Info("default admin user created successfully (username: root, password: root123)")
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package initialize
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/app"
|
||||
)
|
||||
|
||||
func bizModel() error {
|
||||
db := global.GVA_DB
|
||||
err := db.AutoMigrate()
|
||||
err := db.AutoMigrate(app.AutoMigrateTables...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/config"
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
@@ -3,7 +3,6 @@ package internal
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/qiniu/qmgo/options"
|
||||
"go.mongodb.org/mongo-driver/event"
|
||||
opt "go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
25
server/initialize/mcp.go
Normal file
25
server/initialize/mcp.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
mcpTool "git.echol.cn/loser/ai_proxy/server/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
func McpRun() *server.SSEServer {
|
||||
config := global.GVA_CONFIG.MCP
|
||||
|
||||
s := server.NewMCPServer(
|
||||
config.Name,
|
||||
config.Version,
|
||||
)
|
||||
|
||||
global.GVA_MCP_SERVER = s
|
||||
|
||||
mcpTool.RegisterAllTools(s)
|
||||
|
||||
return server.NewSSEServer(s,
|
||||
server.WithSSEEndpoint(config.SSEPath),
|
||||
server.WithMessageEndpoint(config.MessagePath),
|
||||
server.WithBaseURL(config.UrlPrefix))
|
||||
}
|
||||
@@ -3,9 +3,6 @@ package initialize
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/initialize/internal"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
@@ -14,6 +11,8 @@ import (
|
||||
"github.com/qiniu/qmgo/options"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
option "go.mongodb.org/mongo-driver/mongo/options"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Mongo = new(mongo)
|
||||
|
||||
@@ -2,11 +2,10 @@ package initialize
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/songzhibin97/gkit/cache/local_cache"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/songzhibin97/gkit/cache/local_cache"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
)
|
||||
|
||||
15
server/initialize/plugin.go
Normal file
15
server/initialize/plugin.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InstallPlugin(PrivateGroup *gin.RouterGroup, PublicRouter *gin.RouterGroup, engine *gin.Engine) {
|
||||
if global.GVA_DB == nil {
|
||||
global.GVA_LOG.Info("项目暂未初始化,无法安装插件,初始化后重启项目即可完成插件安装")
|
||||
return
|
||||
}
|
||||
bizPluginV1(PrivateGroup, PublicRouter)
|
||||
bizPluginV2(engine)
|
||||
}
|
||||
36
server/initialize/plugin_biz_v1.go
Normal file
36
server/initialize/plugin_biz_v1.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/plugin/email"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils/plugin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {
|
||||
for i := range Plugin {
|
||||
fmt.Println(Plugin[i].RouterPath(), "注册开始!")
|
||||
PluginGroup := group.Group(Plugin[i].RouterPath())
|
||||
Plugin[i].Register(PluginGroup)
|
||||
fmt.Println(Plugin[i].RouterPath(), "注册成功!")
|
||||
}
|
||||
}
|
||||
|
||||
func bizPluginV1(group ...*gin.RouterGroup) {
|
||||
private := group[0]
|
||||
public := group[1]
|
||||
// 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
|
||||
PluginInit(private, email.CreateEmailPlug(
|
||||
global.GVA_CONFIG.Email.To,
|
||||
global.GVA_CONFIG.Email.From,
|
||||
global.GVA_CONFIG.Email.Host,
|
||||
global.GVA_CONFIG.Email.Secret,
|
||||
global.GVA_CONFIG.Email.Nickname,
|
||||
global.GVA_CONFIG.Email.Port,
|
||||
global.GVA_CONFIG.Email.IsSSL,
|
||||
global.GVA_CONFIG.Email.IsLoginAuth,
|
||||
))
|
||||
holder(public, private)
|
||||
}
|
||||
16
server/initialize/plugin_biz_v2.go
Normal file
16
server/initialize/plugin_biz_v2.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
_ "git.echol.cn/loser/ai_proxy/server/plugin"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils/plugin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PluginInitV2(group *gin.Engine, plugins ...plugin.Plugin) {
|
||||
for i := 0; i < len(plugins); i++ {
|
||||
plugins[i].Register(group)
|
||||
}
|
||||
}
|
||||
func bizPluginV2(engine *gin.Engine) {
|
||||
PluginInitV2(engine, plugin.Registered()...)
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
_ "git.echol.cn/loser/ai_proxy/server/source/example"
|
||||
_ "git.echol.cn/loser/ai_proxy/server/source/system"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// do nothing, only import source package so that inits can be registered
|
||||
// do nothing,only import source package so that inits can be registered
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/docs"
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/middleware"
|
||||
"git.echol.cn/loser/ai_proxy/server/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
@@ -30,77 +31,101 @@ func (fs justFilesFilesystem) Open(name string) (http.File, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Routers 初始化总路由
|
||||
// 初始化总路由
|
||||
|
||||
func Routers() *gin.Engine {
|
||||
Router := gin.New()
|
||||
|
||||
// 设置文件上传大小限制(10MB)
|
||||
Router.MaxMultipartMemory = 10 << 20
|
||||
|
||||
// 使用 Gin 默认的 Recovery 中间件
|
||||
Router.Use(gin.Recovery())
|
||||
// 使用自定义的 Recovery 中间件,记录 panic 并入库
|
||||
Router.Use(middleware.GinRecovery(true))
|
||||
if gin.Mode() == gin.DebugMode {
|
||||
Router.Use(gin.Logger())
|
||||
}
|
||||
|
||||
// 简单的 CORS 中间件
|
||||
Router.Use(func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, x-token")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||
if !global.GVA_CONFIG.MCP.Separate {
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
sseServer := McpRun()
|
||||
|
||||
c.Next()
|
||||
})
|
||||
// 注册mcp服务
|
||||
Router.GET(global.GVA_CONFIG.MCP.SSEPath, func(c *gin.Context) {
|
||||
sseServer.SSEHandler().ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
|
||||
Router.POST(global.GVA_CONFIG.MCP.MessagePath, func(c *gin.Context) {
|
||||
sseServer.MessageHandler().ServeHTTP(c.Writer, c.Request)
|
||||
})
|
||||
}
|
||||
|
||||
systemRouter := router.RouterGroupApp.System
|
||||
appRouter := router.RouterGroupApp.App
|
||||
exampleRouter := router.RouterGroupApp.Example
|
||||
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
|
||||
// VUE_APP_BASE_API = /
|
||||
// VUE_APP_BASE_PATH = http://localhost
|
||||
// 然后执行打包命令 npm run build。在打开下面3行注释
|
||||
// Router.StaticFile("/favicon.ico", "./dist/favicon.ico")
|
||||
// Router.Static("/assets", "./dist/assets") // dist里面的静态资源
|
||||
// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
|
||||
|
||||
// 静态文件服务
|
||||
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)})
|
||||
|
||||
// Swagger 文档
|
||||
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)}) // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
|
||||
// 跨域,如需跨域可以打开下面的注释
|
||||
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求
|
||||
// Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
|
||||
// global.GVA_LOG.Info("use middleware cors")
|
||||
docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix
|
||||
Router.GET(global.GVA_CONFIG.System.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
global.GVA_LOG.Info("register swagger handler")
|
||||
// 方便统一添加路由组前缀 多服务器上线使用
|
||||
|
||||
// 路由组
|
||||
PublicGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
|
||||
PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
|
||||
|
||||
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
|
||||
|
||||
{
|
||||
// 健康检查
|
||||
// 健康监测
|
||||
PublicGroup.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, "ok")
|
||||
})
|
||||
}
|
||||
|
||||
// 系统管理路由
|
||||
{
|
||||
systemGroup := PublicGroup.Group("v1/system")
|
||||
systemRouter.UserRouter.InitUserRouter(systemGroup) // 用户管理路由:/v1/system/user/*
|
||||
systemRouter.ApiRouter.InitApiRouter(systemGroup) // API管理路由:/v1/system/api/*
|
||||
systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
|
||||
systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
|
||||
}
|
||||
|
||||
// 前台应用路由
|
||||
{
|
||||
appGroup := PublicGroup.Group("app")
|
||||
appRouter.AiPresetRouter.InitAiPresetRouter(appGroup) // AI预设路由:/app/preset/*
|
||||
appRouter.AiProviderRouter.InitAiProviderRouter(appGroup) // AI提供商路由:/app/provider/*
|
||||
appRouter.PresetBindingRouter.InitPresetBindingRouter(appGroup) // 预设绑定路由:/app/binding/*
|
||||
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.InitAutoCodeRouter(PrivateGroup, PublicGroup) // 创建自动化代码
|
||||
systemRouter.InitAuthorityRouter(PrivateGroup) // 注册角色路由
|
||||
systemRouter.InitSysDictionaryRouter(PrivateGroup) // 字典管理
|
||||
systemRouter.InitAutoCodeHistoryRouter(PrivateGroup) // 自动化代码历史
|
||||
systemRouter.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
|
||||
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
|
||||
systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup) // 按钮权限管理
|
||||
systemRouter.InitSysExportTemplateRouter(PrivateGroup, PublicGroup) // 导出模板
|
||||
systemRouter.InitSysParamsRouter(PrivateGroup, PublicGroup) // 参数管理
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
systemRouter.InitSkillsRouter(PrivateGroup) // Skills 定义器
|
||||
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
|
||||
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
|
||||
}
|
||||
|
||||
// OpenAI 兼容的代理接口
|
||||
{
|
||||
v1Group := PublicGroup.Group("v1")
|
||||
appRouter.AiProxyRouter.InitAiProxyRouter(v1Group) // /v1/chat/completions
|
||||
}
|
||||
//插件路由安装
|
||||
InstallPlugin(PrivateGroup, PublicGroup, Router)
|
||||
|
||||
// 注册业务路由
|
||||
initBizRouter(PrivateGroup, PublicGroup)
|
||||
|
||||
global.GVA_ROUTERS = Router.Routes()
|
||||
|
||||
global.GVA_LOG.Info("router register success")
|
||||
return Router
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ func initBizRouter(routers ...*gin.RouterGroup) {
|
||||
|
||||
holder(publicGroup, privateGroup)
|
||||
|
||||
// 注册 app 模块路由
|
||||
// 注册 AI 代理路由
|
||||
appRouter := router.RouterGroupApp.App
|
||||
appRouter.AiPresetRouter.InitAiPresetRouter(privateGroup)
|
||||
appRouter.AiProviderRouter.InitAiProviderRouter(privateGroup)
|
||||
appRouter.AiProxyRouter.InitAiProxyRouter(privateGroup)
|
||||
appRouter.PresetBindingRouter.InitPresetBindingRouter(privateGroup)
|
||||
appRouter.InitAiProxyRouter(publicGroup) // AI 代理接口(公开)
|
||||
appRouter.InitAiPresetRouter(privateGroup) // 预设管理(需要登录)
|
||||
appRouter.InitAiProviderRouter(privateGroup) // 提供商管理(需要登录)
|
||||
appRouter.InitAiPresetBindingRouter(privateGroup) // 绑定管理(需要登录)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/task"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
@@ -1,739 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:49.976 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[8.026ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:92
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:49.977 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:92
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.453 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.921ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:92
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.455 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:92
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.269 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[22.029ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.270 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.890 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.656ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.891 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.152 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[9.354ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.153 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:93
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.429 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[17.993ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:95
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.430 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:95
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.233 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.206ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.236 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.744 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[8.725ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.745 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.418 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.519ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.420 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:42.978 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.195ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:42.979 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:39.899 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[21.072ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:39.900 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:39.921 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[12.273ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:39.922 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.740 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[7.602ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.741 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/gorm.go:96
|
||||
main.initializeSystem
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:49
|
||||
main.main
|
||||
/Users/en/GolandProjects/st-ui/server/main.go:32
|
||||
runtime.main
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/runtime/proc.go:285
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:22.771 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296 [35;1mrecord not found
|
||||
[0m[33m[44.080ms] [34;1m[rows:0][0m SELECT * FROM "ai_configs" WHERE (provider = 'openai' AND is_active = true) AND "ai_configs"."deleted_at" IS NULL ORDER BY is_default DESC, created_at DESC,"ai_configs"."id" LIMIT 1
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).First
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:129
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).callAIService
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:259
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:227
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:22.772 [31merror[0m /Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229 发送消息失败 {"error": "未找到可用的 AI 配置"}
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:43.641 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296 [35;1mrecord not found
|
||||
[0m[33m[30.845ms] [34;1m[rows:0][0m SELECT * FROM "ai_configs" WHERE (provider = 'openai' AND is_active = true) AND "ai_configs"."deleted_at" IS NULL ORDER BY is_default DESC, created_at DESC,"ai_configs"."id" LIMIT 1
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).First
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:129
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).callAIService
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:259
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:227
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:43.642 [31merror[0m /Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229 发送消息失败 {"error": "未找到可用的 AI 配置"}
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:46.833 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296 [35;1mrecord not found
|
||||
[0m[33m[6.773ms] [34;1m[rows:0][0m SELECT * FROM "ai_configs" WHERE (provider = 'openai' AND is_active = true) AND "ai_configs"."deleted_at" IS NULL ORDER BY is_default DESC, created_at DESC,"ai_configs"."id" LIMIT 1
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).First
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:129
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).callAIService
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:259
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:227
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:46.834 [31merror[0m /Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229 发送消息失败 {"error": "未找到可用的 AI 配置"}
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:54.855 [31merror[0m /Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31 [31;1m/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296 [35;1mrecord not found
|
||||
[0m[33m[16.792ms] [34;1m[rows:0][0m SELECT * FROM "ai_configs" WHERE (provider = 'openai' AND is_active = true) AND "ai_configs"."deleted_at" IS NULL ORDER BY is_default DESC, created_at DESC,"ai_configs"."id" LIMIT 1
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
/Users/en/GolandProjects/st-ui/server/initialize/internal/gorm_logger_writer.go:31
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).First
|
||||
/Users/en/go/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:129
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).callAIService
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:296
|
||||
git.echol.cn/loser/ai_proxy/server/service/app.(*ConversationService).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/service/app/conversation.go:259
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:227
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:54.858 [31merror[0m /Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229 发送消息失败 {"error": "未找到可用的 AI 配置"}
|
||||
git.echol.cn/loser/ai_proxy/server/api/v1/app.(*ConversationApi).SendMessage
|
||||
/Users/en/GolandProjects/st-ui/server/api/v1/app/conversation.go:229
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.AppJWTAuth.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/app_jwt.go:71
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.Cors.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/cors.go:26
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.LoggerWithConfig.func1
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/logger.go:249
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
git.echol.cn/loser/ai_proxy/server/middleware.GinRecovery.func1
|
||||
/Users/en/GolandProjects/st-ui/server/middleware/error.go:78
|
||||
github.com/gin-gonic/gin.(*Context).Next
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185
|
||||
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633
|
||||
github.com/gin-gonic/gin.(*Engine).ServeHTTP
|
||||
/Users/en/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589
|
||||
net/http.serverHandler.ServeHTTP
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:3340
|
||||
net/http.(*conn).serve
|
||||
/Users/en/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.7.darwin-arm64/src/net/http/server.go:2109
|
||||
@@ -1,100 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:46.967 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.020 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:100 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.084 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.099 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.100 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.101 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:161 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:33:48.393 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:33:48.417 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:07.438 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.494 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:100 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.542 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.558 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.559 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.561 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:162 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:35:50.353 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:35:50.374 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:02.621 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.306 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:101 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.355 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.370 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.372 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.374 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:162 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:35.832 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:35.839 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:42.030 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.935 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:101 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.975 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.990 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.992 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.994 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:162 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:01:44.899 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:01:44.910 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:16.588 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.192 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:101 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.234 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.250 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.255 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.258 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:162 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:25.628 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:25.636 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:53.277 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.466 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:103 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.520 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.535 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.538 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.541 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:163 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:33.169 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:33.201 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:50.302 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.274 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.313 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.330 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.331 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.332 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:06.014 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:06.020 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:19.421 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.780 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.826 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.847 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.849 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.853 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:53:59.453 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:53:59.511 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:04.529 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.491 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.531 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.546 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.547 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.548 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:21.772 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:21.793 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:35.497 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.060 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.124 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.154 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.157 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.162 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:28.437 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:28.457 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:32.919 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:39.949 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:39.991 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:40.012 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:40.013 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:40.014 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:32.465 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:39.965 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:40.019 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:40.035 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:40.041 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:40.042 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:14:04.444 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:14:04.451 [34minfo[0m /Users/en/GolandProjects/st-ui/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:00.587 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.867 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/gorm.go:104 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.932 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.958 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.960 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.961 [34minfo[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:164 router register success
|
||||
@@ -1,13 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:21:50.100 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:34:14.558 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:36:08.371 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 18:56:48.990 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:02:22.251 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:14:59.536 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:27:56.331 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:45:27.848 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 19:54:11.546 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:17:43.155 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:33:40.013 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 20:49:40.036 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-26 21:15:08.959 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 14:16:32.040 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 14:53:11.399 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:06:25.876 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:10:59.873 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:13:52.007 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:21:57.951 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:24:45.527 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:41:48.294 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:48:54.136 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 15:55:45.526 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 16:08:32.199 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 16:23:31.784 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 16:30:28.919 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 16:41:46.870 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 16:49:02.739 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 17:03:59.981 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 18:24:51.562 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 19:03:13.475 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 19:07:46.556 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 19:15:47.924 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 19:40:53.649 [33mwarn[0m /Users/en/GolandProjects/st-ui/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 22:03:02.617 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 22:34:07.654 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 23:26:58.610 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-27 23:42:35.973 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:26.677 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:26.681 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:36.835 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.277 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:107 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.297 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.308 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.309 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.311 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:168 router register success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 14:59:47.761 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/core/server_run.go:48 关闭WEB服务...
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 14:59:47.762 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/core/server_run.go:59 WEB服务已关闭
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:12.331 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:22 pgvector extension is ready
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.732 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:107 register table success
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.763 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/redis.go:35 redis connect ping response: {"name": "sys-cache", "pong": "PONG"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.775 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:51 use middleware cors
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.777 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:101 register swagger handler
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.780 [34minfo[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:168 router register success
|
||||
@@ -1,2 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 00:18:40.309 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-02-28 15:00:15.777 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
@@ -1,228 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 00:00:05.840 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/task/clearTable.go:46 [35;1mfailed to connect to `user=postgres database=st2`: 219.152.55.29:5432 (219.152.55.29): failed to receive message: unexpected EOF
|
||||
[0m[33m[5001.319ms] [34;1m[rows:0][0m DELETE FROM sys_operation_records WHERE created_at < '2025-12-01 00:00:00.839'
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/task.ClearTable
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/task/clearTable.go:46
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.Timer.func1.1
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/timer.go:19
|
||||
github.com/robfig/cron/v3.FuncJob.Run
|
||||
D:/GOPATH/pkg/mod/github.com/robfig/cron/v3@v3.0.1/cron.go:136
|
||||
github.com/robfig/cron/v3.(*Cron).startJob.func1
|
||||
D:/GOPATH/pkg/mod/github.com/robfig/cron/v3@v3.0.1/cron.go:312
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:22:55.648 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[3.710ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:22:55.649 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:46:08.588 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[3.115ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:46:08.592 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:50:09.853 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[4.154ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:50:09.853 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:52:55.114 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[3.661ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:52:55.114 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:12:59.096 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[4.166ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:12:59.100 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:18:28.225 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32 [31;1mC:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42 [35;1mERROR: relation "ai_memory_vectors" does not exist (SQLSTATE 42P01)
|
||||
[0m[33m[3.133ms] [34;1m[rows:0][0m
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_vectors_embedding
|
||||
ON ai_memory_vectors
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
|
||||
git.echol.cn/loser/ai_proxy/server/initialize/internal.(*Writer).Printf
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/internal/gorm_logger_writer.go:32
|
||||
gorm.io/gorm/logger.(*logger).Trace
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/logger/logger.go:167
|
||||
gorm.io/gorm.(*processor).Execute
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/callbacks.go:134
|
||||
gorm.io/gorm.(*DB).Exec
|
||||
D:/GOPATH/pkg/mod/gorm.io/gorm@v1.25.12/finisher_api.go:769
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:42
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:18:28.230 [31merror[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43 failed to create vector indexes {"error": "ERROR: relation \"ai_memory_vectors\" does not exist (SQLSTATE 42P01)"}
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.CreateVectorIndexes
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm_pgsql_extension.go:43
|
||||
git.echol.cn/loser/ai_proxy/server/initialize.RegisterTables
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/initialize/gorm.go:99
|
||||
main.initializeSystem
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:49
|
||||
main.main
|
||||
C:/Users/Administrator/GolandProjects/st-react/server/main.go:32
|
||||
runtime.main
|
||||
C:/Program Files/Go/src/runtime/proc.go:283
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:22:55.696 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:46:08.641 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:50:09.903 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 21:52:55.198 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:12:59.150 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-01 22:18:28.278 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 00:25:36.353 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 00:29:09.468 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 00:57:24.167 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/service/app/regex_script.go:218 正则表达式编译失败 {"pattern": "/<(UpdateVariable)>((?:(?!<\\1>)[\\s\\S])*?)</\\1>/mi", "error": "error parsing regexp: invalid or unsupported Perl syntax: `(?!`"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 00:57:24.171 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/service/app/regex_script.go:281 执行正则脚本失败 {"name": "对AI隐藏状态栏更新", "error": "error parsing regexp: invalid or unsupported Perl syntax: `(?!`"}
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 01:02:44.889 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 02:43:19.037 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 03:02:08.766 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-02 23:18:20.568 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 02:14:33.098 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 02:59:50.061 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:13:21.290 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:20:20.084 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:26:19.621 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:30:35.712 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:37:53.896 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
[git.echol.cn/loser/ai_proxy/server]2026-03-03 03:54:16.681 [33mwarn[0m C:/Users/Administrator/GolandProjects/st-react/server/initialize/router.go:85 SillyTavern 核心脚本目录不存在: data/st-core-scripts
|
||||
191
server/mcp/api_creator.go
Normal file
191
server/mcp/api_creator.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&ApiCreator{})
|
||||
}
|
||||
|
||||
// ApiCreateRequest API创建请求结构
|
||||
type ApiCreateRequest struct {
|
||||
Path string `json:"path"` // API路径
|
||||
Description string `json:"description"` // API中文描述
|
||||
ApiGroup string `json:"apiGroup"` // API组
|
||||
Method string `json:"method"` // HTTP方法
|
||||
}
|
||||
|
||||
// ApiCreateResponse API创建响应结构
|
||||
type ApiCreateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
ApiID uint `json:"apiId"`
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
// ApiCreator API创建工具
|
||||
type ApiCreator struct{}
|
||||
|
||||
// New 创建API创建工具
|
||||
func (a *ApiCreator) New() mcp.Tool {
|
||||
return mcp.NewTool("create_api",
|
||||
mcp.WithDescription(`创建后端API记录,用于AI编辑器自动添加API接口时自动创建对应的API权限记录。
|
||||
|
||||
**重要限制:**
|
||||
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成API权限,不应调用此工具
|
||||
- 仅在以下情况使用:1) 单独创建API(不涉及模块创建);2) AI编辑器自动添加API;3) router下的文件产生路径变化时`),
|
||||
mcp.WithString("path",
|
||||
mcp.Required(),
|
||||
mcp.Description("API路径,如:/user/create"),
|
||||
),
|
||||
mcp.WithString("description",
|
||||
mcp.Required(),
|
||||
mcp.Description("API中文描述,如:创建用户"),
|
||||
),
|
||||
mcp.WithString("apiGroup",
|
||||
mcp.Required(),
|
||||
mcp.Description("API组名称,用于分类管理,如:用户管理"),
|
||||
),
|
||||
mcp.WithString("method",
|
||||
mcp.Description("HTTP方法"),
|
||||
mcp.DefaultString("POST"),
|
||||
),
|
||||
mcp.WithString("apis",
|
||||
mcp.Description("批量创建API的JSON字符串,格式:[{\"path\":\"/user/create\",\"description\":\"创建用户\",\"apiGroup\":\"用户管理\",\"method\":\"POST\"}]"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理API创建请求
|
||||
func (a *ApiCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
|
||||
var apis []ApiCreateRequest
|
||||
|
||||
// 检查是否是批量创建
|
||||
if apisStr, ok := args["apis"].(string); ok && apisStr != "" {
|
||||
if err := json.Unmarshal([]byte(apisStr), &apis); err != nil {
|
||||
return nil, fmt.Errorf("apis 参数格式错误: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 单个API创建
|
||||
path, ok := args["path"].(string)
|
||||
if !ok || path == "" {
|
||||
return nil, errors.New("path 参数是必需的")
|
||||
}
|
||||
|
||||
description, ok := args["description"].(string)
|
||||
if !ok || description == "" {
|
||||
return nil, errors.New("description 参数是必需的")
|
||||
}
|
||||
|
||||
apiGroup, ok := args["apiGroup"].(string)
|
||||
if !ok || apiGroup == "" {
|
||||
return nil, errors.New("apiGroup 参数是必需的")
|
||||
}
|
||||
|
||||
method := "POST"
|
||||
if val, ok := args["method"].(string); ok && val != "" {
|
||||
method = val
|
||||
}
|
||||
|
||||
apis = append(apis, ApiCreateRequest{
|
||||
Path: path,
|
||||
Description: description,
|
||||
ApiGroup: apiGroup,
|
||||
Method: method,
|
||||
})
|
||||
}
|
||||
|
||||
if len(apis) == 0 {
|
||||
return nil, errors.New("没有要创建的API")
|
||||
}
|
||||
|
||||
// 创建API记录
|
||||
apiService := service.ServiceGroupApp.SystemServiceGroup.ApiService
|
||||
var responses []ApiCreateResponse
|
||||
successCount := 0
|
||||
|
||||
for _, apiReq := range apis {
|
||||
api := system.SysApi{
|
||||
Path: apiReq.Path,
|
||||
Description: apiReq.Description,
|
||||
ApiGroup: apiReq.ApiGroup,
|
||||
Method: apiReq.Method,
|
||||
}
|
||||
|
||||
err := apiService.CreateApi(api)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("创建API失败",
|
||||
zap.String("path", apiReq.Path),
|
||||
zap.String("method", apiReq.Method),
|
||||
zap.Error(err))
|
||||
|
||||
responses = append(responses, ApiCreateResponse{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("创建API失败: %v", err),
|
||||
Path: apiReq.Path,
|
||||
Method: apiReq.Method,
|
||||
})
|
||||
} else {
|
||||
// 获取创建的API ID
|
||||
var createdApi system.SysApi
|
||||
err = global.GVA_DB.Where("path = ? AND method = ?", apiReq.Path, apiReq.Method).First(&createdApi).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("获取创建的API ID失败", zap.Error(err))
|
||||
}
|
||||
|
||||
responses = append(responses, ApiCreateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建API %s %s", apiReq.Method, apiReq.Path),
|
||||
ApiID: createdApi.ID,
|
||||
Path: apiReq.Path,
|
||||
Method: apiReq.Method,
|
||||
})
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
// 构建总体响应
|
||||
var resultMessage string
|
||||
if len(apis) == 1 {
|
||||
resultMessage = responses[0].Message
|
||||
} else {
|
||||
resultMessage = fmt.Sprintf("批量创建API完成,成功 %d 个,失败 %d 个", successCount, len(apis)-successCount)
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"success": successCount > 0,
|
||||
"message": resultMessage,
|
||||
"totalCount": len(apis),
|
||||
"successCount": successCount,
|
||||
"failedCount": len(apis) - successCount,
|
||||
"details": responses,
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("API创建结果:\n\n%s", string(resultJSON)),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
168
server/mcp/api_lister.go
Normal file
168
server/mcp/api_lister.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
// 注册工具将在enter.go中统一处理
|
||||
RegisterTool(&ApiLister{})
|
||||
}
|
||||
|
||||
// ApiInfo API信息结构
|
||||
type ApiInfo struct {
|
||||
ID uint `json:"id,omitempty"` // 数据库ID(仅数据库API有)
|
||||
Path string `json:"path"` // API路径
|
||||
Description string `json:"description,omitempty"` // API描述
|
||||
ApiGroup string `json:"apiGroup,omitempty"` // API组
|
||||
Method string `json:"method"` // HTTP方法
|
||||
Source string `json:"source"` // 来源:database 或 gin
|
||||
}
|
||||
|
||||
// ApiListResponse API列表响应结构
|
||||
type ApiListResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
DatabaseApis []ApiInfo `json:"databaseApis"` // 数据库中的API
|
||||
GinApis []ApiInfo `json:"ginApis"` // gin框架中的API
|
||||
TotalCount int `json:"totalCount"` // 总数量
|
||||
}
|
||||
|
||||
// ApiLister API列表工具
|
||||
type ApiLister struct{}
|
||||
|
||||
// New 创建API列表工具
|
||||
func (a *ApiLister) New() mcp.Tool {
|
||||
return mcp.NewTool("list_all_apis",
|
||||
mcp.WithDescription(`获取系统中所有的API接口,分为两组:
|
||||
|
||||
**功能说明:**
|
||||
- 返回数据库中已注册的API列表
|
||||
- 返回gin框架中实际注册的路由API列表
|
||||
- 帮助前端判断是使用现有API还是需要创建新的API,如果api在前端未使用且需要前端调用的时候,请到api文件夹下对应模块的js中添加方法并暴露给当前业务调用
|
||||
|
||||
**返回数据结构:**
|
||||
- databaseApis: 数据库中的API记录(包含ID、描述、分组等完整信息)
|
||||
- ginApis: gin路由中的API(仅包含路径和方法),需要AI根据路径自行揣摩路径的业务含义,例如:/api/user/:id 表示根据用户ID获取用户信息`),
|
||||
mcp.WithString("_placeholder",
|
||||
mcp.Description("占位符,防止json schema校验失败"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理API列表请求
|
||||
func (a *ApiLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
|
||||
// 获取数据库中的API
|
||||
databaseApis, err := a.getDatabaseApis()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取数据库API失败", zap.Error(err))
|
||||
errorResponse := ApiListResponse{
|
||||
Success: false,
|
||||
Message: "获取数据库API失败: " + err.Error(),
|
||||
}
|
||||
resultJSON, _ := json.Marshal(errorResponse)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 获取gin路由中的API
|
||||
ginApis, err := a.getGinApis()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取gin路由API失败", zap.Error(err))
|
||||
errorResponse := ApiListResponse{
|
||||
Success: false,
|
||||
Message: "获取gin路由API失败: " + err.Error(),
|
||||
}
|
||||
resultJSON, _ := json.Marshal(errorResponse)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := ApiListResponse{
|
||||
Success: true,
|
||||
Message: "获取API列表成功",
|
||||
DatabaseApis: databaseApis,
|
||||
GinApis: ginApis,
|
||||
TotalCount: len(databaseApis) + len(ginApis),
|
||||
}
|
||||
|
||||
global.GVA_LOG.Info("API列表获取成功",
|
||||
zap.Int("数据库API数量", len(databaseApis)),
|
||||
zap.Int("gin路由API数量", len(ginApis)),
|
||||
zap.Int("总数量", response.TotalCount))
|
||||
|
||||
resultJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(resultJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getDatabaseApis 获取数据库中的所有API
|
||||
func (a *ApiLister) getDatabaseApis() ([]ApiInfo, error) {
|
||||
var apis []system.SysApi
|
||||
err := global.GVA_DB.Model(&system.SysApi{}).Order("api_group ASC, path ASC").Find(&apis).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为ApiInfo格式
|
||||
var result []ApiInfo
|
||||
for _, api := range apis {
|
||||
result = append(result, ApiInfo{
|
||||
ID: api.ID,
|
||||
Path: api.Path,
|
||||
Description: api.Description,
|
||||
ApiGroup: api.ApiGroup,
|
||||
Method: api.Method,
|
||||
Source: "database",
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getGinApis 获取gin路由中的所有API(包含被忽略的API)
|
||||
func (a *ApiLister) getGinApis() ([]ApiInfo, error) {
|
||||
// 从gin路由信息中获取所有API
|
||||
var result []ApiInfo
|
||||
for _, route := range global.GVA_ROUTERS {
|
||||
result = append(result, ApiInfo{
|
||||
Path: route.Path,
|
||||
Method: route.Method,
|
||||
Source: "gin",
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
39
server/mcp/client/client.go
Normal file
39
server/mcp/client/client.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
mcpClient "github.com/mark3labs/mcp-go/client"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
func NewClient(baseUrl, name, version, serverName string) (*mcpClient.Client, error) {
|
||||
client, err := mcpClient.NewSSEMCPClient(baseUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 启动client
|
||||
if err := client.Start(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 初始化
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
|
||||
result, err := client.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.ServerInfo.Name != serverName {
|
||||
return nil, errors.New("server name mismatch")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
132
server/mcp/client/client_test.go
Normal file
132
server/mcp/client/client_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 测试 MCP 客户端连接
|
||||
func TestMcpClientConnection(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTools(t *testing.T) {
|
||||
t.Run("currentTime", func(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = "currentTime"
|
||||
request.Params.Arguments = map[string]interface{}{
|
||||
"timezone": "UTC+8",
|
||||
}
|
||||
|
||||
result, err := c.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("方法调用错误: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Content) != 1 {
|
||||
t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
|
||||
}
|
||||
if content, ok := result.Content[0].(mcp.TextContent); ok {
|
||||
t.Logf("成功返回信息%s", content.Text)
|
||||
} else {
|
||||
t.Logf("返回为止类型信息%+v", content)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("getNickname", func(t *testing.T) {
|
||||
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize
|
||||
initRequest := mcp.InitializeRequest{}
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: "test-client",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
_, err = c.Initialize(ctx, initRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("初始化失败: %v", err)
|
||||
}
|
||||
|
||||
request := mcp.CallToolRequest{}
|
||||
request.Params.Name = "getNickname"
|
||||
request.Params.Arguments = map[string]interface{}{
|
||||
"username": "admin",
|
||||
}
|
||||
|
||||
result, err := c.CallTool(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("方法调用错误: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Content) != 1 {
|
||||
t.Errorf("应该有且仅返回1条信息,但是现在有 %d", len(result.Content))
|
||||
}
|
||||
if content, ok := result.Content[0].(mcp.TextContent); ok {
|
||||
t.Logf("成功返回信息%s", content.Text)
|
||||
} else {
|
||||
t.Logf("返回为止类型信息%+v", content)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTools(t *testing.T) {
|
||||
c, err := NewClient("http://localhost:8888/sse", "test-client", "1.0.0", "gin-vue-admin MCP服务")
|
||||
defer c.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
toolsRequest := mcp.ListToolsRequest{}
|
||||
|
||||
toolListResult, err := c.ListTools(ctx, toolsRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("获取工具列表失败: %v", err)
|
||||
}
|
||||
for i := range toolListResult.Tools {
|
||||
tool := toolListResult.Tools[i]
|
||||
fmt.Printf("工具名称: %s\n", tool.Name)
|
||||
fmt.Printf("工具描述: %s\n", tool.Description)
|
||||
|
||||
// 打印参数信息
|
||||
if tool.InputSchema.Properties != nil {
|
||||
fmt.Println("参数列表:")
|
||||
for paramName, prop := range tool.InputSchema.Properties {
|
||||
required := "否"
|
||||
// 检查参数是否在必填列表中
|
||||
for _, reqField := range tool.InputSchema.Required {
|
||||
if reqField == paramName {
|
||||
required = "是"
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf(" - %s (类型: %s, 描述: %s, 必填: %s)\n",
|
||||
paramName, prop.(map[string]any)["type"], prop.(map[string]any)["description"], required)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("该工具没有参数")
|
||||
}
|
||||
fmt.Println("-------------------")
|
||||
}
|
||||
}
|
||||
229
server/mcp/dictionary_generator.go
Normal file
229
server/mcp/dictionary_generator.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterTool(&DictionaryOptionsGenerator{})
|
||||
}
|
||||
|
||||
// DictionaryOptionsGenerator 字典选项生成器
|
||||
type DictionaryOptionsGenerator struct{}
|
||||
|
||||
// DictionaryOption 字典选项结构
|
||||
type DictionaryOption struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
|
||||
// DictionaryGenerateRequest 字典生成请求
|
||||
type DictionaryGenerateRequest struct {
|
||||
DictType string `json:"dictType"` // 字典类型
|
||||
FieldDesc string `json:"fieldDesc"` // 字段描述
|
||||
Options []DictionaryOption `json:"options"` // AI生成的字典选项
|
||||
DictName string `json:"dictName"` // 字典名称(可选)
|
||||
Description string `json:"description"` // 字典描述(可选)
|
||||
}
|
||||
|
||||
// DictionaryGenerateResponse 字典生成响应
|
||||
type DictionaryGenerateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
DictType string `json:"dictType"`
|
||||
OptionsCount int `json:"optionsCount"`
|
||||
}
|
||||
|
||||
// New 返回工具注册信息
|
||||
func (d *DictionaryOptionsGenerator) New() mcp.Tool {
|
||||
return mcp.NewTool("generate_dictionary_options",
|
||||
mcp.WithDescription("智能生成字典选项并自动创建字典和字典详情"),
|
||||
mcp.WithString("dictType",
|
||||
mcp.Required(),
|
||||
mcp.Description("字典类型,用于标识字典的唯一性"),
|
||||
),
|
||||
mcp.WithString("fieldDesc",
|
||||
mcp.Required(),
|
||||
mcp.Description("字段描述,用于AI理解字段含义"),
|
||||
),
|
||||
mcp.WithString("options",
|
||||
mcp.Required(),
|
||||
mcp.Description("字典选项JSON字符串,格式:[{\"label\":\"显示名\",\"value\":\"值\",\"sort\":1}]"),
|
||||
),
|
||||
mcp.WithString("dictName",
|
||||
mcp.Description("字典名称,如果不提供将自动生成"),
|
||||
),
|
||||
mcp.WithString("description",
|
||||
mcp.Description("字典描述"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理工具调用
|
||||
func (d *DictionaryOptionsGenerator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
args := request.GetArguments()
|
||||
|
||||
dictType, ok := args["dictType"].(string)
|
||||
if !ok || dictType == "" {
|
||||
return nil, errors.New("dictType 参数是必需的")
|
||||
}
|
||||
|
||||
fieldDesc, ok := args["fieldDesc"].(string)
|
||||
if !ok || fieldDesc == "" {
|
||||
return nil, errors.New("fieldDesc 参数是必需的")
|
||||
}
|
||||
|
||||
optionsStr, ok := args["options"].(string)
|
||||
if !ok || optionsStr == "" {
|
||||
return nil, errors.New("options 参数是必需的")
|
||||
}
|
||||
|
||||
// 解析options JSON字符串
|
||||
var options []DictionaryOption
|
||||
if err := json.Unmarshal([]byte(optionsStr), &options); err != nil {
|
||||
return nil, fmt.Errorf("options 参数格式错误: %v", err)
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("options 不能为空")
|
||||
}
|
||||
|
||||
dictName, _ := args["dictName"].(string)
|
||||
description, _ := args["description"].(string)
|
||||
|
||||
// 构建请求对象
|
||||
req := &DictionaryGenerateRequest{
|
||||
DictType: dictType,
|
||||
FieldDesc: fieldDesc,
|
||||
Options: options,
|
||||
DictName: dictName,
|
||||
Description: description,
|
||||
}
|
||||
|
||||
// 创建字典
|
||||
response, err := d.createDictionaryWithOptions(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
resultJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("字典选项生成结果:\n\n%s", string(resultJSON)),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createDictionaryWithOptions 创建字典和字典选项
|
||||
func (d *DictionaryOptionsGenerator) createDictionaryWithOptions(ctx context.Context, req *DictionaryGenerateRequest) (*DictionaryGenerateResponse, error) {
|
||||
// 检查字典是否已存在
|
||||
exists, err := d.checkDictionaryExists(req.DictType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查字典是否存在失败: %v", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return &DictionaryGenerateResponse{
|
||||
Success: false,
|
||||
Message: fmt.Sprintf("字典 %s 已存在,跳过创建", req.DictType),
|
||||
DictType: req.DictType,
|
||||
OptionsCount: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 生成字典名称
|
||||
dictName := req.DictName
|
||||
if dictName == "" {
|
||||
dictName = d.generateDictionaryName(req.DictType, req.FieldDesc)
|
||||
}
|
||||
|
||||
// 创建字典
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
dictionary := system.SysDictionary{
|
||||
Name: dictName,
|
||||
Type: req.DictType,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Desc: req.Description,
|
||||
}
|
||||
|
||||
err = dictionaryService.CreateSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建字典失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取刚创建的字典ID
|
||||
var createdDict system.SysDictionary
|
||||
err = global.GVA_DB.Where("type = ?", req.DictType).First(&createdDict).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取创建的字典失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建字典详情项
|
||||
dictionaryDetailService := service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
successCount := 0
|
||||
|
||||
for _, option := range req.Options {
|
||||
dictionaryDetail := system.SysDictionaryDetail{
|
||||
Label: option.Label,
|
||||
Value: option.Value,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Sort: option.Sort,
|
||||
SysDictionaryID: int(createdDict.ID),
|
||||
}
|
||||
|
||||
err = dictionaryDetailService.CreateSysDictionaryDetail(dictionaryDetail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("创建字典详情项失败", zap.Error(err))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
return &DictionaryGenerateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建字典 %s,包含 %d 个选项", req.DictType, successCount),
|
||||
DictType: req.DictType,
|
||||
OptionsCount: successCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkDictionaryExists 检查字典是否存在
|
||||
func (d *DictionaryOptionsGenerator) checkDictionaryExists(dictType string) (bool, error) {
|
||||
var dictionary system.SysDictionary
|
||||
err := global.GVA_DB.Where("type = ?", dictType).First(&dictionary).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil // 字典不存在
|
||||
}
|
||||
return false, err // 其他错误
|
||||
}
|
||||
return true, nil // 字典存在
|
||||
}
|
||||
|
||||
// generateDictionaryName 生成字典名称
|
||||
func (d *DictionaryOptionsGenerator) generateDictionaryName(dictType, fieldDesc string) string {
|
||||
if fieldDesc != "" {
|
||||
return fmt.Sprintf("%s字典", fieldDesc)
|
||||
}
|
||||
return fmt.Sprintf("%s字典", dictType)
|
||||
}
|
||||
239
server/mcp/dictionary_query.go
Normal file
239
server/mcp/dictionary_query.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&DictionaryQuery{})
|
||||
}
|
||||
|
||||
type DictionaryPre struct {
|
||||
Type string `json:"type"` // 字典名(英)
|
||||
Desc string `json:"desc"` // 描述
|
||||
}
|
||||
|
||||
// DictionaryInfo 字典信息结构
|
||||
type DictionaryInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"` // 字典名(中)
|
||||
Type string `json:"type"` // 字典名(英)
|
||||
Status *bool `json:"status"` // 状态
|
||||
Desc string `json:"desc"` // 描述
|
||||
Details []DictionaryDetailInfo `json:"details"` // 字典详情
|
||||
}
|
||||
|
||||
// DictionaryDetailInfo 字典详情信息结构
|
||||
type DictionaryDetailInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"` // 展示值
|
||||
Value string `json:"value"` // 字典值
|
||||
Extend string `json:"extend"` // 扩展值
|
||||
Status *bool `json:"status"` // 启用状态
|
||||
Sort int `json:"sort"` // 排序标记
|
||||
}
|
||||
|
||||
// DictionaryQueryResponse 字典查询响应结构
|
||||
type DictionaryQueryResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Total int `json:"total"`
|
||||
Dictionaries []DictionaryInfo `json:"dictionaries"`
|
||||
}
|
||||
|
||||
// DictionaryQuery 字典查询工具
|
||||
type DictionaryQuery struct{}
|
||||
|
||||
// New 创建字典查询工具
|
||||
func (d *DictionaryQuery) New() mcp.Tool {
|
||||
return mcp.NewTool("query_dictionaries",
|
||||
mcp.WithDescription("查询系统中所有的字典和字典属性,用于AI生成逻辑时了解可用的字典选项"),
|
||||
mcp.WithString("dictType",
|
||||
mcp.Description("可选:指定字典类型进行精确查询,如果不提供则返回所有字典"),
|
||||
),
|
||||
mcp.WithBoolean("includeDisabled",
|
||||
mcp.Description("是否包含已禁用的字典和字典项,默认为false(只返回启用的)"),
|
||||
),
|
||||
mcp.WithBoolean("detailsOnly",
|
||||
mcp.Description("是否只返回字典详情信息(不包含字典基本信息),默认为false"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理字典查询请求
|
||||
func (d *DictionaryQuery) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
|
||||
// 获取参数
|
||||
dictType := ""
|
||||
if val, ok := args["dictType"].(string); ok {
|
||||
dictType = val
|
||||
}
|
||||
|
||||
includeDisabled := false
|
||||
if val, ok := args["includeDisabled"].(bool); ok {
|
||||
includeDisabled = val
|
||||
}
|
||||
|
||||
detailsOnly := false
|
||||
if val, ok := args["detailsOnly"].(bool); ok {
|
||||
detailsOnly = val
|
||||
}
|
||||
|
||||
// 获取字典服务
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
|
||||
var dictionaries []DictionaryInfo
|
||||
var err error
|
||||
|
||||
if dictType != "" {
|
||||
// 查询指定类型的字典
|
||||
var status *bool
|
||||
if !includeDisabled {
|
||||
status = &[]bool{true}[0]
|
||||
}
|
||||
|
||||
sysDictionary, err := dictionaryService.GetSysDictionary(dictType, 0, status)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询字典失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
dictInfo := DictionaryInfo{
|
||||
ID: sysDictionary.ID,
|
||||
Name: sysDictionary.Name,
|
||||
Type: sysDictionary.Type,
|
||||
Status: sysDictionary.Status,
|
||||
Desc: sysDictionary.Desc,
|
||||
}
|
||||
|
||||
// 获取字典详情
|
||||
for _, detail := range sysDictionary.SysDictionaryDetails {
|
||||
if includeDisabled || (detail.Status != nil && *detail.Status) {
|
||||
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
|
||||
ID: detail.ID,
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries = append(dictionaries, dictInfo)
|
||||
} else {
|
||||
// 查询所有字典
|
||||
var sysDictionaries []system.SysDictionary
|
||||
db := global.GVA_DB.Model(&system.SysDictionary{})
|
||||
|
||||
if !includeDisabled {
|
||||
db = db.Where("status = ?", true)
|
||||
}
|
||||
|
||||
err = db.Preload("SysDictionaryDetails", func(db *gorm.DB) *gorm.DB {
|
||||
if includeDisabled {
|
||||
return db.Order("sort")
|
||||
} else {
|
||||
return db.Where("status = ?", true).Order("sort")
|
||||
}
|
||||
}).Find(&sysDictionaries).Error
|
||||
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询字典列表失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "查询字典列表失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
for _, dict := range sysDictionaries {
|
||||
dictInfo := DictionaryInfo{
|
||||
ID: dict.ID,
|
||||
Name: dict.Name,
|
||||
Type: dict.Type,
|
||||
Status: dict.Status,
|
||||
Desc: dict.Desc,
|
||||
}
|
||||
|
||||
// 获取字典详情
|
||||
for _, detail := range dict.SysDictionaryDetails {
|
||||
if includeDisabled || (detail.Status != nil && *detail.Status) {
|
||||
dictInfo.Details = append(dictInfo.Details, DictionaryDetailInfo{
|
||||
ID: detail.ID,
|
||||
Label: detail.Label,
|
||||
Value: detail.Value,
|
||||
Extend: detail.Extend,
|
||||
Status: detail.Status,
|
||||
Sort: detail.Sort,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries = append(dictionaries, dictInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只需要详情信息,则提取所有详情
|
||||
if detailsOnly {
|
||||
var allDetails []DictionaryDetailInfo
|
||||
for _, dict := range dictionaries {
|
||||
allDetails = append(allDetails, dict.Details...)
|
||||
}
|
||||
|
||||
response := map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "查询字典详情成功",
|
||||
"total": len(allDetails),
|
||||
"details": allDetails,
|
||||
}
|
||||
|
||||
responseJSON, _ := json.Marshal(response)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := DictionaryQueryResponse{
|
||||
Success: true,
|
||||
Message: "查询字典成功",
|
||||
Total: len(dictionaries),
|
||||
Dictionaries: dictionaries,
|
||||
}
|
||||
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("序列化响应失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf(`{"success": false, "message": "序列化响应失败: %v", "total": 0, "dictionaries": []}`, err.Error())),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
31
server/mcp/enter.go
Normal file
31
server/mcp/enter.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
// McpTool 定义了MCP工具必须实现的接口
|
||||
type McpTool interface {
|
||||
// Handle 返回工具调用信息
|
||||
Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
|
||||
// New 返回工具注册信息
|
||||
New() mcp.Tool
|
||||
}
|
||||
|
||||
// 工具注册表
|
||||
var toolRegister = make(map[string]McpTool)
|
||||
|
||||
// RegisterTool 供工具在init时调用,将自己注册到工具注册表中
|
||||
func RegisterTool(tool McpTool) {
|
||||
mcpTool := tool.New()
|
||||
toolRegister[mcpTool.Name] = tool
|
||||
}
|
||||
|
||||
// RegisterAllTools 将所有注册的工具注册到MCP服务中
|
||||
func RegisterAllTools(mcpServer *server.MCPServer) {
|
||||
for _, tool := range toolRegister {
|
||||
mcpServer.AddTool(tool.New(), tool.Handle)
|
||||
}
|
||||
}
|
||||
502
server/mcp/gva_analyze.go
Normal file
502
server/mcp/gva_analyze.go
Normal file
@@ -0,0 +1,502 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
model "git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAAnalyzer{})
|
||||
}
|
||||
|
||||
// GVAAnalyzer GVA分析器 - 用于分析当前功能是否需要创建独立的package和module
|
||||
type GVAAnalyzer struct{}
|
||||
|
||||
// AnalyzeRequest 分析请求结构体
|
||||
type AnalyzeRequest struct {
|
||||
Requirement string `json:"requirement" binding:"required"` // 用户需求描述
|
||||
}
|
||||
|
||||
// AnalyzeResponse 分析响应结构体
|
||||
type AnalyzeResponse struct {
|
||||
ExistingPackages []PackageInfo `json:"existingPackages"` // 现有包信息
|
||||
PredesignedModules []PredesignedModuleInfo `json:"predesignedModules"` // 预设计模块信息
|
||||
Dictionaries []DictionaryPre `json:"dictionaries"` // 字典信息
|
||||
CleanupInfo *CleanupInfo `json:"cleanupInfo"` // 清理信息(如果有)
|
||||
}
|
||||
|
||||
// ModuleInfo 模块信息
|
||||
type ModuleInfo struct {
|
||||
ModuleName string `json:"moduleName"` // 模块名称
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
StructName string `json:"structName"` // 结构体名称
|
||||
TableName string `json:"tableName"` // 表名
|
||||
Description string `json:"description"` // 描述
|
||||
FilePaths []string `json:"filePaths"` // 相关文件路径
|
||||
}
|
||||
|
||||
// PackageInfo 包信息
|
||||
type PackageInfo struct {
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
Label string `json:"label"` // 标签
|
||||
Desc string `json:"desc"` // 描述
|
||||
Module string `json:"module"` // 模块
|
||||
IsEmpty bool `json:"isEmpty"` // 是否为空包
|
||||
}
|
||||
|
||||
// PredesignedModuleInfo 预设计模块信息
|
||||
type PredesignedModuleInfo struct {
|
||||
ModuleName string `json:"moduleName"` // 模块名称
|
||||
PackageName string `json:"packageName"` // 包名
|
||||
Template string `json:"template"` // 模板类型
|
||||
FilePaths []string `json:"filePaths"` // 文件路径列表
|
||||
Description string `json:"description"` // 描述
|
||||
}
|
||||
|
||||
// CleanupInfo 清理信息
|
||||
type CleanupInfo struct {
|
||||
DeletedPackages []string `json:"deletedPackages"` // 已删除的包
|
||||
DeletedModules []string `json:"deletedModules"` // 已删除的模块
|
||||
CleanupMessage string `json:"cleanupMessage"` // 清理消息
|
||||
}
|
||||
|
||||
// New 创建GVA分析器工具
|
||||
func (g *GVAAnalyzer) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_analyze",
|
||||
mcp.WithDescription("返回当前系统中有效的包和模块信息,并分析用户需求是否需要创建新的包、模块和字典。同时检查并清理空包,确保系统整洁。"),
|
||||
mcp.WithString("requirement",
|
||||
mcp.Description("用户需求描述,用于分析是否需要创建新的包和模块"),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理分析请求
|
||||
func (g *GVAAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
requirementStr, ok := request.GetArguments()["requirement"].(string)
|
||||
if !ok || requirementStr == "" {
|
||||
return nil, errors.New("参数错误:requirement 必须是非空字符串")
|
||||
}
|
||||
|
||||
// 创建分析请求
|
||||
analyzeReq := AnalyzeRequest{
|
||||
Requirement: requirementStr,
|
||||
}
|
||||
|
||||
// 执行分析逻辑
|
||||
response, err := g.performAnalysis(ctx, analyzeReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("分析失败: %v", err)
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseJSON)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// performAnalysis 执行分析逻辑
|
||||
func (g *GVAAnalyzer) performAnalysis(ctx context.Context, req AnalyzeRequest) (*AnalyzeResponse, error) {
|
||||
// 1. 获取数据库中的包信息
|
||||
var packages []model.SysAutoCodePackage
|
||||
if err := global.GVA_DB.Find(&packages).Error; err != nil {
|
||||
return nil, fmt.Errorf("获取包信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取历史记录
|
||||
var histories []model.SysAutoCodeHistory
|
||||
if err := global.GVA_DB.Find(&histories).Error; err != nil {
|
||||
return nil, fmt.Errorf("获取历史记录失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 检查空包并进行清理
|
||||
cleanupInfo := &CleanupInfo{
|
||||
DeletedPackages: []string{},
|
||||
DeletedModules: []string{},
|
||||
}
|
||||
|
||||
var validPackages []model.SysAutoCodePackage
|
||||
var emptyPackageHistoryIDs []uint
|
||||
|
||||
for _, pkg := range packages {
|
||||
isEmpty, err := g.isPackageFolderEmpty(pkg.PackageName, pkg.Template)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("检查包 %s 是否为空时出错: %v", pkg.PackageName, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if isEmpty {
|
||||
// 删除空包文件夹
|
||||
if err := g.removeEmptyPackageFolder(pkg.PackageName, pkg.Template); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除空包文件夹 %s 失败: %v", pkg.PackageName, err))
|
||||
} else {
|
||||
cleanupInfo.DeletedPackages = append(cleanupInfo.DeletedPackages, pkg.PackageName)
|
||||
}
|
||||
|
||||
// 删除数据库记录
|
||||
if err := global.GVA_DB.Delete(&pkg).Error; err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除包数据库记录 %s 失败: %v", pkg.PackageName, err))
|
||||
}
|
||||
|
||||
// 收集相关的历史记录ID
|
||||
for _, history := range histories {
|
||||
if history.Package == pkg.PackageName {
|
||||
emptyPackageHistoryIDs = append(emptyPackageHistoryIDs, history.ID)
|
||||
cleanupInfo.DeletedModules = append(cleanupInfo.DeletedModules, history.StructName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validPackages = append(validPackages, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 清理空包相关的历史记录和脏历史记录
|
||||
var dirtyHistoryIDs []uint
|
||||
for _, history := range histories {
|
||||
// 检查是否为空包相关的历史记录
|
||||
for _, emptyID := range emptyPackageHistoryIDs {
|
||||
if history.ID == emptyID {
|
||||
dirtyHistoryIDs = append(dirtyHistoryIDs, history.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除脏历史记录
|
||||
if len(dirtyHistoryIDs) > 0 {
|
||||
if err := global.GVA_DB.Delete(&model.SysAutoCodeHistory{}, "id IN ?", dirtyHistoryIDs).Error; err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("删除脏历史记录失败: %v", err))
|
||||
} else {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("成功删除 %d 条脏历史记录", len(dirtyHistoryIDs)))
|
||||
}
|
||||
|
||||
// 清理相关的API和菜单记录
|
||||
if err := g.cleanupRelatedApiAndMenus(dirtyHistoryIDs); err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("清理相关API和菜单记录失败: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 扫描预设计模块
|
||||
predesignedModules, err := g.scanPredesignedModules()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描预设计模块失败: %v", err))
|
||||
predesignedModules = []PredesignedModuleInfo{} // 设置为空列表,不影响主流程
|
||||
}
|
||||
|
||||
// 7. 过滤掉与已删除包相关的模块
|
||||
filteredModules := []PredesignedModuleInfo{}
|
||||
for _, module := range predesignedModules {
|
||||
isDeleted := false
|
||||
for _, deletedPkg := range cleanupInfo.DeletedPackages {
|
||||
if module.PackageName == deletedPkg {
|
||||
isDeleted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isDeleted {
|
||||
filteredModules = append(filteredModules, module)
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 构建分析结果消息
|
||||
var analysisMessage strings.Builder
|
||||
if len(cleanupInfo.DeletedPackages) > 0 || len(cleanupInfo.DeletedModules) > 0 {
|
||||
analysisMessage.WriteString("**系统清理完成**\n\n")
|
||||
if len(cleanupInfo.DeletedPackages) > 0 {
|
||||
analysisMessage.WriteString(fmt.Sprintf("- 删除了 %d 个空包: %s\n", len(cleanupInfo.DeletedPackages), strings.Join(cleanupInfo.DeletedPackages, ", ")))
|
||||
}
|
||||
if len(cleanupInfo.DeletedModules) > 0 {
|
||||
analysisMessage.WriteString(fmt.Sprintf("- 删除了 %d 个相关模块: %s\n", len(cleanupInfo.DeletedModules), strings.Join(cleanupInfo.DeletedModules, ", ")))
|
||||
}
|
||||
analysisMessage.WriteString("\n")
|
||||
cleanupInfo.CleanupMessage = analysisMessage.String()
|
||||
}
|
||||
|
||||
analysisMessage.WriteString(" **分析结果**\n\n")
|
||||
analysisMessage.WriteString(fmt.Sprintf("- **现有包数量**: %d\n", len(validPackages)))
|
||||
analysisMessage.WriteString(fmt.Sprintf("- **预设计模块数量**: %d\n\n", len(filteredModules)))
|
||||
|
||||
// 9. 转换包信息
|
||||
existingPackages := make([]PackageInfo, len(validPackages))
|
||||
for i, pkg := range validPackages {
|
||||
existingPackages[i] = PackageInfo{
|
||||
PackageName: pkg.PackageName,
|
||||
Template: pkg.Template,
|
||||
Label: pkg.Label,
|
||||
Desc: pkg.Desc,
|
||||
Module: pkg.Module,
|
||||
IsEmpty: false, // 已经过滤掉空包
|
||||
}
|
||||
}
|
||||
|
||||
dictionaries := []DictionaryPre{} // 这里可以根据需要填充字典信息
|
||||
err = global.GVA_DB.Table("sys_dictionaries").Find(&dictionaries, "deleted_at is null").Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("获取字典信息失败: %v", err))
|
||||
dictionaries = []DictionaryPre{} // 设置为空列表,不影响主流程
|
||||
}
|
||||
|
||||
// 10. 构建响应
|
||||
response := &AnalyzeResponse{
|
||||
ExistingPackages: existingPackages,
|
||||
PredesignedModules: filteredModules,
|
||||
Dictionaries: dictionaries,
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// isPackageFolderEmpty 检查包文件夹是否为空
|
||||
func (g *GVAAnalyzer) isPackageFolderEmpty(packageName, template string) (bool, error) {
|
||||
// 根据模板类型确定基础路径
|
||||
var basePath string
|
||||
if template == "plugin" {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName)
|
||||
} else {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", packageName)
|
||||
}
|
||||
|
||||
// 检查文件夹是否存在
|
||||
if _, err := os.Stat(basePath); os.IsNotExist(err) {
|
||||
return true, nil // 文件夹不存在,视为空
|
||||
} else if err != nil {
|
||||
return false, err // 其他错误
|
||||
}
|
||||
// 递归检查是否有.go文件
|
||||
return g.hasGoFilesRecursive(basePath)
|
||||
}
|
||||
|
||||
// hasGoFilesRecursive 递归检查目录及其子目录中是否有.go文件
|
||||
func (g *GVAAnalyzer) hasGoFilesRecursive(dirPath string) (bool, error) {
|
||||
entries, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return true, err // 读取失败,返回空
|
||||
}
|
||||
|
||||
// 检查当前目录下的.go文件
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
|
||||
return false, nil // 找到.go文件,不为空
|
||||
}
|
||||
}
|
||||
|
||||
// 递归检查子目录
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
subDirPath := filepath.Join(dirPath, entry.Name())
|
||||
isEmpty, err := g.hasGoFilesRecursive(subDirPath)
|
||||
if err != nil {
|
||||
continue // 忽略子目录的错误,继续检查其他目录
|
||||
}
|
||||
if !isEmpty {
|
||||
return false, nil // 子目录中找到.go文件,不为空
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil // 没有找到.go文件,为空
|
||||
}
|
||||
|
||||
// removeEmptyPackageFolder 删除空包文件夹
|
||||
func (g *GVAAnalyzer) removeEmptyPackageFolder(packageName, template string) error {
|
||||
var basePath string
|
||||
if template == "plugin" {
|
||||
basePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", packageName)
|
||||
} else {
|
||||
// 对于package类型,需要删除多个目录
|
||||
paths := []string{
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "model", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", packageName),
|
||||
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", packageName),
|
||||
}
|
||||
for _, path := range paths {
|
||||
if err := g.removeDirectoryIfExists(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return g.removeDirectoryIfExists(basePath)
|
||||
}
|
||||
|
||||
// removeDirectoryIfExists 删除目录(如果存在)
|
||||
func (g *GVAAnalyzer) removeDirectoryIfExists(dirPath string) error {
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
return nil // 目录不存在,无需删除
|
||||
} else if err != nil {
|
||||
return err // 其他错误
|
||||
}
|
||||
|
||||
// 检查目录中是否包含go文件
|
||||
noGoFiles, err := g.hasGoFilesRecursive(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// hasGoFilesRecursive 返回 false 表示发现了 go 文件
|
||||
if noGoFiles {
|
||||
return os.RemoveAll(dirPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupRelatedApiAndMenus 清理相关的API和菜单记录
|
||||
func (g *GVAAnalyzer) cleanupRelatedApiAndMenus(historyIDs []uint) error {
|
||||
if len(historyIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 这里可以根据需要实现具体的API和菜单清理逻辑
|
||||
// 由于涉及到具体的业务逻辑,这里只做日志记录
|
||||
global.GVA_LOG.Info(fmt.Sprintf("清理历史记录ID %v 相关的API和菜单记录", historyIDs))
|
||||
|
||||
// 可以调用service层的相关方法进行清理
|
||||
// 例如:service.ServiceGroupApp.SystemApiService.DeleteApisByIds(historyIDs)
|
||||
// 例如:service.ServiceGroupApp.MenuService.DeleteMenusByIds(historyIDs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanPredesignedModules 扫描预设计模块
|
||||
func (g *GVAAnalyzer) scanPredesignedModules() ([]PredesignedModuleInfo, error) {
|
||||
// 获取autocode配置路径
|
||||
autocodeRoot := global.GVA_CONFIG.AutoCode.Root
|
||||
if autocodeRoot == "" {
|
||||
return nil, errors.New("autocode根路径未配置")
|
||||
}
|
||||
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
// 扫描plugin目录
|
||||
pluginModules, err := g.scanPluginModules(filepath.Join(autocodeRoot, global.GVA_CONFIG.AutoCode.Server, "plugin"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描plugin模块失败: %v", err))
|
||||
} else {
|
||||
modules = append(modules, pluginModules...)
|
||||
}
|
||||
|
||||
// 扫描model目录
|
||||
modelModules, err := g.scanModelModules(filepath.Join(autocodeRoot, global.GVA_CONFIG.AutoCode.Server, "model"))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描model模块失败: %v", err))
|
||||
} else {
|
||||
modules = append(modules, modelModules...)
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanPluginModules 扫描插件模块
|
||||
func (g *GVAAnalyzer) scanPluginModules(pluginDir string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
|
||||
return modules, nil // 目录不存在,返回空列表
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(pluginDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
pluginName := entry.Name()
|
||||
pluginPath := filepath.Join(pluginDir, pluginName)
|
||||
|
||||
// 查找model目录
|
||||
modelDir := filepath.Join(pluginPath, "model")
|
||||
if _, err := os.Stat(modelDir); err == nil {
|
||||
// 扫描model目录下的模块
|
||||
pluginModules, err := g.scanModulesInDirectory(modelDir, pluginName, "plugin")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描插件 %s 的模块失败: %v", pluginName, err))
|
||||
continue
|
||||
}
|
||||
modules = append(modules, pluginModules...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanModelModules 扫描模型模块
|
||||
func (g *GVAAnalyzer) scanModelModules(modelDir string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
if _, err := os.Stat(modelDir); os.IsNotExist(err) {
|
||||
return modules, nil // 目录不存在,返回空列表
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(modelDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
packageName := entry.Name()
|
||||
packagePath := filepath.Join(modelDir, packageName)
|
||||
|
||||
// 扫描包目录下的模块
|
||||
packageModules, err := g.scanModulesInDirectory(packagePath, packageName, "package")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn(fmt.Sprintf("扫描包 %s 的模块失败: %v", packageName, err))
|
||||
continue
|
||||
}
|
||||
modules = append(modules, packageModules...)
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// scanModulesInDirectory 扫描目录中的模块
|
||||
func (g *GVAAnalyzer) scanModulesInDirectory(dir, packageName, template string) ([]PredesignedModuleInfo, error) {
|
||||
var modules []PredesignedModuleInfo
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
|
||||
moduleName := strings.TrimSuffix(entry.Name(), ".go")
|
||||
filePath := filepath.Join(dir, entry.Name())
|
||||
|
||||
module := PredesignedModuleInfo{
|
||||
ModuleName: moduleName,
|
||||
PackageName: packageName,
|
||||
Template: template,
|
||||
FilePaths: []string{filePath},
|
||||
Description: fmt.Sprintf("%s模块中的%s", packageName, moduleName),
|
||||
}
|
||||
modules = append(modules, module)
|
||||
}
|
||||
}
|
||||
|
||||
return modules, nil
|
||||
}
|
||||
792
server/mcp/gva_execute.go
Normal file
792
server/mcp/gva_execute.go
Normal file
@@ -0,0 +1,792 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
model "git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system/request"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAExecutor{})
|
||||
}
|
||||
|
||||
// GVAExecutor GVA代码生成器
|
||||
type GVAExecutor struct{}
|
||||
|
||||
// ExecuteRequest 执行请求结构
|
||||
type ExecuteRequest struct {
|
||||
ExecutionPlan ExecutionPlan `json:"executionPlan"` // 执行计划
|
||||
Requirement string `json:"requirement"` // 原始需求(可选,用于日志记录)
|
||||
}
|
||||
|
||||
// ExecuteResponse 执行响应结构
|
||||
type ExecuteResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PackageID uint `json:"packageId,omitempty"`
|
||||
HistoryID uint `json:"historyId,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
GeneratedPaths []string `json:"generatedPaths,omitempty"`
|
||||
NextActions []string `json:"nextActions,omitempty"`
|
||||
}
|
||||
|
||||
// ExecutionPlan 执行计划结构
|
||||
type ExecutionPlan struct {
|
||||
PackageName string `json:"packageName"`
|
||||
PackageType string `json:"packageType"` // "plugin" 或 "package"
|
||||
NeedCreatedPackage bool `json:"needCreatedPackage"`
|
||||
NeedCreatedModules bool `json:"needCreatedModules"`
|
||||
NeedCreatedDictionaries bool `json:"needCreatedDictionaries"`
|
||||
PackageInfo *request.SysAutoCodePackageCreate `json:"packageInfo,omitempty"`
|
||||
ModulesInfo []*request.AutoCode `json:"modulesInfo,omitempty"`
|
||||
Paths map[string]string `json:"paths,omitempty"`
|
||||
DictionariesInfo []*DictionaryGenerateRequest `json:"dictionariesInfo,omitempty"`
|
||||
}
|
||||
|
||||
// New 创建GVA代码生成执行器工具
|
||||
func (g *GVAExecutor) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_execute",
|
||||
mcp.WithDescription(`**GVA代码生成执行器:直接执行代码生成,无需确认步骤**
|
||||
|
||||
**核心功能:**
|
||||
根据需求分析和当前的包信息判断是否调用,直接生成代码。支持批量创建多个模块、自动创建包、模块、字典等。
|
||||
|
||||
**使用场景:**
|
||||
在gva_analyze获取了当前的包信息和字典信息之后,如果已经包含了可以使用的包和模块,那就不要调用本mcp。根据分析结果直接生成代码,适用于自动化代码生成流程。
|
||||
|
||||
**重要提示:**
|
||||
- 当needCreatedModules=true时,模块创建会自动生成API和菜单,不应再调用api_creator和menu_creator工具
|
||||
- 字段使用字典类型时,系统会自动检查并创建字典
|
||||
- 字典创建会在模块创建之前执行
|
||||
- 当字段配置了dataSource且association=2(一对多关联)时,系统会自动将fieldType修改为'array'`),
|
||||
mcp.WithObject("executionPlan",
|
||||
mcp.Description("执行计划,包含包信息、模块与字典信息"),
|
||||
mcp.Required(),
|
||||
mcp.Properties(map[string]interface{}{
|
||||
"packageName": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "包名(小写开头)",
|
||||
},
|
||||
"packageType": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "package 或 plugin,如果用户提到了使用插件则创建plugin,如果用户没有特定说明则一律选用package",
|
||||
"enum": []string{"package", "plugin"},
|
||||
},
|
||||
"needCreatedPackage": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建包,为true时packageInfo必需",
|
||||
},
|
||||
"needCreatedModules": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建模块,为true时modulesInfo必需",
|
||||
},
|
||||
"needCreatedDictionaries": map[string]interface{}{
|
||||
"type": "boolean",
|
||||
"description": "是否需要创建字典,为true时dictionariesInfo必需",
|
||||
},
|
||||
"packageInfo": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "包创建信息,当needCreatedPackage=true时必需",
|
||||
"properties": map[string]interface{}{
|
||||
"desc": map[string]interface{}{"type": "string", "description": "包描述"},
|
||||
"label": map[string]interface{}{"type": "string", "description": "展示名"},
|
||||
"template": map[string]interface{}{"type": "string", "description": "package 或 plugin,如果用户提到了使用插件则创建plugin,如果用户没有特定说明则一律选用package", "enum": []string{"package", "plugin"}},
|
||||
"packageName": map[string]interface{}{"type": "string", "description": "包名"},
|
||||
},
|
||||
},
|
||||
"modulesInfo": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "模块配置列表,支持批量创建多个模块",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"package": map[string]interface{}{"type": "string", "description": "包名(小写开头,示例: userInfo)"},
|
||||
"tableName": map[string]interface{}{"type": "string", "description": "数据库表名(蛇形命名法,示例:user_info)"},
|
||||
"businessDB": map[string]interface{}{"type": "string", "description": "业务数据库(可留空表示默认)"},
|
||||
"structName": map[string]interface{}{"type": "string", "description": "结构体名(大驼峰示例:UserInfo)"},
|
||||
"packageName": map[string]interface{}{"type": "string", "description": "文件名称"},
|
||||
"description": map[string]interface{}{"type": "string", "description": "中文描述"},
|
||||
"abbreviation": map[string]interface{}{"type": "string", "description": "简称"},
|
||||
"humpPackageName": map[string]interface{}{"type": "string", "description": "文件名称(小驼峰),一般是结构体名的小驼峰示例:userInfo"},
|
||||
"gvaModel": map[string]interface{}{"type": "boolean", "description": "是否使用GVA模型(固定为true),自动包含ID、CreatedAt、UpdatedAt、DeletedAt字段"},
|
||||
"autoMigrate": map[string]interface{}{"type": "boolean", "description": "是否自动迁移数据库"},
|
||||
"autoCreateResource": map[string]interface{}{"type": "boolean", "description": "是否创建资源(默认为false)"},
|
||||
"autoCreateApiToSql": map[string]interface{}{"type": "boolean", "description": "是否创建API(默认为true)"},
|
||||
"autoCreateMenuToSql": map[string]interface{}{"type": "boolean", "description": "是否创建菜单(默认为true)"},
|
||||
"autoCreateBtnAuth": map[string]interface{}{"type": "boolean", "description": "是否创建按钮权限(默认为false)"},
|
||||
"onlyTemplate": map[string]interface{}{"type": "boolean", "description": "是否仅模板(默认为false)"},
|
||||
"isTree": map[string]interface{}{"type": "boolean", "description": "是否树形结构(默认为false)"},
|
||||
"treeJson": map[string]interface{}{"type": "string", "description": "树形JSON字段"},
|
||||
"isAdd": map[string]interface{}{"type": "boolean", "description": "是否新增(固定为false)"},
|
||||
"generateWeb": map[string]interface{}{"type": "boolean", "description": "是否生成前端代码"},
|
||||
"generateServer": map[string]interface{}{"type": "boolean", "description": "是否生成后端代码"},
|
||||
"fields": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字段列表",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"fieldName": map[string]interface{}{"type": "string", "description": "字段名(必须大写开头示例:UserName)"},
|
||||
"fieldDesc": map[string]interface{}{"type": "string", "description": "字段描述"},
|
||||
"fieldType": map[string]interface{}{"type": "string", "description": "字段类型:string(字符串)、richtext(富文本)、int(整型)、bool(布尔值)、float64(浮点型)、time.Time(时间)、enum(枚举)、picture(单图片)、pictures(多图片)、video(视频)、file(文件)、json(JSON)、array(数组)"},
|
||||
"fieldJson": map[string]interface{}{"type": "string", "description": "JSON标签,示例: userName"},
|
||||
"dataTypeLong": map[string]interface{}{"type": "string", "description": "数据长度"},
|
||||
"comment": map[string]interface{}{"type": "string", "description": "注释"},
|
||||
"columnName": map[string]interface{}{"type": "string", "description": "数据库列名,示例: user_name"},
|
||||
"fieldSearchType": map[string]interface{}{"type": "string", "description": "搜索类型:=、!=、>、>=、<、<=、LIKE、BETWEEN、IN、NOT IN、NOT BETWEEN"},
|
||||
"fieldSearchHide": map[string]interface{}{"type": "boolean", "description": "是否隐藏搜索"},
|
||||
"dictType": map[string]interface{}{"type": "string", "description": "字典类型,使用字典类型时系统会自动检查并创建字典"},
|
||||
"form": map[string]interface{}{"type": "boolean", "description": "表单显示"},
|
||||
"table": map[string]interface{}{"type": "boolean", "description": "表格显示"},
|
||||
"desc": map[string]interface{}{"type": "boolean", "description": "详情显示"},
|
||||
"excel": map[string]interface{}{"type": "boolean", "description": "导入导出"},
|
||||
"require": map[string]interface{}{"type": "boolean", "description": "是否必填"},
|
||||
"defaultValue": map[string]interface{}{"type": "string", "description": "默认值"},
|
||||
"errorText": map[string]interface{}{"type": "string", "description": "错误提示"},
|
||||
"clearable": map[string]interface{}{"type": "boolean", "description": "是否可清空"},
|
||||
"sort": map[string]interface{}{"type": "boolean", "description": "是否排序"},
|
||||
"primaryKey": map[string]interface{}{"type": "boolean", "description": "是否主键(gvaModel=false时必须有一个字段为true)"},
|
||||
"dataSource": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "数据源配置,用于配置字段的关联表信息。获取表名提示:可在 server/model 和 plugin/xxx/model 目录下查看对应模块的 TableName() 接口实现获取实际表名(如 SysUser 的表名为 sys_users)。获取数据库名提示:主数据库通常使用 gva(默认数据库标识),多数据库可在 config.yaml 的 db-list 配置中查看可用数据库的 alias-name 字段,如果用户未提及关联多数据库信息则使用默认数据库,默认数据库的情况下 dbName填写为空",
|
||||
"properties": map[string]interface{}{
|
||||
"dbName": map[string]interface{}{"type": "string", "description": "关联的数据库名称(默认数据库留空)"},
|
||||
"table": map[string]interface{}{"type": "string", "description": "关联的表名"},
|
||||
"label": map[string]interface{}{"type": "string", "description": "用于显示的字段名(如name、title等)"},
|
||||
"value": map[string]interface{}{"type": "string", "description": "用于存储的值字段名(通常是id)"},
|
||||
"association": map[string]interface{}{"type": "integer", "description": "关联关系类型:1=一对一关联,2=一对多关联。一对一和一对多的前面的一是当前的实体,如果他只能关联另一个实体的一个则选用一对一,如果他需要关联多个他的关联实体则选用一对多"},
|
||||
"hasDeletedAt": map[string]interface{}{"type": "boolean", "description": "关联表是否有软删除字段"},
|
||||
},
|
||||
},
|
||||
"checkDataSource": map[string]interface{}{"type": "boolean", "description": "是否检查数据源,启用后会验证关联表的存在性"},
|
||||
"fieldIndexType": map[string]interface{}{"type": "string", "description": "索引类型"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"paths": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "生成的文件路径映射",
|
||||
"additionalProperties": map[string]interface{}{"type": "string"},
|
||||
},
|
||||
"dictionariesInfo": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字典创建信息,字典创建会在模块创建之前执行",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"dictType": map[string]interface{}{"type": "string", "description": "字典类型,用于标识字典的唯一性"},
|
||||
"dictName": map[string]interface{}{"type": "string", "description": "字典名称,必须生成,字典的中文名称"},
|
||||
"description": map[string]interface{}{"type": "string", "description": "字典描述,字典的用途说明"},
|
||||
"status": map[string]interface{}{"type": "boolean", "description": "字典状态:true启用,false禁用"},
|
||||
"fieldDesc": map[string]interface{}{"type": "string", "description": "字段描述,用于AI理解字段含义并生成合适的选项"},
|
||||
"options": map[string]interface{}{
|
||||
"type": "array",
|
||||
"description": "字典选项列表(可选,如果不提供将根据fieldDesc自动生成默认选项)",
|
||||
"items": map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"label": map[string]interface{}{"type": "string", "description": "显示名称,用户看到的选项名"},
|
||||
"value": map[string]interface{}{"type": "string", "description": "选项值,实际存储的值"},
|
||||
"sort": map[string]interface{}{"type": "integer", "description": "排序号,数字越小越靠前"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
mcp.AdditionalProperties(false),
|
||||
),
|
||||
mcp.WithString("requirement",
|
||||
mcp.Description("原始需求描述(可选,用于日志记录)"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理执行请求(移除确认步骤)
|
||||
func (g *GVAExecutor) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
executionPlanData, ok := request.GetArguments()["executionPlan"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:executionPlan 必须提供")
|
||||
}
|
||||
|
||||
// 解析执行计划
|
||||
planJSON, err := json.Marshal(executionPlanData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析执行计划失败: %v", err)
|
||||
}
|
||||
|
||||
var plan ExecutionPlan
|
||||
err = json.Unmarshal(planJSON, &plan)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析执行计划失败: %v\n\n请确保ExecutionPlan格式正确,参考工具描述中的结构体格式要求", err)
|
||||
}
|
||||
|
||||
// 验证执行计划的完整性
|
||||
if err := g.validateExecutionPlan(&plan); err != nil {
|
||||
return nil, fmt.Errorf("执行计划验证失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取原始需求(可选)
|
||||
var originalRequirement string
|
||||
if reqData, ok := request.GetArguments()["requirement"]; ok {
|
||||
if reqStr, ok := reqData.(string); ok {
|
||||
originalRequirement = reqStr
|
||||
}
|
||||
}
|
||||
|
||||
// 直接执行创建操作(无确认步骤)
|
||||
result := g.executeCreation(ctx, &plan)
|
||||
|
||||
// 如果执行成功且有原始需求,提供代码复检建议
|
||||
var reviewMessage string
|
||||
if result.Success && originalRequirement != "" {
|
||||
global.GVA_LOG.Info("执行完成,返回生成的文件路径供AI进行代码复检...")
|
||||
|
||||
// 构建文件路径信息供AI使用
|
||||
var pathsInfo []string
|
||||
for _, path := range result.GeneratedPaths {
|
||||
pathsInfo = append(pathsInfo, fmt.Sprintf("- %s", path))
|
||||
}
|
||||
|
||||
reviewMessage = fmt.Sprintf("\n\n📁 已生成以下文件:\n%s\n\n💡 提示:可以检查生成的代码是否满足原始需求。", strings.Join(pathsInfo, "\n"))
|
||||
} else if originalRequirement == "" {
|
||||
reviewMessage = "\n\n💡 提示:如需代码复检,请提供原始需求描述。"
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
response := ExecuteResponse{
|
||||
Success: result.Success,
|
||||
Message: result.Message,
|
||||
PackageID: result.PackageID,
|
||||
HistoryID: result.HistoryID,
|
||||
Paths: result.Paths,
|
||||
GeneratedPaths: result.GeneratedPaths,
|
||||
NextActions: result.NextActions,
|
||||
}
|
||||
|
||||
responseJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("执行结果:\n\n%s%s", string(responseJSON), reviewMessage)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validateExecutionPlan 验证执行计划的完整性
|
||||
func (g *GVAExecutor) validateExecutionPlan(plan *ExecutionPlan) error {
|
||||
// 验证基本字段
|
||||
if plan.PackageName == "" {
|
||||
return errors.New("packageName 不能为空")
|
||||
}
|
||||
if plan.PackageType != "package" && plan.PackageType != "plugin" {
|
||||
return errors.New("packageType 必须是 'package' 或 'plugin'")
|
||||
}
|
||||
|
||||
// 验证packageType和template字段的一致性
|
||||
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
|
||||
if plan.PackageType != plan.PackageInfo.Template {
|
||||
return errors.New("packageType 和 packageInfo.template 必须保持一致")
|
||||
}
|
||||
}
|
||||
|
||||
// 验证包信息
|
||||
if plan.NeedCreatedPackage {
|
||||
if plan.PackageInfo == nil {
|
||||
return errors.New("当 needCreatedPackage=true 时,packageInfo 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.PackageName == "" {
|
||||
return errors.New("packageInfo.packageName 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.Template != "package" && plan.PackageInfo.Template != "plugin" {
|
||||
return errors.New("packageInfo.template 必须是 'package' 或 'plugin'")
|
||||
}
|
||||
if plan.PackageInfo.Label == "" {
|
||||
return errors.New("packageInfo.label 不能为空")
|
||||
}
|
||||
if plan.PackageInfo.Desc == "" {
|
||||
return errors.New("packageInfo.desc 不能为空")
|
||||
}
|
||||
}
|
||||
|
||||
// 验证模块信息(批量验证)
|
||||
if plan.NeedCreatedModules {
|
||||
if len(plan.ModulesInfo) == 0 {
|
||||
return errors.New("当 needCreatedModules=true 时,modulesInfo 不能为空")
|
||||
}
|
||||
|
||||
// 遍历验证每个模块
|
||||
for moduleIndex, moduleInfo := range plan.ModulesInfo {
|
||||
if moduleInfo.Package == "" {
|
||||
return fmt.Errorf("模块 %d 的 package 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.StructName == "" {
|
||||
return fmt.Errorf("模块 %d 的 structName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.TableName == "" {
|
||||
return fmt.Errorf("模块 %d 的 tableName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.Description == "" {
|
||||
return fmt.Errorf("模块 %d 的 description 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.Abbreviation == "" {
|
||||
return fmt.Errorf("模块 %d 的 abbreviation 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.PackageName == "" {
|
||||
return fmt.Errorf("模块 %d 的 packageName 不能为空", moduleIndex+1)
|
||||
}
|
||||
if moduleInfo.HumpPackageName == "" {
|
||||
return fmt.Errorf("模块 %d 的 humpPackageName 不能为空", moduleIndex+1)
|
||||
}
|
||||
|
||||
// 验证字段信息
|
||||
if len(moduleInfo.Fields) == 0 {
|
||||
return fmt.Errorf("模块 %d 的 fields 不能为空,至少需要一个字段", moduleIndex+1)
|
||||
}
|
||||
|
||||
for i, field := range moduleInfo.Fields {
|
||||
if field.FieldName == "" {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldName 不能为空", moduleIndex+1, i+1)
|
||||
}
|
||||
|
||||
// 确保字段名首字母大写
|
||||
if len(field.FieldName) > 0 {
|
||||
firstChar := string(field.FieldName[0])
|
||||
if firstChar >= "a" && firstChar <= "z" {
|
||||
moduleInfo.Fields[i].FieldName = strings.ToUpper(firstChar) + field.FieldName[1:]
|
||||
}
|
||||
}
|
||||
if field.FieldDesc == "" {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldDesc 不能为空", moduleIndex+1, i+1)
|
||||
}
|
||||
if field.FieldType == "" {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldType 不能为空", moduleIndex+1, i+1)
|
||||
}
|
||||
if field.FieldJson == "" {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldJson 不能为空", moduleIndex+1, i+1)
|
||||
}
|
||||
if field.ColumnName == "" {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 columnName 不能为空", moduleIndex+1, i+1)
|
||||
}
|
||||
|
||||
// 验证字段类型
|
||||
validFieldTypes := []string{"string", "int", "int64", "float64", "bool", "time.Time", "enum", "picture", "video", "file", "pictures", "array", "richtext", "json"}
|
||||
validType := false
|
||||
for _, validFieldType := range validFieldTypes {
|
||||
if field.FieldType == validFieldType {
|
||||
validType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldType, validFieldTypes)
|
||||
}
|
||||
|
||||
// 验证搜索类型(如果设置了)
|
||||
if field.FieldSearchType != "" {
|
||||
validSearchTypes := []string{"=", "!=", ">", ">=", "<", "<=", "LIKE", "BETWEEN", "IN", "NOT IN"}
|
||||
validSearchType := false
|
||||
for _, validType := range validSearchTypes {
|
||||
if field.FieldSearchType == validType {
|
||||
validSearchType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSearchType {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 fieldSearchType '%s' 不支持,支持的类型:%v", moduleIndex+1, i+1, field.FieldSearchType, validSearchTypes)
|
||||
}
|
||||
}
|
||||
|
||||
// 验证 dataSource 字段配置
|
||||
if field.DataSource != nil {
|
||||
associationValue := field.DataSource.Association
|
||||
// 当 association 为 2(一对多关联)时,强制修改 fieldType 为 array
|
||||
if associationValue == 2 {
|
||||
if field.FieldType != "array" {
|
||||
global.GVA_LOG.Info(fmt.Sprintf("模块 %d 字段 %d:检测到一对多关联(association=2),自动将 fieldType 从 '%s' 修改为 'array'", moduleIndex+1, i+1, field.FieldType))
|
||||
moduleInfo.Fields[i].FieldType = "array"
|
||||
}
|
||||
}
|
||||
|
||||
// 验证 association 值的有效性
|
||||
if associationValue != 1 && associationValue != 2 {
|
||||
return fmt.Errorf("模块 %d 字段 %d 的 dataSource.association 必须是 1(一对一)或 2(一对多)", moduleIndex+1, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证主键设置
|
||||
if !moduleInfo.GvaModel {
|
||||
// 当不使用GVA模型时,必须有且仅有一个字段设置为主键
|
||||
primaryKeyCount := 0
|
||||
for _, field := range moduleInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
primaryKeyCount++
|
||||
}
|
||||
}
|
||||
if primaryKeyCount == 0 {
|
||||
return fmt.Errorf("模块 %d:当 gvaModel=false 时,必须有一个字段的 primaryKey=true", moduleIndex+1)
|
||||
}
|
||||
if primaryKeyCount > 1 {
|
||||
return fmt.Errorf("模块 %d:当 gvaModel=false 时,只能有一个字段的 primaryKey=true", moduleIndex+1)
|
||||
}
|
||||
} else {
|
||||
// 当使用GVA模型时,所有字段的primaryKey都应该为false
|
||||
for i, field := range moduleInfo.Fields {
|
||||
if field.PrimaryKey {
|
||||
return fmt.Errorf("模块 %d:当 gvaModel=true 时,字段 %d 的 primaryKey 应该为 false,系统会自动创建ID主键", moduleIndex+1, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// executeCreation 执行创建操作
|
||||
func (g *GVAExecutor) executeCreation(ctx context.Context, plan *ExecutionPlan) *ExecuteResponse {
|
||||
result := &ExecuteResponse{
|
||||
Success: false,
|
||||
Paths: make(map[string]string),
|
||||
GeneratedPaths: []string{}, // 初始化生成文件路径列表
|
||||
}
|
||||
|
||||
// 无论如何都先构建目录结构信息,确保paths始终返回
|
||||
result.Paths = g.buildDirectoryStructure(plan)
|
||||
|
||||
// 记录预期生成的文件路径
|
||||
result.GeneratedPaths = g.collectExpectedFilePaths(plan)
|
||||
|
||||
if !plan.NeedCreatedModules {
|
||||
result.Success = true
|
||||
result.Message += "已列出当前功能所涉及的目录结构信息; 请在paths中查看; 并且在对应指定文件中实现相关的业务逻辑; "
|
||||
return result
|
||||
}
|
||||
|
||||
// 创建包(如果需要)
|
||||
if plan.NeedCreatedPackage && plan.PackageInfo != nil {
|
||||
packageService := service.ServiceGroupApp.SystemServiceGroup.AutoCodePackage
|
||||
err := packageService.Create(ctx, plan.PackageInfo)
|
||||
if err != nil {
|
||||
result.Message = fmt.Sprintf("创建包失败: %v", err)
|
||||
// 即使创建包失败,也要返回paths信息
|
||||
return result
|
||||
}
|
||||
result.Message += "包创建成功; "
|
||||
}
|
||||
|
||||
// 创建指定字典(如果需要)
|
||||
if plan.NeedCreatedDictionaries && len(plan.DictionariesInfo) > 0 {
|
||||
dictResult := g.createDictionariesFromInfo(ctx, plan.DictionariesInfo)
|
||||
result.Message += dictResult
|
||||
}
|
||||
|
||||
// 批量创建字典和模块(如果需要)
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
templateService := service.ServiceGroupApp.SystemServiceGroup.AutoCodeTemplate
|
||||
|
||||
// 遍历所有模块进行创建
|
||||
for _, moduleInfo := range plan.ModulesInfo {
|
||||
|
||||
// 创建模块
|
||||
err := moduleInfo.Pretreatment()
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("模块 %s 信息预处理失败: %v; ", moduleInfo.StructName, err)
|
||||
continue // 继续处理下一个模块
|
||||
}
|
||||
|
||||
err = templateService.Create(ctx, *moduleInfo)
|
||||
if err != nil {
|
||||
result.Message += fmt.Sprintf("创建模块 %s 失败: %v; ", moduleInfo.StructName, err)
|
||||
continue // 继续处理下一个模块
|
||||
}
|
||||
result.Message += fmt.Sprintf("模块 %s 创建成功; ", moduleInfo.StructName)
|
||||
}
|
||||
|
||||
result.Message += fmt.Sprintf("批量创建完成,共处理 %d 个模块; ", len(plan.ModulesInfo))
|
||||
|
||||
// 添加重要提醒:不要使用其他MCP工具
|
||||
result.Message += "\n\n⚠️ 重要提醒:\n"
|
||||
result.Message += "模块创建已完成,API和菜单已自动生成。请不要再调用以下MCP工具:\n"
|
||||
result.Message += "- api_creator:API权限已在模块创建时自动生成\n"
|
||||
result.Message += "- menu_creator:前端菜单已在模块创建时自动生成\n"
|
||||
result.Message += "如需修改API或菜单,请直接在系统管理界面中进行配置。\n"
|
||||
}
|
||||
|
||||
result.Message += "已构建目录结构信息; "
|
||||
result.Success = true
|
||||
|
||||
if result.Message == "" {
|
||||
result.Message = "执行计划完成"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// buildDirectoryStructure 构建目录结构信息
|
||||
func (g *GVAExecutor) buildDirectoryStructure(plan *ExecutionPlan) map[string]string {
|
||||
paths := make(map[string]string)
|
||||
|
||||
// 获取配置信息
|
||||
autoCodeConfig := global.GVA_CONFIG.AutoCode
|
||||
|
||||
// 构建基础路径
|
||||
rootPath := autoCodeConfig.Root
|
||||
serverPath := autoCodeConfig.Server
|
||||
webPath := autoCodeConfig.Web
|
||||
moduleName := autoCodeConfig.Module
|
||||
|
||||
// 如果计划中有包名,使用计划中的包名,否则使用默认
|
||||
packageName := "example"
|
||||
if plan.PackageName != "" {
|
||||
packageName = plan.PackageName
|
||||
}
|
||||
|
||||
// 如果计划中有模块信息,获取第一个模块的结构名作为默认值
|
||||
structName := "ExampleStruct"
|
||||
if len(plan.ModulesInfo) > 0 && plan.ModulesInfo[0].StructName != "" {
|
||||
structName = plan.ModulesInfo[0].StructName
|
||||
}
|
||||
|
||||
// 根据包类型构建不同的路径结构
|
||||
packageType := plan.PackageType
|
||||
if packageType == "" {
|
||||
packageType = "package" // 默认为package模式
|
||||
}
|
||||
|
||||
// 构建服务端路径
|
||||
if serverPath != "" {
|
||||
serverBasePath := fmt.Sprintf("%s/%s", rootPath, serverPath)
|
||||
|
||||
if packageType == "plugin" {
|
||||
// Plugin 模式:所有文件都在 /plugin/packageName/ 目录下
|
||||
plugingBasePath := fmt.Sprintf("%s/plugin/%s", serverBasePath, packageName)
|
||||
|
||||
// API 路径
|
||||
paths["api"] = fmt.Sprintf("%s/api", plugingBasePath)
|
||||
|
||||
// Service 路径
|
||||
paths["service"] = fmt.Sprintf("%s/service", plugingBasePath)
|
||||
|
||||
// Model 路径
|
||||
paths["model"] = fmt.Sprintf("%s/model", plugingBasePath)
|
||||
|
||||
// Router 路径
|
||||
paths["router"] = fmt.Sprintf("%s/router", plugingBasePath)
|
||||
|
||||
// Request 路径
|
||||
paths["request"] = fmt.Sprintf("%s/model/request", plugingBasePath)
|
||||
|
||||
// Response 路径
|
||||
paths["response"] = fmt.Sprintf("%s/model/response", plugingBasePath)
|
||||
|
||||
// Plugin 特有文件
|
||||
paths["plugin_main"] = fmt.Sprintf("%s/main.go", plugingBasePath)
|
||||
paths["plugin_config"] = fmt.Sprintf("%s/plugin.go", plugingBasePath)
|
||||
paths["plugin_initialize"] = fmt.Sprintf("%s/initialize", plugingBasePath)
|
||||
} else {
|
||||
// Package 模式:传统的目录结构
|
||||
// API 路径
|
||||
paths["api"] = fmt.Sprintf("%s/api/v1/%s", serverBasePath, packageName)
|
||||
|
||||
// Service 路径
|
||||
paths["service"] = fmt.Sprintf("%s/service/%s", serverBasePath, packageName)
|
||||
|
||||
// Model 路径
|
||||
paths["model"] = fmt.Sprintf("%s/model/%s", serverBasePath, packageName)
|
||||
|
||||
// Router 路径
|
||||
paths["router"] = fmt.Sprintf("%s/router/%s", serverBasePath, packageName)
|
||||
|
||||
// Request 路径
|
||||
paths["request"] = fmt.Sprintf("%s/model/%s/request", serverBasePath, packageName)
|
||||
|
||||
// Response 路径
|
||||
paths["response"] = fmt.Sprintf("%s/model/%s/response", serverBasePath, packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建前端路径(两种模式相同)
|
||||
if webPath != "" {
|
||||
webBasePath := fmt.Sprintf("%s/%s", rootPath, webPath)
|
||||
|
||||
if packageType == "plugin" {
|
||||
// Plugin 模式:前端文件也在 /plugin/packageName/ 目录下
|
||||
pluginWebBasePath := fmt.Sprintf("%s/plugin/%s", webBasePath, packageName)
|
||||
|
||||
// Vue 页面路径
|
||||
paths["vue_page"] = fmt.Sprintf("%s/view", pluginWebBasePath)
|
||||
|
||||
// API 路径
|
||||
paths["vue_api"] = fmt.Sprintf("%s/api", pluginWebBasePath)
|
||||
} else {
|
||||
// Package 模式:传统的目录结构
|
||||
// Vue 页面路径
|
||||
paths["vue_page"] = fmt.Sprintf("%s/view/%s", webBasePath, packageName)
|
||||
|
||||
// API 路径
|
||||
paths["vue_api"] = fmt.Sprintf("%s/api/%s", webBasePath, packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加模块信息
|
||||
paths["module"] = moduleName
|
||||
paths["package_name"] = packageName
|
||||
paths["package_type"] = packageType
|
||||
paths["struct_name"] = structName
|
||||
paths["root_path"] = rootPath
|
||||
paths["server_path"] = serverPath
|
||||
paths["web_path"] = webPath
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
// collectExpectedFilePaths 收集预期生成的文件路径
|
||||
func (g *GVAExecutor) collectExpectedFilePaths(plan *ExecutionPlan) []string {
|
||||
var paths []string
|
||||
|
||||
// 获取目录结构
|
||||
dirPaths := g.buildDirectoryStructure(plan)
|
||||
|
||||
// 如果需要创建模块,添加预期的文件路径
|
||||
if plan.NeedCreatedModules && len(plan.ModulesInfo) > 0 {
|
||||
for _, moduleInfo := range plan.ModulesInfo {
|
||||
structName := moduleInfo.StructName
|
||||
|
||||
// 后端文件
|
||||
if apiPath, ok := dirPaths["api"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", apiPath, strings.ToLower(structName)))
|
||||
}
|
||||
if servicePath, ok := dirPaths["service"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", servicePath, strings.ToLower(structName)))
|
||||
}
|
||||
if modelPath, ok := dirPaths["model"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", modelPath, strings.ToLower(structName)))
|
||||
}
|
||||
if routerPath, ok := dirPaths["router"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", routerPath, strings.ToLower(structName)))
|
||||
}
|
||||
if requestPath, ok := dirPaths["request"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", requestPath, strings.ToLower(structName)))
|
||||
}
|
||||
if responsePath, ok := dirPaths["response"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.go", responsePath, strings.ToLower(structName)))
|
||||
}
|
||||
|
||||
// 前端文件
|
||||
if vuePage, ok := dirPaths["vue_page"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.vue", vuePage, strings.ToLower(structName)))
|
||||
}
|
||||
if vueApi, ok := dirPaths["vue_api"]; ok {
|
||||
paths = append(paths, fmt.Sprintf("%s/%s.js", vueApi, strings.ToLower(structName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
// checkDictionaryExists 检查字典是否存在
|
||||
func (g *GVAExecutor) checkDictionaryExists(dictType string) (bool, error) {
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
_, err := dictionaryService.GetSysDictionary(dictType, 0, nil)
|
||||
if err != nil {
|
||||
// 如果是记录不存在的错误,返回false
|
||||
if strings.Contains(err.Error(), "record not found") {
|
||||
return false, nil
|
||||
}
|
||||
// 其他错误返回错误信息
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// createDictionariesFromInfo 根据 DictionariesInfo 创建字典
|
||||
func (g *GVAExecutor) createDictionariesFromInfo(ctx context.Context, dictionariesInfo []*DictionaryGenerateRequest) string {
|
||||
var messages []string
|
||||
dictionaryService := service.ServiceGroupApp.SystemServiceGroup.DictionaryService
|
||||
dictionaryDetailService := service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
|
||||
|
||||
messages = append(messages, fmt.Sprintf("开始创建 %d 个指定字典: ", len(dictionariesInfo)))
|
||||
|
||||
for _, dictInfo := range dictionariesInfo {
|
||||
// 检查字典是否存在
|
||||
exists, err := g.checkDictionaryExists(dictInfo.DictType)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("检查字典 %s 时出错: %v; ", dictInfo.DictType, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// 字典不存在,创建字典
|
||||
dictionary := model.SysDictionary{
|
||||
Name: dictInfo.DictName,
|
||||
Type: dictInfo.DictType,
|
||||
Status: utils.Pointer(true),
|
||||
Desc: dictInfo.Description,
|
||||
}
|
||||
|
||||
err = dictionaryService.CreateSysDictionary(dictionary)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("创建字典 %s 失败: %v; ", dictInfo.DictType, err))
|
||||
continue
|
||||
}
|
||||
|
||||
messages = append(messages, fmt.Sprintf("成功创建字典 %s (%s); ", dictInfo.DictType, dictInfo.DictName))
|
||||
|
||||
// 获取刚创建的字典ID
|
||||
var createdDict model.SysDictionary
|
||||
err = global.GVA_DB.Where("type = ?", dictInfo.DictType).First(&createdDict).Error
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("获取创建的字典失败: %v; ", err))
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建字典选项
|
||||
if len(dictInfo.Options) > 0 {
|
||||
successCount := 0
|
||||
for _, option := range dictInfo.Options {
|
||||
dictionaryDetail := model.SysDictionaryDetail{
|
||||
Label: option.Label,
|
||||
Value: option.Value,
|
||||
Status: &[]bool{true}[0], // 默认启用
|
||||
Sort: option.Sort,
|
||||
SysDictionaryID: int(createdDict.ID),
|
||||
}
|
||||
|
||||
err = dictionaryDetailService.CreateSysDictionaryDetail(dictionaryDetail)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("创建字典详情项失败", zap.Error(err))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
messages = append(messages, fmt.Sprintf("创建了 %d 个字典选项; ", successCount))
|
||||
}
|
||||
} else {
|
||||
messages = append(messages, fmt.Sprintf("字典 %s 已存在,跳过创建; ", dictInfo.DictType))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(messages, "")
|
||||
}
|
||||
170
server/mcp/gva_review.go
Normal file
170
server/mcp/gva_review.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// GVAReviewer GVA代码审查工具
|
||||
type GVAReviewer struct{}
|
||||
|
||||
// init 注册工具
|
||||
func init() {
|
||||
RegisterTool(&GVAReviewer{})
|
||||
}
|
||||
|
||||
// ReviewRequest 审查请求结构
|
||||
type ReviewRequest struct {
|
||||
UserRequirement string `json:"userRequirement"` // 经过requirement_analyze后的用户需求
|
||||
GeneratedFiles []string `json:"generatedFiles"` // gva_execute创建的文件列表
|
||||
}
|
||||
|
||||
// ReviewResponse 审查响应结构
|
||||
type ReviewResponse struct {
|
||||
Success bool `json:"success"` // 是否审查成功
|
||||
Message string `json:"message"` // 审查结果消息
|
||||
AdjustmentPrompt string `json:"adjustmentPrompt"` // 调整代码的提示
|
||||
ReviewDetails string `json:"reviewDetails"` // 详细的审查结果
|
||||
}
|
||||
|
||||
// New 创建GVA代码审查工具
|
||||
func (g *GVAReviewer) New() mcp.Tool {
|
||||
return mcp.NewTool("gva_review",
|
||||
mcp.WithDescription(`**GVA代码审查工具 - 在gva_execute调用后使用**
|
||||
|
||||
**核心功能:**
|
||||
- 接收经过requirement_analyze处理的用户需求和gva_execute生成的文件列表
|
||||
- 分析生成的代码是否满足用户的原始需求
|
||||
- 检查是否涉及到关联、交互等复杂功能
|
||||
- 如果代码不满足需求,提供调整建议和新的prompt
|
||||
|
||||
**使用场景:**
|
||||
- 在gva_execute成功执行后调用
|
||||
- 用于验证生成的代码是否完整满足用户需求
|
||||
- 检查模块间的关联关系是否正确实现
|
||||
- 发现缺失的交互功能或业务逻辑
|
||||
|
||||
**工作流程:**
|
||||
1. 接收用户原始需求和生成的文件列表
|
||||
2. 分析需求中的关键功能点
|
||||
3. 检查生成的文件是否覆盖所有功能
|
||||
4. 识别缺失的关联关系、交互功能等
|
||||
5. 生成调整建议和新的开发prompt
|
||||
|
||||
**输出内容:**
|
||||
- 审查结果和是否需要调整
|
||||
- 详细的缺失功能分析
|
||||
- 针对性的代码调整建议
|
||||
- 可直接使用的开发prompt
|
||||
|
||||
**重要提示:**
|
||||
- 本工具专门用于代码质量审查,不执行实际的代码修改
|
||||
- 重点关注模块间关联、用户交互、业务流程完整性
|
||||
- 提供的调整建议应该具体可执行`),
|
||||
mcp.WithString("userRequirement",
|
||||
mcp.Description("经过requirement_analyze处理后的用户需求描述,包含详细的功能要求和字段信息"),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithString("generatedFiles",
|
||||
mcp.Description("gva_execute创建的文件列表,JSON字符串格式,包含所有生成的后端和前端文件路径"),
|
||||
mcp.Required(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理审查请求
|
||||
func (g *GVAReviewer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 获取用户需求
|
||||
userRequirementData, ok := request.GetArguments()["userRequirement"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:userRequirement 必须提供")
|
||||
}
|
||||
|
||||
userRequirement, ok := userRequirementData.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:userRequirement 必须是字符串类型")
|
||||
}
|
||||
|
||||
// 获取生成的文件列表
|
||||
generatedFilesData, ok := request.GetArguments()["generatedFiles"]
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:generatedFiles 必须提供")
|
||||
}
|
||||
|
||||
generatedFilesStr, ok := generatedFilesData.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("参数错误:generatedFiles 必须是JSON字符串")
|
||||
}
|
||||
|
||||
// 解析JSON字符串为字符串数组
|
||||
var generatedFiles []string
|
||||
err := json.Unmarshal([]byte(generatedFilesStr), &generatedFiles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析generatedFiles失败: %v", err)
|
||||
}
|
||||
|
||||
if len(generatedFiles) == 0 {
|
||||
return nil, errors.New("参数错误:generatedFiles 不能为空")
|
||||
}
|
||||
|
||||
// 直接生成调整提示,不进行复杂分析
|
||||
adjustmentPrompt := g.generateAdjustmentPrompt(userRequirement, generatedFiles)
|
||||
|
||||
// 构建简化的审查详情
|
||||
reviewDetails := fmt.Sprintf("📋 **代码审查报告**\n\n **用户原始需求:**\n%s\n\n **已生成文件数量:** %d\n\n **建议进行代码优化和完善**", userRequirement, len(generatedFiles))
|
||||
|
||||
// 构建审查结果
|
||||
reviewResult := &ReviewResponse{
|
||||
Success: true,
|
||||
Message: "代码审查完成",
|
||||
AdjustmentPrompt: adjustmentPrompt,
|
||||
ReviewDetails: reviewDetails,
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.MarshalIndent(reviewResult, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化审查结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(fmt.Sprintf("代码审查结果:\n\n%s", string(responseJSON))),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateAdjustmentPrompt 生成调整代码的提示
|
||||
func (g *GVAReviewer) generateAdjustmentPrompt(userRequirement string, generatedFiles []string) string {
|
||||
var prompt strings.Builder
|
||||
|
||||
prompt.WriteString("🔧 **代码调整指导 Prompt:**\n\n")
|
||||
prompt.WriteString(fmt.Sprintf("**用户的原始需求为:** %s\n\n", userRequirement))
|
||||
prompt.WriteString("**经过GVA生成后的文件有如下内容:**\n")
|
||||
for _, file := range generatedFiles {
|
||||
prompt.WriteString(fmt.Sprintf("- %s\n", file))
|
||||
}
|
||||
prompt.WriteString("\n")
|
||||
|
||||
prompt.WriteString("**请帮我优化和完善代码,确保:**\n")
|
||||
prompt.WriteString("1. 代码完全满足用户的原始需求\n")
|
||||
prompt.WriteString("2. 完善模块间的关联关系,确保数据一致性\n")
|
||||
prompt.WriteString("3. 实现所有必要的用户交互功能\n")
|
||||
prompt.WriteString("4. 保持代码的完整性和可维护性\n")
|
||||
prompt.WriteString("5. 遵循GVA框架的开发规范和最佳实践\n")
|
||||
prompt.WriteString("6. 确保前后端功能完整对接\n")
|
||||
prompt.WriteString("7. 添加必要的错误处理和数据验证\n\n")
|
||||
prompt.WriteString("8. 如果需要vue路由跳转,请使用 menu_lister获取完整路由表,并且路由跳转使用 router.push({\"name\":从menu_lister中获取的name})\n\n")
|
||||
prompt.WriteString("9. 如果当前所有的vue页面内容无法满足需求,则自行书写vue文件,并且调用 menu_creator创建菜单记录\n\n")
|
||||
prompt.WriteString("10. 如果需要API调用,请使用 api_lister获取api表,根据需求调用对应接口\n\n")
|
||||
prompt.WriteString("11. 如果当前所有API无法满足则自行书写接口,补全前后端代码,并使用 api_creator创建api记录\n\n")
|
||||
prompt.WriteString("12. 无论前后端都不要随意删除import的内容\n\n")
|
||||
prompt.WriteString("**请基于用户需求和现有文件,提供完整的代码优化方案。**")
|
||||
|
||||
return prompt.String()
|
||||
}
|
||||
277
server/mcp/menu_creator.go
Normal file
277
server/mcp/menu_creator.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
RegisterTool(&MenuCreator{})
|
||||
}
|
||||
|
||||
// MenuCreateRequest 菜单创建请求结构
|
||||
type MenuCreateRequest struct {
|
||||
ParentId uint `json:"parentId"` // 父菜单ID,0表示根菜单
|
||||
Path string `json:"path"` // 路由path
|
||||
Name string `json:"name"` // 路由name
|
||||
Hidden bool `json:"hidden"` // 是否在列表隐藏
|
||||
Component string `json:"component"` // 对应前端文件路径
|
||||
Sort int `json:"sort"` // 排序标记
|
||||
Title string `json:"title"` // 菜单名
|
||||
Icon string `json:"icon"` // 菜单图标
|
||||
KeepAlive bool `json:"keepAlive"` // 是否缓存
|
||||
DefaultMenu bool `json:"defaultMenu"` // 是否是基础路由
|
||||
CloseTab bool `json:"closeTab"` // 自动关闭tab
|
||||
ActiveName string `json:"activeName"` // 高亮菜单
|
||||
Parameters []MenuParameterRequest `json:"parameters"` // 路由参数
|
||||
MenuBtn []MenuButtonRequest `json:"menuBtn"` // 菜单按钮
|
||||
}
|
||||
|
||||
// MenuParameterRequest 菜单参数请求结构
|
||||
type MenuParameterRequest struct {
|
||||
Type string `json:"type"` // 参数类型:params或query
|
||||
Key string `json:"key"` // 参数key
|
||||
Value string `json:"value"` // 参数值
|
||||
}
|
||||
|
||||
// MenuButtonRequest 菜单按钮请求结构
|
||||
type MenuButtonRequest struct {
|
||||
Name string `json:"name"` // 按钮名称
|
||||
Desc string `json:"desc"` // 按钮描述
|
||||
}
|
||||
|
||||
// MenuCreateResponse 菜单创建响应结构
|
||||
type MenuCreateResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
MenuID uint `json:"menuId"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// MenuCreator 菜单创建工具
|
||||
type MenuCreator struct{}
|
||||
|
||||
// New 创建菜单创建工具
|
||||
func (m *MenuCreator) New() mcp.Tool {
|
||||
return mcp.NewTool("create_menu",
|
||||
mcp.WithDescription(`创建前端菜单记录,用于AI编辑器自动添加前端页面时自动创建对应的菜单项。
|
||||
|
||||
**重要限制:**
|
||||
- 当使用gva_auto_generate工具且needCreatedModules=true时,模块创建会自动生成菜单项,不应调用此工具
|
||||
- 仅在以下情况使用:1) 单独创建菜单(不涉及模块创建);2) AI编辑器自动添加前端页面时`),
|
||||
mcp.WithNumber("parentId",
|
||||
mcp.Description("父菜单ID,0表示根菜单"),
|
||||
mcp.DefaultNumber(0),
|
||||
),
|
||||
mcp.WithString("path",
|
||||
mcp.Required(),
|
||||
mcp.Description("路由path,如:userList"),
|
||||
),
|
||||
mcp.WithString("name",
|
||||
mcp.Required(),
|
||||
mcp.Description("路由name,用于Vue Router,如:userList"),
|
||||
),
|
||||
mcp.WithBoolean("hidden",
|
||||
mcp.Description("是否在菜单列表中隐藏"),
|
||||
),
|
||||
mcp.WithString("component",
|
||||
mcp.Required(),
|
||||
mcp.Description("对应的前端Vue组件路径,如:view/user/list.vue"),
|
||||
),
|
||||
mcp.WithNumber("sort",
|
||||
mcp.Description("菜单排序号,数字越小越靠前"),
|
||||
mcp.DefaultNumber(1),
|
||||
),
|
||||
mcp.WithString("title",
|
||||
mcp.Required(),
|
||||
mcp.Description("菜单显示标题"),
|
||||
),
|
||||
mcp.WithString("icon",
|
||||
mcp.Description("菜单图标名称"),
|
||||
mcp.DefaultString("menu"),
|
||||
),
|
||||
mcp.WithBoolean("keepAlive",
|
||||
mcp.Description("是否缓存页面"),
|
||||
),
|
||||
mcp.WithBoolean("defaultMenu",
|
||||
mcp.Description("是否是基础路由"),
|
||||
),
|
||||
mcp.WithBoolean("closeTab",
|
||||
mcp.Description("是否自动关闭tab"),
|
||||
),
|
||||
mcp.WithString("activeName",
|
||||
mcp.Description("高亮菜单名称"),
|
||||
),
|
||||
mcp.WithString("parameters",
|
||||
mcp.Description("路由参数JSON字符串,格式:[{\"type\":\"params\",\"key\":\"id\",\"value\":\"1\"}]"),
|
||||
),
|
||||
mcp.WithString("menuBtn",
|
||||
mcp.Description("菜单按钮JSON字符串,格式:[{\"name\":\"add\",\"desc\":\"新增\"}]"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理菜单创建请求
|
||||
func (m *MenuCreator) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 解析请求参数
|
||||
args := request.GetArguments()
|
||||
|
||||
// 必需参数
|
||||
path, ok := args["path"].(string)
|
||||
if !ok || path == "" {
|
||||
return nil, errors.New("path 参数是必需的")
|
||||
}
|
||||
|
||||
name, ok := args["name"].(string)
|
||||
if !ok || name == "" {
|
||||
return nil, errors.New("name 参数是必需的")
|
||||
}
|
||||
|
||||
component, ok := args["component"].(string)
|
||||
if !ok || component == "" {
|
||||
return nil, errors.New("component 参数是必需的")
|
||||
}
|
||||
|
||||
title, ok := args["title"].(string)
|
||||
if !ok || title == "" {
|
||||
return nil, errors.New("title 参数是必需的")
|
||||
}
|
||||
|
||||
// 可选参数
|
||||
parentId := uint(0)
|
||||
if val, ok := args["parentId"].(float64); ok {
|
||||
parentId = uint(val)
|
||||
}
|
||||
|
||||
hidden := false
|
||||
if val, ok := args["hidden"].(bool); ok {
|
||||
hidden = val
|
||||
}
|
||||
|
||||
sort := 1
|
||||
if val, ok := args["sort"].(float64); ok {
|
||||
sort = int(val)
|
||||
}
|
||||
|
||||
icon := "menu"
|
||||
if val, ok := args["icon"].(string); ok && val != "" {
|
||||
icon = val
|
||||
}
|
||||
|
||||
keepAlive := false
|
||||
if val, ok := args["keepAlive"].(bool); ok {
|
||||
keepAlive = val
|
||||
}
|
||||
|
||||
defaultMenu := false
|
||||
if val, ok := args["defaultMenu"].(bool); ok {
|
||||
defaultMenu = val
|
||||
}
|
||||
|
||||
closeTab := false
|
||||
if val, ok := args["closeTab"].(bool); ok {
|
||||
closeTab = val
|
||||
}
|
||||
|
||||
activeName := ""
|
||||
if val, ok := args["activeName"].(string); ok {
|
||||
activeName = val
|
||||
}
|
||||
|
||||
// 解析参数和按钮
|
||||
var parameters []system.SysBaseMenuParameter
|
||||
if parametersStr, ok := args["parameters"].(string); ok && parametersStr != "" {
|
||||
var paramReqs []MenuParameterRequest
|
||||
if err := json.Unmarshal([]byte(parametersStr), ¶mReqs); err != nil {
|
||||
return nil, fmt.Errorf("parameters 参数格式错误: %v", err)
|
||||
}
|
||||
for _, param := range paramReqs {
|
||||
parameters = append(parameters, system.SysBaseMenuParameter{
|
||||
Type: param.Type,
|
||||
Key: param.Key,
|
||||
Value: param.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var menuBtn []system.SysBaseMenuBtn
|
||||
if menuBtnStr, ok := args["menuBtn"].(string); ok && menuBtnStr != "" {
|
||||
var btnReqs []MenuButtonRequest
|
||||
if err := json.Unmarshal([]byte(menuBtnStr), &btnReqs); err != nil {
|
||||
return nil, fmt.Errorf("menuBtn 参数格式错误: %v", err)
|
||||
}
|
||||
for _, btn := range btnReqs {
|
||||
menuBtn = append(menuBtn, system.SysBaseMenuBtn{
|
||||
Name: btn.Name,
|
||||
Desc: btn.Desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 构建菜单对象
|
||||
menu := system.SysBaseMenu{
|
||||
ParentId: parentId,
|
||||
Path: path,
|
||||
Name: name,
|
||||
Hidden: hidden,
|
||||
Component: component,
|
||||
Sort: sort,
|
||||
Meta: system.Meta{
|
||||
Title: title,
|
||||
Icon: icon,
|
||||
KeepAlive: keepAlive,
|
||||
DefaultMenu: defaultMenu,
|
||||
CloseTab: closeTab,
|
||||
ActiveName: activeName,
|
||||
},
|
||||
Parameters: parameters,
|
||||
MenuBtn: menuBtn,
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
menuService := service.ServiceGroupApp.SystemServiceGroup.MenuService
|
||||
err := menuService.AddBaseMenu(menu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建菜单失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取创建的菜单ID
|
||||
var createdMenu system.SysBaseMenu
|
||||
err = global.GVA_DB.Where("name = ? AND path = ?", name, path).First(&createdMenu).Error
|
||||
if err != nil {
|
||||
global.GVA_LOG.Warn("获取创建的菜单ID失败", zap.Error(err))
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := &MenuCreateResponse{
|
||||
Success: true,
|
||||
Message: fmt.Sprintf("成功创建菜单 %s", title),
|
||||
MenuID: createdMenu.ID,
|
||||
Name: name,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
resultJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化结果失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("菜单创建结果:\n\n%s", string(resultJSON)),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
114
server/mcp/menu_lister.go
Normal file
114
server/mcp/menu_lister.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 注册工具
|
||||
func init() {
|
||||
// 注册工具将在enter.go中统一处理
|
||||
RegisterTool(&MenuLister{})
|
||||
}
|
||||
|
||||
// MenuListResponse 菜单列表响应结构
|
||||
type MenuListResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Menus []system.SysBaseMenu `json:"menus"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// MenuLister 菜单列表工具
|
||||
type MenuLister struct{}
|
||||
|
||||
// New 创建菜单列表工具
|
||||
func (m *MenuLister) New() mcp.Tool {
|
||||
return mcp.NewTool("list_all_menus",
|
||||
mcp.WithDescription(`获取系统中所有菜单信息,包括菜单树结构、路由信息、组件路径等,用于前端编写vue-router时正确跳转
|
||||
|
||||
**功能说明:**
|
||||
- 返回完整的菜单树形结构
|
||||
- 包含路由配置信息(path、name、component)
|
||||
- 包含菜单元数据(title、icon、keepAlive等)
|
||||
- 包含菜单参数和按钮配置
|
||||
- 支持父子菜单关系展示
|
||||
|
||||
**使用场景:**
|
||||
- 前端路由配置:获取所有菜单信息用于配置vue-router
|
||||
- 菜单权限管理:了解系统中所有可用的菜单项
|
||||
- 导航组件开发:构建动态导航菜单
|
||||
- 系统架构分析:了解系统的菜单结构和页面组织`),
|
||||
mcp.WithString("_placeholder",
|
||||
mcp.Description("占位符,防止json schema校验失败"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理菜单列表请求
|
||||
func (m *MenuLister) Handle(_ context.Context, _ mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// 获取所有基础菜单
|
||||
allMenus, err := m.getAllMenus()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取菜单列表失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("获取菜单列表失败: %v", err),
|
||||
},
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
response := MenuListResponse{
|
||||
Success: true,
|
||||
Message: "获取菜单列表成功",
|
||||
Menus: allMenus,
|
||||
TotalCount: len(allMenus),
|
||||
Description: "系统中所有菜单信息的标准列表,包含路由配置和组件信息",
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseJSON, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("序列化菜单响应失败", zap.Error(err))
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: fmt.Sprintf("序列化响应失败: %v", err),
|
||||
},
|
||||
},
|
||||
IsError: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: string(responseJSON),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getAllMenus 获取所有基础菜单
|
||||
func (m *MenuLister) getAllMenus() ([]system.SysBaseMenu, error) {
|
||||
var menus []system.SysBaseMenu
|
||||
err := global.GVA_DB.Order("sort").Preload("Parameters").Preload("MenuBtn").Find(&menus).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
199
server/mcp/requirement_analyzer.go
Normal file
199
server/mcp/requirement_analyzer.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package mcpTool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterTool(&RequirementAnalyzer{})
|
||||
}
|
||||
|
||||
type RequirementAnalyzer struct{}
|
||||
|
||||
// RequirementAnalysisRequest 需求分析请求
|
||||
type RequirementAnalysisRequest struct {
|
||||
UserRequirement string `json:"userRequirement"`
|
||||
}
|
||||
|
||||
// RequirementAnalysisResponse 需求分析响应
|
||||
type RequirementAnalysisResponse struct {
|
||||
AIPrompt string `json:"aiPrompt"` // 给AI的提示词
|
||||
}
|
||||
|
||||
// New 返回工具注册信息
|
||||
func (t *RequirementAnalyzer) New() mcp.Tool {
|
||||
return mcp.NewTool("requirement_analyzer",
|
||||
mcp.WithDescription(`** 智能需求分析与模块设计工具 - 首选入口工具(最高优先级)**
|
||||
|
||||
** 重要提示:这是所有MCP工具的首选入口,请优先使用!**
|
||||
|
||||
** 核心能力:**
|
||||
作为资深系统架构师,智能分析用户需求并自动设计完整的模块架构
|
||||
|
||||
** 核心功能:**
|
||||
1. **智能需求解构**:深度分析用户需求,识别核心业务实体、业务流程、数据关系
|
||||
2. **自动模块设计**:基于需求分析,智能确定需要多少个模块及各模块功能
|
||||
3. **字段智能推导**:为每个模块自动设计详细字段,包含数据类型、关联关系、字典需求
|
||||
4. **架构优化建议**:提供模块拆分、关联设计、扩展性等专业建议
|
||||
|
||||
** 输出内容:**
|
||||
- 模块数量和架构设计
|
||||
- 每个模块的详细字段清单
|
||||
- 数据类型和关联关系设计
|
||||
- 字典需求和类型定义
|
||||
- 模块间关系图和扩展建议
|
||||
|
||||
** 适用场景:**
|
||||
- 用户需求描述不完整,需要智能补全
|
||||
- 复杂业务系统的模块架构设计
|
||||
- 需要专业的数据库设计建议
|
||||
- 想要快速搭建生产级业务系统
|
||||
|
||||
** 推荐工作流:**
|
||||
requirement_analyzer → gva_analyze → gva_execute → 其他辅助工具
|
||||
|
||||
`),
|
||||
mcp.WithString("userRequirement",
|
||||
mcp.Required(),
|
||||
mcp.Description("用户的需求描述,支持自然语言,如:'我要做一个猫舍管理系统,用来录入猫的信息,并且记录每只猫每天的活动信息'"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Handle 处理工具调用
|
||||
func (t *RequirementAnalyzer) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
userRequirement, ok := request.GetArguments()["userRequirement"].(string)
|
||||
if !ok || userRequirement == "" {
|
||||
return nil, errors.New("参数错误:userRequirement 必须是非空字符串")
|
||||
}
|
||||
|
||||
// 分析用户需求
|
||||
analysisResponse, err := t.analyzeRequirement(userRequirement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("需求分析失败: %v", err)
|
||||
}
|
||||
|
||||
// 序列化响应
|
||||
responseData, err := json.Marshal(analysisResponse)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.NewTextContent(string(responseData)),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// analyzeRequirement 分析用户需求 - 专注于AI需求传递
|
||||
func (t *RequirementAnalyzer) analyzeRequirement(userRequirement string) (*RequirementAnalysisResponse, error) {
|
||||
// 生成AI提示词 - 这是唯一功能
|
||||
aiPrompt := t.generateAIPrompt(userRequirement)
|
||||
|
||||
return &RequirementAnalysisResponse{
|
||||
AIPrompt: aiPrompt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateAIPrompt 生成AI提示词 - 智能分析需求并确定模块结构
|
||||
func (t *RequirementAnalyzer) generateAIPrompt(userRequirement string) string {
|
||||
prompt := fmt.Sprintf(`# 智能需求分析与模块设计任务
|
||||
|
||||
## 用户原始需求
|
||||
%s
|
||||
|
||||
## 核心任务
|
||||
你需要作为一个资深的系统架构师,深度分析用户需求,智能设计出完整的模块架构。
|
||||
|
||||
## 分析步骤
|
||||
|
||||
### 第一步:需求解构分析
|
||||
请仔细分析用户需求,识别出:
|
||||
1. **核心业务实体**(如:用户、商品、订单、疫苗、宠物等)
|
||||
2. **业务流程**(如:注册、购买、记录、管理等)
|
||||
3. **数据关系**(实体间的关联关系)
|
||||
4. **功能模块**(需要哪些独立的管理模块)
|
||||
|
||||
### 第二步:模块架构设计
|
||||
基于需求分析,设计出模块架构,格式如下:
|
||||
|
||||
**模块1:[模块名称]**
|
||||
- 功能描述:[该模块的核心功能]
|
||||
- 主要字段:[列出关键字段,注明数据类型]
|
||||
- 关联关系:[与其他模块的关系,明确一对一/一对多]
|
||||
- 字典需求:[需要哪些字典类型]
|
||||
|
||||
**模块2:[模块名称]**
|
||||
- 功能描述:[该模块的核心功能]
|
||||
- 主要字段:[列出关键字段,注明数据类型]
|
||||
- 关联关系:[与其他模块的关系]
|
||||
- 字典需求:[需要哪些字典类型]
|
||||
|
||||
**...**
|
||||
|
||||
### 第三步:字段详细设计
|
||||
为每个模块详细设计字段:
|
||||
|
||||
#### 模块1字段清单:
|
||||
- 字段名1 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- 字段名2 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- ...
|
||||
|
||||
#### 模块2字段清单:
|
||||
- 字段名1 (数据类型) - 字段描述 [是否必填] [关联信息/字典类型]
|
||||
- ...
|
||||
|
||||
## 智能分析指导原则
|
||||
|
||||
### 模块拆分原则
|
||||
1. **单一职责**:每个模块只负责一个核心业务实体
|
||||
2. **数据完整性**:相关数据应该在同一模块中
|
||||
3. **业务独立性**:模块应该能够独立完成特定业务功能
|
||||
4. **扩展性考虑**:为未来功能扩展预留空间
|
||||
|
||||
### 字段设计原则
|
||||
1. **必要性**:只包含业务必需的字段
|
||||
2. **规范性**:遵循数据库设计规范
|
||||
3. **关联性**:正确识别实体间关系
|
||||
4. **字典化**:状态、类型等枚举值使用字典
|
||||
|
||||
### 关联关系识别
|
||||
- **一对一**:一个实体只能关联另一个实体的一个记录
|
||||
- **一对多**:一个实体可以关联另一个实体的多个记录
|
||||
- **多对多**:通过中间表实现复杂关联
|
||||
|
||||
## 特殊场景处理
|
||||
|
||||
### 复杂实体识别
|
||||
当用户提到某个概念时,要判断它是否需要独立模块:
|
||||
- **字典处理**:简单的常见的状态、类型(如:开关、性别、完成状态等)
|
||||
- **独立模块**:复杂实体(如:疫苗管理、宠物档案、注射记录)
|
||||
|
||||
## 输出要求
|
||||
|
||||
### 必须包含的信息
|
||||
1. **模块数量**:明确需要几个模块
|
||||
2. **模块关系图**:用文字描述模块间关系
|
||||
3. **核心字段**:每个模块的关键字段(至少5-10个)
|
||||
4. **数据类型**:string、int、bool、time.Time、float64等
|
||||
5. **关联设计**:明确哪些字段是关联字段
|
||||
6. **字典需求**:列出需要创建的字典类型
|
||||
|
||||
### 严格遵循用户输入
|
||||
- 如果用户提供了具体字段,**必须使用**用户提供的字段
|
||||
- 如果用户提供了SQL文件,**严格按照**SQL结构设计
|
||||
- **不要**随意发散,**不要**添加用户未提及的功能
|
||||
---
|
||||
|
||||
**现在请开始深度分析用户需求:"%s"**
|
||||
|
||||
请按照上述框架进行系统性分析,确保输出的模块设计既满足当前需求,又具备良好的扩展性。`, userRequirement, userRequirement)
|
||||
|
||||
return prompt
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AppJWTAuth 前台用户 JWT 认证中间件
|
||||
func AppJWTAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
token = c.GetHeader("x-token")
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
response.FailWithDetailed(gin.H{"reload": true}, "未登录或非法访问", c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 移除 Bearer 前缀
|
||||
if len(token) > 7 && token[:7] == "Bearer " {
|
||||
token = token[7:]
|
||||
}
|
||||
|
||||
// 解析 token
|
||||
claims, err := utils.ParseAppToken(token)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("解析 App Token 失败: " + err.Error())
|
||||
response.FailWithDetailed(gin.H{"reload": true}, "授权已过期或无效", c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户信息存入上下文
|
||||
c.Set("appClaims", claims)
|
||||
c.Set("userId", claims.UserID)
|
||||
c.Set("username", claims.Username)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
32
server/middleware/casbin_rbac.go
Normal file
32
server/middleware/casbin_rbac.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CasbinHandler 拦截器
|
||||
func CasbinHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
waitUse, _ := utils.GetClaims(c)
|
||||
//获取请求的PATH
|
||||
path := c.Request.URL.Path
|
||||
obj := strings.TrimPrefix(path, global.GVA_CONFIG.System.RouterPrefix)
|
||||
// 获取请求方法
|
||||
act := c.Request.Method
|
||||
// 获取用户的角色
|
||||
sub := strconv.Itoa(int(waitUse.AuthorityId))
|
||||
e := utils.GetCasbin() // 判断策略中是否存在
|
||||
success, _ := e.Enforce(sub, obj, act)
|
||||
if !success {
|
||||
response.FailWithDetailed(gin.H{}, "权限不足", c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/ai_proxy/server/config"
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Cors 直接放行全部跨域请求
|
||||
// Cors 直接放行所有跨域请求并放行所有 OPTIONS 方法
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
@@ -20,6 +22,52 @@ func Cors() gin.HandlerFunc {
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// CorsByRules 按照配置处理跨域请求
|
||||
func CorsByRules() gin.HandlerFunc {
|
||||
// 放行全部
|
||||
if global.GVA_CONFIG.Cors.Mode == "allow-all" {
|
||||
return Cors()
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
whitelist := checkCors(c.GetHeader("origin"))
|
||||
|
||||
// 通过检查, 添加请求头
|
||||
if whitelist != nil {
|
||||
c.Header("Access-Control-Allow-Origin", whitelist.AllowOrigin)
|
||||
c.Header("Access-Control-Allow-Headers", whitelist.AllowHeaders)
|
||||
c.Header("Access-Control-Allow-Methods", whitelist.AllowMethods)
|
||||
c.Header("Access-Control-Expose-Headers", whitelist.ExposeHeaders)
|
||||
if whitelist.AllowCredentials {
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
}
|
||||
|
||||
// 严格白名单模式且未通过检查,直接拒绝处理请求
|
||||
if whitelist == nil && global.GVA_CONFIG.Cors.Mode == "strict-whitelist" && !(c.Request.Method == "GET" && c.Request.URL.Path == "/health") {
|
||||
c.AbortWithStatus(http.StatusForbidden)
|
||||
} else {
|
||||
// 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func checkCors(currentOrigin string) *config.CORSWhitelist {
|
||||
for _, whitelist := range global.GVA_CONFIG.Cors.Whitelist {
|
||||
// 遍历配置中的跨域头,寻找匹配项
|
||||
if currentOrigin == whitelist.AllowOrigin {
|
||||
return &whitelist
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,21 +1,58 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/plugin/email/utils"
|
||||
utils2 "git.echol.cn/loser/ai_proxy/server/utils"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ErrorToEmail 错误发送邮件中间件
|
||||
func ErrorToEmail() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
global.GVA_LOG.Error("panic error", zap.Any("error", err))
|
||||
response.FailWithMessage("服务器内部错误", c)
|
||||
var username string
|
||||
claims, _ := utils2.GetClaims(c)
|
||||
if claims.Username != "" {
|
||||
username = claims.Username
|
||||
} else {
|
||||
id, _ := strconv.Atoi(c.Request.Header.Get("x-user-id"))
|
||||
var u system.SysUser
|
||||
err := global.GVA_DB.Where("id = ?", id).First(&u).Error
|
||||
if err != nil {
|
||||
username = "Unknown"
|
||||
}
|
||||
}()
|
||||
username = u.Username
|
||||
}
|
||||
body, _ := io.ReadAll(c.Request.Body)
|
||||
// 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
record := system.SysOperationRecord{
|
||||
Ip: c.ClientIP(),
|
||||
Method: c.Request.Method,
|
||||
Path: c.Request.URL.Path,
|
||||
Agent: c.Request.UserAgent(),
|
||||
Body: string(body),
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
c.Next()
|
||||
|
||||
latency := time.Since(now)
|
||||
status := c.Writer.Status()
|
||||
record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
|
||||
str := "接收到的请求为" + record.Body + "\n" + "请求方式为" + record.Method + "\n" + "报错信息如下" + record.ErrorMessage + "\n" + "耗时" + latency.String() + "\n"
|
||||
if status != 200 {
|
||||
subject := username + "" + record.Ip + "调用了" + record.Path + "报错了"
|
||||
if err := utils.ErrorToEmail(subject, str); err != nil {
|
||||
global.GVA_LOG.Error("ErrorToEmail Failed, err:", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,80 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ErrorLogger 错误日志中间件
|
||||
func ErrorLogger() gin.HandlerFunc {
|
||||
// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
|
||||
func GinRecovery(stack bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// Check for a broken connection, as it is not really a
|
||||
// condition that warrants a panic stack trace.
|
||||
var brokenPipe bool
|
||||
if ne, ok := err.(*net.OpError); ok {
|
||||
if se, ok := ne.Err.(*os.SyscallError); ok {
|
||||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
||||
brokenPipe = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
if brokenPipe {
|
||||
global.GVA_LOG.Error(c.Request.URL.Path,
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
)
|
||||
// If the connection is dead, we can't write a status to it.
|
||||
_ = c.Error(err.(error)) // nolint: errcheck
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if stack {
|
||||
form := "后端"
|
||||
info := fmt.Sprintf("Panic: %v\nRequest: %s\nStack: %s", err, string(httpRequest), string(debug.Stack()))
|
||||
level := "error"
|
||||
_ = service.ServiceGroupApp.SystemServiceGroup.SysErrorService.CreateSysError(context.Background(), &system.SysError{
|
||||
Form: &form,
|
||||
Info: &info,
|
||||
Level: level,
|
||||
})
|
||||
global.GVA_LOG.Error("[Recovery from panic]",
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
)
|
||||
} else {
|
||||
form := "后端"
|
||||
info := fmt.Sprintf("Panic: %v\nRequest: %s", err, string(httpRequest))
|
||||
level := "error"
|
||||
_ = service.ServiceGroupApp.SystemServiceGroup.SysErrorService.CreateSysError(context.Background(), &system.SysError{
|
||||
Form: &form,
|
||||
Info: &info,
|
||||
Level: level,
|
||||
})
|
||||
global.GVA_LOG.Error("[Recovery from panic]",
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
)
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
// 记录错误
|
||||
for _, err := range c.Errors {
|
||||
global.GVA_LOG.Error("request error", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,89 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func JWTAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 从请求头获取 token
|
||||
// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
|
||||
token := utils.GetToken(c)
|
||||
if token == "" {
|
||||
response.FailWithDetailed(gin.H{"reload": true}, "未登录或非法访问", c)
|
||||
response.NoAuth("未登录或非法访问,请登录", c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查 JWT 黑名单
|
||||
if jwtService.IsBlacklist(token) {
|
||||
response.FailWithDetailed(gin.H{"reload": true}, "您的帐户异地登陆或令牌失效", c)
|
||||
if isBlacklist(token) {
|
||||
response.NoAuth("您的帐户异地登陆或令牌失效", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 解析 token
|
||||
j := utils.NewJWT()
|
||||
// parseToken 解析token包含的信息
|
||||
claims, err := j.ParseToken(token)
|
||||
if err != nil {
|
||||
if err == utils.TokenExpired {
|
||||
response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
|
||||
if errors.Is(err, utils.TokenExpired) {
|
||||
response.NoAuth("登录已过期,请重新登录", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
|
||||
response.NoAuth(err.Error(), c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Token 续期检查
|
||||
// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
|
||||
// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
|
||||
|
||||
//if user, err := userService.FindUserByUuid(claims.UUID.String()); err != nil || user.Enable == 2 {
|
||||
// _ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: token})
|
||||
// response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
|
||||
// c.Abort()
|
||||
//}
|
||||
c.Set("claims", claims)
|
||||
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
|
||||
dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
|
||||
claims.ExpiresAt = utils.NewNumericDate(time.Now().Add(dr))
|
||||
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
|
||||
newToken, _ := j.CreateTokenByOldToken(token, *claims)
|
||||
newClaims, _ := j.ParseToken(newToken)
|
||||
c.Header("new-token", newToken)
|
||||
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
|
||||
utils.SetToken(c, newToken, int(dr.Seconds()))
|
||||
utils.SetToken(c, newToken, int(dr.Seconds()/60))
|
||||
if global.GVA_CONFIG.System.UseMultipoint {
|
||||
RedisJwtToken, err := jwtService.GetRedisJWT(newClaims.Username)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("get redis jwt failed", zap.Error(err))
|
||||
} else {
|
||||
_ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: RedisJwtToken})
|
||||
}
|
||||
_ = jwtService.SetRedisJWT(newToken, newClaims.Username)
|
||||
// 记录新的活跃jwt
|
||||
_ = utils.SetRedisJWT(newToken, newClaims.Username)
|
||||
}
|
||||
}
|
||||
|
||||
c.Set("claims", claims)
|
||||
c.Next()
|
||||
|
||||
if newToken, exists := c.Get("new-token"); exists {
|
||||
c.Header("new-token", newToken.(string))
|
||||
}
|
||||
if newExpiresAt, exists := c.Get("new-expires-at"); exists {
|
||||
c.Header("new-expires-at", newExpiresAt.(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@author: [piexlmax](https://github.com/piexlmax)
|
||||
//@function: IsBlacklist
|
||||
//@description: 判断JWT是否在黑名单内部
|
||||
//@param: jwt string
|
||||
//@return: bool
|
||||
|
||||
func isBlacklist(jwt string) bool {
|
||||
_, ok := global.BlackCache.Get(jwt)
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -3,64 +3,90 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type LimitConfig struct {
|
||||
GenerationDuration time.Duration
|
||||
Limit int
|
||||
// GenerationKey 根据业务生成key 下面CheckOrMark查询生成
|
||||
GenerationKey func(c *gin.Context) string
|
||||
// 检查函数,用户可修改具体逻辑,更加灵活
|
||||
CheckOrMark func(key string, expire int, limit int) error
|
||||
// Expire key 过期时间
|
||||
Expire int
|
||||
// Limit 周期时间
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (l LimitConfig) LimitKey(c *gin.Context) string {
|
||||
func (l LimitConfig) LimitWithTime() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if err := l.CheckOrMark(l.GenerationKey(c), l.Expire, l.Limit); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err.Error()})
|
||||
c.Abort()
|
||||
return
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGenerationKey 默认生成key
|
||||
func DefaultGenerationKey(c *gin.Context) string {
|
||||
return "GVA_Limit" + c.ClientIP()
|
||||
}
|
||||
|
||||
func (l LimitConfig) GetLimit(c *gin.Context) int {
|
||||
return l.Limit
|
||||
func DefaultCheckOrMark(key string, expire int, limit int) (err error) {
|
||||
// 判断是否开启redis
|
||||
if global.GVA_REDIS == nil {
|
||||
return err
|
||||
}
|
||||
if err = SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil {
|
||||
global.GVA_LOG.Error("limit", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l LimitConfig) Reached(c *gin.Context) response.Response {
|
||||
return response.Response{Code: response.ERROR, Data: nil, Msg: "操作过于频繁,请稍后再试"}
|
||||
func DefaultLimit() gin.HandlerFunc {
|
||||
return LimitConfig{
|
||||
GenerationKey: DefaultGenerationKey,
|
||||
CheckOrMark: DefaultCheckOrMark,
|
||||
Expire: global.GVA_CONFIG.System.LimitTimeIP,
|
||||
Limit: global.GVA_CONFIG.System.LimitCountIP,
|
||||
}.LimitWithTime()
|
||||
}
|
||||
|
||||
// IPLimit IP 限流中间件
|
||||
func IPLimit() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if global.GVA_CONFIG.System.UseRedis {
|
||||
key := "GVA_Limit" + c.ClientIP()
|
||||
limit := global.GVA_CONFIG.System.IpLimitCount
|
||||
limitTime := global.GVA_CONFIG.System.IpLimitTime
|
||||
|
||||
ctx := context.Background()
|
||||
count, err := global.GVA_REDIS.Get(ctx, key).Int()
|
||||
if err != nil && !errors.Is(err, context.DeadlineExceeded) {
|
||||
global.GVA_LOG.Error("get redis key error", zap.Error(err))
|
||||
}
|
||||
|
||||
if count >= limit {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": response.ERROR,
|
||||
"msg": fmt.Sprintf("操作过于频繁,请在 %d 秒后再试", limitTime),
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
pipe := global.GVA_REDIS.Pipeline()
|
||||
pipe.Incr(ctx, key)
|
||||
pipe.Expire(ctx, key, time.Second*time.Duration(limitTime))
|
||||
_, err = pipe.Exec(ctx)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("redis pipeline error", zap.Error(err))
|
||||
// SetLimitWithTime 设置访问次数
|
||||
func SetLimitWithTime(key string, limit int, expiration time.Duration) error {
|
||||
count, err := global.GVA_REDIS.Exists(context.Background(), key).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
pipe := global.GVA_REDIS.TxPipeline()
|
||||
pipe.Incr(context.Background(), key)
|
||||
pipe.Expire(context.Background(), key, expiration)
|
||||
_, err = pipe.Exec(context.Background())
|
||||
return err
|
||||
} else {
|
||||
// 次数
|
||||
if times, err := global.GVA_REDIS.Get(context.Background(), key).Int(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if times >= limit {
|
||||
if t, err := global.GVA_REDIS.PTTL(context.Background(), key).Result(); err != nil {
|
||||
return errors.New("请求太过频繁,请稍后再试")
|
||||
} else {
|
||||
return errors.New("请求太过频繁, 请 " + t.String() + " 秒后尝试")
|
||||
}
|
||||
} else {
|
||||
return global.GVA_REDIS.Incr(context.Background(), key).Err()
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,67 +2,26 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"github.com/unrolled/secure"
|
||||
)
|
||||
|
||||
// LoadTls 加载 TLS 证书
|
||||
// 用https把这个中间件在router里面use一下就好
|
||||
|
||||
func LoadTls() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if global.GVA_CONFIG.System.UseHttps {
|
||||
certFile := global.GVA_CONFIG.System.TlsCert
|
||||
keyFile := global.GVA_CONFIG.System.TlsKey
|
||||
|
||||
if certFile == "" || keyFile == "" {
|
||||
global.GVA_LOG.Error("TLS cert or key file not configured")
|
||||
c.AbortWithStatus(500)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查证书文件是否存在
|
||||
if _, err := os.Stat(certFile); os.IsNotExist(err) {
|
||||
global.GVA_LOG.Error("TLS cert file not found", zap.String("file", certFile))
|
||||
c.AbortWithStatus(500)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(keyFile); os.IsNotExist(err) {
|
||||
global.GVA_LOG.Error("TLS key file not found", zap.String("file", keyFile))
|
||||
c.AbortWithStatus(500)
|
||||
return
|
||||
}
|
||||
middleware := secure.New(secure.Options{
|
||||
SSLRedirect: true,
|
||||
SSLHost: "localhost:443",
|
||||
})
|
||||
err := middleware.Process(c.Writer, c.Request)
|
||||
if err != nil {
|
||||
// 如果出现错误,请不要继续
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 继续往下处理
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// LoadTlsFromFile 从文件加载 TLS 证书内容
|
||||
func LoadTlsFromFile(certFile, keyFile string) (certPEM, keyPEM []byte, err error) {
|
||||
certF, err := os.Open(certFile)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("open cert file error: %w", err)
|
||||
}
|
||||
defer certF.Close()
|
||||
|
||||
keyF, err := os.Open(keyFile)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("open key file error: %w", err)
|
||||
}
|
||||
defer keyF.Close()
|
||||
|
||||
certPEM, err = io.ReadAll(certF)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read cert file error: %w", err)
|
||||
}
|
||||
|
||||
keyPEM, err = io.ReadAll(keyF)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read key file error: %w", err)
|
||||
}
|
||||
|
||||
return certPEM, keyPEM, nil
|
||||
}
|
||||
|
||||
@@ -3,111 +3,87 @@ package middleware
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/ai_proxy/server/global"
|
||||
"git.echol.cn/loser/ai_proxy/server/model/system"
|
||||
"git.echol.cn/loser/ai_proxy/server/service"
|
||||
"git.echol.cn/loser/ai_proxy/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
|
||||
// LogLayout 日志layout
|
||||
type LogLayout struct {
|
||||
Time time.Time
|
||||
Metadata map[string]interface{} // 存储自定义原数据
|
||||
Path string // 访问路径
|
||||
Query string // 携带query
|
||||
Body string // 携带body数据
|
||||
IP string // ip地址
|
||||
UserAgent string // 代理
|
||||
Error string // 错误
|
||||
Cost time.Duration // 花费时间
|
||||
Source string // 来源
|
||||
}
|
||||
|
||||
// OperationRecord 操作记录中间件
|
||||
func OperationRecord() gin.HandlerFunc {
|
||||
type Logger struct {
|
||||
// Filter 用户自定义过滤
|
||||
Filter func(c *gin.Context) bool
|
||||
// FilterKeyword 关键字过滤(key)
|
||||
FilterKeyword func(layout *LogLayout) bool
|
||||
// AuthProcess 鉴权处理
|
||||
AuthProcess func(c *gin.Context, layout *LogLayout)
|
||||
// 日志处理
|
||||
Print func(LogLayout)
|
||||
// Source 服务唯一标识
|
||||
Source string
|
||||
}
|
||||
|
||||
func (l Logger) SetLoggerMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
query := c.Request.URL.RawQuery
|
||||
var body []byte
|
||||
var userId int
|
||||
if c.Request.Method != http.MethodGet {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("read body from request error:", zap.Error(err))
|
||||
} else {
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
}
|
||||
if l.Filter != nil && !l.Filter(c) {
|
||||
body, _ = c.GetRawData()
|
||||
// 将原body塞回去
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
}
|
||||
|
||||
userId = int(utils.GetUserID(c))
|
||||
|
||||
writer := responseBodyWriter{
|
||||
ResponseWriter: c.Writer,
|
||||
body: &bytes.Buffer{},
|
||||
}
|
||||
c.Writer = writer
|
||||
now := time.Now()
|
||||
|
||||
c.Next()
|
||||
|
||||
latency := time.Since(now)
|
||||
|
||||
if c.Request.Method != http.MethodGet {
|
||||
record := system.SysOperationRecord{
|
||||
Ip: c.ClientIP(),
|
||||
Method: c.Request.Method,
|
||||
Path: c.Request.URL.Path,
|
||||
Agent: c.Request.UserAgent(),
|
||||
Body: string(body),
|
||||
UserID: userId,
|
||||
Status: c.Writer.Status(),
|
||||
Latency: latency,
|
||||
Resp: writer.body.String(),
|
||||
}
|
||||
|
||||
values, _ := url.ParseQuery(c.Request.URL.RawQuery)
|
||||
record.Query = values.Encode()
|
||||
|
||||
if err := operationRecordService.CreateSysOperationRecord(record); err != nil {
|
||||
global.GVA_LOG.Error("create operation record error:", zap.Error(err))
|
||||
}
|
||||
cost := time.Since(start)
|
||||
layout := LogLayout{
|
||||
Time: time.Now(),
|
||||
Path: path,
|
||||
Query: query,
|
||||
IP: c.ClientIP(),
|
||||
UserAgent: c.Request.UserAgent(),
|
||||
Error: strings.TrimRight(c.Errors.ByType(gin.ErrorTypePrivate).String(), "\n"),
|
||||
Cost: cost,
|
||||
Source: l.Source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type responseBodyWriter struct {
|
||||
gin.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
}
|
||||
|
||||
func (r responseBodyWriter) Write(b []byte) (int, error) {
|
||||
r.body.Write(b)
|
||||
return r.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
func (r responseBodyWriter) WriteString(s string) (int, error) {
|
||||
r.body.WriteString(s)
|
||||
return r.ResponseWriter.WriteString(s)
|
||||
}
|
||||
|
||||
func (r responseBodyWriter) WriteJSON(obj interface{}) error {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.body.Write(data)
|
||||
return r.ResponseWriter.WriteJSON(obj)
|
||||
}
|
||||
|
||||
// NeedRecordPath 判断是否需要记录操作日志
|
||||
func NeedRecordPath(path string) bool {
|
||||
// 排除不需要记录的路径
|
||||
excludePaths := []string{
|
||||
"/health",
|
||||
"/swagger",
|
||||
"/api/captcha",
|
||||
}
|
||||
|
||||
for _, excludePath := range excludePaths {
|
||||
if strings.HasPrefix(path, excludePath) {
|
||||
return false
|
||||
if l.Filter != nil && !l.Filter(c) {
|
||||
layout.Body = string(body)
|
||||
}
|
||||
if l.AuthProcess != nil {
|
||||
// 处理鉴权需要的信息
|
||||
l.AuthProcess(c, &layout)
|
||||
}
|
||||
if l.FilterKeyword != nil {
|
||||
// 自行判断key/value 脱敏等
|
||||
l.FilterKeyword(&layout)
|
||||
}
|
||||
// 自行处理日志
|
||||
l.Print(layout)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func DefaultLogger() gin.HandlerFunc {
|
||||
return Logger{
|
||||
Print: func(layout LogLayout) {
|
||||
// 标准输出,k8s做收集
|
||||
v, _ := json.Marshal(layout)
|
||||
fmt.Println(string(v))
|
||||
},
|
||||
Source: "GVA",
|
||||
}.SetLoggerMiddleware()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user