后端重构

This commit is contained in:
Yvan
2026-01-08 19:28:58 +08:00
parent 8800c069d9
commit 9cb6b965e0
49 changed files with 4907 additions and 14 deletions
+28
View File
@@ -0,0 +1,28 @@
package example
import "kra/internal/biz/example"
// ApiGroup Example API组
type ApiGroup struct {
FileUploadApi
CustomerApi
AttachmentCategoryApi
}
// 业务层依赖
var (
fileUploadUsecase *example.FileUploadUsecase
customerUsecase *example.CustomerUsecase
attachmentCategoryUsecase *example.AttachmentCategoryUsecase
)
// InitUsecases 初始化业务层依赖
func InitUsecases(
fileUpload *example.FileUploadUsecase,
customer *example.CustomerUsecase,
attachmentCategory *example.AttachmentCategoryUsecase,
) {
fileUploadUsecase = fileUpload
customerUsecase = customer
attachmentCategoryUsecase = attachmentCategory
}
@@ -0,0 +1,116 @@
package example
import (
"kra/internal/biz/example"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type AttachmentCategoryApi struct{}
// AddCategory 创建/更新分类
// @Summary 创建/更新分类
// @Tags ExaAttachmentCategory
// @Accept json
// @Produce json
// @Param data body CategoryRequest true "分类信息"
// @Success 200 {object} response.Response
// @Router /attachmentCategory/addCategory [post]
func (api *AttachmentCategoryApi) AddCategory(c *gin.Context) {
var req CategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("参数错误", c)
return
}
category := &example.AttachmentCategory{
ID: req.ID,
Name: req.Name,
Pid: req.Pid,
}
if err := attachmentCategoryUsecase.AddCategory(c.Request.Context(), category); err != nil {
response.FailWithMessage("创建/更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建/更新成功", c)
}
// DeleteCategory 删除分类
// @Summary 删除分类
// @Tags ExaAttachmentCategory
// @Accept json
// @Produce json
// @Param data body DeleteCategoryRequest true "分类ID"
// @Success 200 {object} response.Response
// @Router /attachmentCategory/deleteCategory [post]
func (api *AttachmentCategoryApi) DeleteCategory(c *gin.Context) {
var req DeleteCategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("参数错误", c)
return
}
if req.ID == 0 {
response.FailWithMessage("参数错误", c)
return
}
if err := attachmentCategoryUsecase.DeleteCategory(c.Request.Context(), req.ID); err != nil {
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// GetCategoryList 获取分类列表
// @Summary 获取分类列表
// @Tags ExaAttachmentCategory
// @Accept json
// @Produce json
// @Success 200 {object} response.Response{data=[]CategoryResponse}
// @Router /attachmentCategory/getCategoryList [get]
func (api *AttachmentCategoryApi) GetCategoryList(c *gin.Context) {
list, err := attachmentCategoryUsecase.GetCategoryList(c.Request.Context())
if err != nil {
response.FailWithMessage("获取分类列表失败", c)
return
}
respList := toCategoryResponseList(list)
response.OkWithData(respList, c)
}
// Request/Response 结构
type CategoryRequest struct {
ID uint `json:"ID"`
Name string `json:"name"`
Pid uint `json:"pid"`
}
type DeleteCategoryRequest struct {
ID uint `json:"id"`
}
type CategoryResponse struct {
ID uint `json:"ID"`
Name string `json:"name"`
Pid uint `json:"pid"`
Children []CategoryResponse `json:"children"`
}
func toCategoryResponseList(categories []*example.AttachmentCategory) []CategoryResponse {
result := make([]CategoryResponse, len(categories))
for i, cat := range categories {
result[i] = CategoryResponse{
ID: cat.ID,
Name: cat.Name,
Pid: cat.Pid,
Children: toCategoryResponseList(cat.Children),
}
}
return result
}
@@ -0,0 +1,192 @@
package example
import (
"kra/internal/biz/example"
"kra/pkg/response"
"kra/pkg/utils"
"github.com/gin-gonic/gin"
)
type CustomerApi struct{}
// CreateExaCustomer 创建客户
// @Summary 创建客户
// @Tags ExaCustomer
// @Accept json
// @Produce json
// @Param data body CustomerRequest true "客户信息"
// @Success 200 {object} response.Response
// @Router /customer/customer [post]
func (api *CustomerApi) CreateExaCustomer(c *gin.Context) {
var req CustomerRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
customer := &example.Customer{
CustomerName: req.CustomerName,
CustomerPhoneData: req.CustomerPhoneData,
SysUserID: utils.GetUserID(c),
SysUserAuthorityID: utils.GetUserAuthorityId(c),
}
if err := customerUsecase.CreateExaCustomer(c.Request.Context(), customer); err != nil {
response.FailWithMessage("创建失败: "+err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteExaCustomer 删除客户
// @Summary 删除客户
// @Tags ExaCustomer
// @Accept json
// @Produce json
// @Param data body DeleteCustomerRequest true "客户ID"
// @Success 200 {object} response.Response
// @Router /customer/customer [delete]
func (api *CustomerApi) DeleteExaCustomer(c *gin.Context) {
var req DeleteCustomerRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := customerUsecase.DeleteExaCustomer(c.Request.Context(), req.ID); err != nil {
response.FailWithMessage("删除失败: "+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateExaCustomer 更新客户
// @Summary 更新客户
// @Tags ExaCustomer
// @Accept json
// @Produce json
// @Param data body CustomerRequest true "客户信息"
// @Success 200 {object} response.Response
// @Router /customer/customer [put]
func (api *CustomerApi) UpdateExaCustomer(c *gin.Context) {
var req CustomerRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
customer := &example.Customer{
ID: req.ID,
CustomerName: req.CustomerName,
CustomerPhoneData: req.CustomerPhoneData,
}
if err := customerUsecase.UpdateExaCustomer(c.Request.Context(), customer); err != nil {
response.FailWithMessage("更新失败: "+err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
}
// GetExaCustomer 获取客户信息
// @Summary 获取客户信息
// @Tags ExaCustomer
// @Accept json
// @Produce json
// @Param data body GetCustomerRequest true "客户ID"
// @Success 200 {object} response.Response{data=CustomerResponse}
// @Router /customer/customer [get]
func (api *CustomerApi) GetExaCustomer(c *gin.Context) {
var req GetCustomerRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
customer, err := customerUsecase.GetExaCustomer(c.Request.Context(), req.ID)
if err != nil {
response.FailWithMessage("获取失败: "+err.Error(), c)
return
}
response.OkWithDetailed(toCustomerResponse(customer), "获取成功", c)
}
// GetExaCustomerList 分页获取客户列表
// @Summary 分页获取客户列表
// @Tags ExaCustomer
// @Accept json
// @Produce json
// @Param data body GetCustomerListRequest true "分页参数"
// @Success 200 {object} response.Response{data=response.PageResult}
// @Router /customer/customerList [get]
func (api *CustomerApi) GetExaCustomerList(c *gin.Context) {
var req GetCustomerListRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
authorityId := utils.GetUserAuthorityId(c)
list, total, err := customerUsecase.GetCustomerInfoList(c.Request.Context(), authorityId, req.Page, req.PageSize)
if err != nil {
response.FailWithMessage("获取失败: "+err.Error(), c)
return
}
respList := make([]CustomerResponse, len(list))
for i, cust := range list {
respList[i] = *toCustomerResponse(cust)
}
response.OkWithDetailed(response.PageResult{
List: respList,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// Request/Response 结构
type CustomerRequest struct {
ID uint `json:"ID"`
CustomerName string `json:"customerName"`
CustomerPhoneData string `json:"customerPhoneData"`
}
type DeleteCustomerRequest struct {
ID uint `json:"ID"`
}
type GetCustomerRequest struct {
ID uint `form:"ID"`
}
type GetCustomerListRequest struct {
Page int `form:"page"`
PageSize int `form:"pageSize"`
}
type CustomerResponse struct {
ID uint `json:"ID"`
CustomerName string `json:"customerName"`
CustomerPhoneData string `json:"customerPhoneData"`
SysUserID uint `json:"sysUserId"`
SysUserAuthorityID uint `json:"sysUserAuthorityID"`
SysUserName string `json:"sysUserName"`
}
func toCustomerResponse(c *example.Customer) *CustomerResponse {
return &CustomerResponse{
ID: c.ID,
CustomerName: c.CustomerName,
CustomerPhoneData: c.CustomerPhoneData,
SysUserID: c.SysUserID,
SysUserAuthorityID: c.SysUserAuthorityID,
SysUserName: c.SysUserName,
}
}
@@ -0,0 +1,198 @@
package example
import (
"strconv"
"kra/internal/biz/example"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type FileUploadApi struct{}
// UploadFile 上传文件
// @Summary 上传文件
// @Tags ExaFileUploadAndDownload
// @Accept multipart/form-data
// @Produce json
// @Param file formance file true "上传文件"
// @Success 200 {object} response.Response{data=FileUploadResponse}
// @Router /fileUploadAndDownload/upload [post]
func (api *FileUploadApi) UploadFile(c *gin.Context) {
noSave := c.DefaultQuery("noSave", "0")
classIdStr := c.DefaultQuery("classId", "0")
classId, _ := strconv.Atoi(classIdStr)
_, header, err := c.Request.FormFile("file")
if err != nil {
response.FailWithMessage("接收文件失败", c)
return
}
file, err := fileUploadUsecase.UploadFile(c.Request.Context(), header, noSave, classId)
if err != nil {
response.FailWithMessage("上传失败: "+err.Error(), c)
return
}
response.OkWithDetailed(toFileUploadResponse(file), "上传成功", c)
}
// DeleteFile 删除文件
// @Summary 删除文件
// @Tags ExaFileUploadAndDownload
// @Accept json
// @Produce json
// @Param data body DeleteFileRequest true "文件ID"
// @Success 200 {object} response.Response
// @Router /fileUploadAndDownload/deleteFile [post]
func (api *FileUploadApi) DeleteFile(c *gin.Context) {
var req DeleteFileRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := fileUploadUsecase.DeleteFile(c.Request.Context(), req.ID); err != nil {
response.FailWithMessage("删除失败: "+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// EditFileName 编辑文件名
// @Summary 编辑文件名
// @Tags ExaFileUploadAndDownload
// @Accept json
// @Produce json
// @Param data body EditFileNameRequest true "文件信息"
// @Success 200 {object} response.Response
// @Router /fileUploadAndDownload/editFileName [post]
func (api *FileUploadApi) EditFileName(c *gin.Context) {
var req EditFileNameRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := fileUploadUsecase.EditFileName(c.Request.Context(), req.ID, req.Name); err != nil {
response.FailWithMessage("编辑失败: "+err.Error(), c)
return
}
response.OkWithMessage("编辑成功", c)
}
// GetFileList 分页获取文件列表
// @Summary 分页获取文件列表
// @Tags ExaFileUploadAndDownload
// @Accept json
// @Produce json
// @Param data body GetFileListRequest true "分页参数"
// @Success 200 {object} response.Response{data=response.PageResult}
// @Router /fileUploadAndDownload/getFileList [post]
func (api *FileUploadApi) GetFileList(c *gin.Context) {
var req GetFileListRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
searchReq := &example.FileUploadSearchReq{
Page: req.Page,
PageSize: req.PageSize,
Keyword: req.Keyword,
ClassId: req.ClassId,
}
list, total, err := fileUploadUsecase.GetFileRecordInfoList(c.Request.Context(), searchReq)
if err != nil {
response.FailWithMessage("获取失败: "+err.Error(), c)
return
}
respList := make([]FileUploadResponse, len(list))
for i, f := range list {
respList[i] = *toFileUploadResponse(f)
}
response.OkWithDetailed(response.PageResult{
List: respList,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// ImportURL 导入URL
// @Summary 导入URL
// @Tags ExaFileUploadAndDownload
// @Accept json
// @Produce json
// @Param data body []FileUploadResponse true "文件列表"
// @Success 200 {object} response.Response
// @Router /fileUploadAndDownload/importURL [post]
func (api *FileUploadApi) ImportURL(c *gin.Context) {
var req []FileUploadResponse
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
files := make([]*example.FileUpload, len(req))
for i, r := range req {
files[i] = &example.FileUpload{
Name: r.Name,
ClassId: r.ClassId,
Url: r.Url,
Tag: r.Tag,
Key: r.Key,
}
}
if err := fileUploadUsecase.ImportURL(c.Request.Context(), files); err != nil {
response.FailWithMessage("导入失败: "+err.Error(), c)
return
}
response.OkWithMessage("导入成功", c)
}
// Request/Response 结构
type DeleteFileRequest struct {
ID uint `json:"id"`
}
type EditFileNameRequest struct {
ID uint `json:"id"`
Name string `json:"name"`
}
type GetFileListRequest struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`
Keyword string `json:"keyword"`
ClassId int `json:"classId"`
}
type FileUploadResponse struct {
ID uint `json:"ID"`
Name string `json:"name"`
ClassId int `json:"classId"`
Url string `json:"url"`
Tag string `json:"tag"`
Key string `json:"key"`
}
func toFileUploadResponse(f *example.FileUpload) *FileUploadResponse {
return &FileUploadResponse{
ID: f.ID,
Name: f.Name,
ClassId: f.ClassId,
Url: f.Url,
Tag: f.Tag,
Key: f.Key,
}
}
+26
View File
@@ -10,10 +10,18 @@ type ApiGroup struct {
AuthorityApi
MenuApi
DictionaryApi
DictionaryDetailApi
CasbinApi
JwtApi
OperationRecordApi
ParamsApi
ErrorApi
VersionApi
SystemApi
ExportTemplateApi
DBApi
AutoCodeApi
AutoCodeHistoryApi
}
// 业务层依赖
@@ -28,6 +36,12 @@ var (
jwtBlacklistUsecase *system.JwtBlacklistUsecase
operationRecordUsecase *system.OperationRecordUsecase
paramsUsecase *system.ParamsUsecase
errorUsecase *system.ErrorUsecase
versionUsecase *system.VersionUsecase
systemUsecase *system.SystemUsecase
exportTemplateUsecase *system.ExportTemplateUsecase
autoCodeUsecase *system.AutoCodeUsecase
autoCodeHistoryUsecase *system.AutoCodeHistoryUsecase
)
// InitUsecases 初始化业务层依赖
@@ -42,6 +56,12 @@ func InitUsecases(
jwtBlacklist *system.JwtBlacklistUsecase,
operationRecord *system.OperationRecordUsecase,
params *system.ParamsUsecase,
sysError *system.ErrorUsecase,
version *system.VersionUsecase,
sysSystem *system.SystemUsecase,
exportTemplate *system.ExportTemplateUsecase,
autoCode *system.AutoCodeUsecase,
autoCodeHistory *system.AutoCodeHistoryUsecase,
) {
userUsecase = user
apiUsecase = api
@@ -53,4 +73,10 @@ func InitUsecases(
jwtBlacklistUsecase = jwtBlacklist
operationRecordUsecase = operationRecord
paramsUsecase = params
errorUsecase = sysError
versionUsecase = version
systemUsecase = sysSystem
exportTemplateUsecase = exportTemplate
autoCodeUsecase = autoCode
autoCodeHistoryUsecase = autoCodeHistory
}
@@ -0,0 +1,77 @@
package system
import (
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type AutoCodeApi struct{}
// GetDB 获取当前所有数据库
// @Summary 获取当前所有数据库
// @Tags AutoCode
// @Accept json
// @Produce json
// @Success 200 {object} response.Response{data=map[string]interface{}}
// @Router /autoCode/getDB [get]
func (api *AutoCodeApi) GetDB(c *gin.Context) {
dbs, err := autoCodeUsecase.GetDB(c.Request.Context())
if err != nil {
response.FailWithMessage("获取失败: "+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"dbs": dbs}, "获取成功", c)
}
// GetTables 获取当前数据库所有表
// @Summary 获取当前数据库所有表
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param dbName query string false "数据库名"
// @Success 200 {object} response.Response{data=map[string]interface{}}
// @Router /autoCode/getTables [get]
func (api *AutoCodeApi) GetTables(c *gin.Context) {
dbName := c.Query("dbName")
if dbName == "" {
response.FailWithMessage("数据库名不能为空", c)
return
}
tables, err := autoCodeUsecase.GetTables(c.Request.Context(), dbName)
if err != nil {
response.FailWithMessage("查询table失败: "+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c)
}
// GetColumn 获取当前表所有字段
// @Summary 获取当前表所有字段
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param dbName query string false "数据库名"
// @Param tableName query string true "表名"
// @Success 200 {object} response.Response{data=map[string]interface{}}
// @Router /autoCode/getColumn [get]
func (api *AutoCodeApi) GetColumn(c *gin.Context) {
dbName := c.Query("dbName")
tableName := c.Query("tableName")
if tableName == "" {
response.FailWithMessage("表名不能为空", c)
return
}
if dbName == "" {
response.FailWithMessage("数据库名不能为空", c)
return
}
columns, err := autoCodeUsecase.GetColumn(c.Request.Context(), tableName, dbName)
if err != nil {
response.FailWithMessage("获取失败: "+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
}
@@ -0,0 +1,118 @@
package system
import (
"kra/internal/biz/system"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type AutoCodeHistoryApi struct{}
// GetByIdReq 获取请求
type GetByIdReq struct {
ID uint `json:"id" form:"id"`
}
// First 获取meta信息
// @Summary 获取meta信息
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param data body GetByIdReq true "请求参数"
// @Success 200 {object} response.Response{data=map[string]interface{}}
// @Router /autoCode/getMeta [post]
func (api *AutoCodeHistoryApi) First(c *gin.Context) {
var req GetByIdReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
data, err := autoCodeHistoryUsecase.First(c.Request.Context(), req.ID)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
}
// Delete 删除回滚记录
// @Summary 删除回滚记录
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param data body GetByIdReq true "请求参数"
// @Success 200 {object} response.Response
// @Router /autoCode/delSysHistory [post]
func (api *AutoCodeHistoryApi) Delete(c *gin.Context) {
var req GetByIdReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err := autoCodeHistoryUsecase.Delete(c.Request.Context(), req.ID)
if err != nil {
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// RollBack 回滚自动生成代码
// @Summary 回滚自动生成代码
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param data body system.SysAutoHistoryRollBack true "请求参数"
// @Success 200 {object} response.Response
// @Router /autoCode/rollback [post]
func (api *AutoCodeHistoryApi) RollBack(c *gin.Context) {
var req system.SysAutoHistoryRollBack
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
err := autoCodeHistoryUsecase.RollBack(c.Request.Context(), req)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("回滚成功", c)
}
// PageInfoReq 分页请求
type PageInfoReq struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
}
// GetList 查询回滚记录
// @Summary 查询回滚记录
// @Tags AutoCode
// @Accept json
// @Produce json
// @Param data body PageInfoReq true "请求参数"
// @Success 200 {object} response.Response{data=response.PageResult}
// @Router /autoCode/getSysHistory [post]
func (api *AutoCodeHistoryApi) GetList(c *gin.Context) {
var req PageInfoReq
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, total, err := autoCodeHistoryUsecase.GetList(c.Request.Context(), req.Page, req.PageSize)
if err != nil {
response.FailWithMessage("获取失败", c)
return
}
response.OkWithDetailed(response.PageResult{
List: list,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
@@ -0,0 +1,289 @@
package system
import (
"strconv"
"kra/internal/biz/system"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type DictionaryDetailApi struct{}
// DictionaryDetailRequest 字典详情请求
type DictionaryDetailRequest 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"`
SysDictionaryID uint `json:"sysDictionaryID"`
ParentID *uint `json:"parentId"`
}
// DictionaryDetailSearchRequest 字典详情搜索请求
type DictionaryDetailSearchRequest struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
Label string `json:"label" form:"label"`
Value string `json:"value" form:"value"`
Status *bool `json:"status" form:"status"`
SysDictionaryID uint `json:"sysDictionaryID" form:"sysDictionaryID"`
ParentID *uint `json:"parentId" form:"parentId"`
Level *int `json:"level" form:"level"`
}
// GetDictionaryDetailsByParentRequest 根据父级获取字典详情请求
type GetDictionaryDetailsByParentRequest struct {
SysDictionaryID uint `json:"sysDictionaryID" form:"sysDictionaryID" binding:"required"`
ParentID *uint `json:"parentId" form:"parentId"`
IncludeChildren bool `json:"includeChildren" form:"includeChildren"`
}
// CreateSysDictionaryDetail 创建字典详情
func (d *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
var req DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
detail := &system.DictionaryDetail{
Label: req.Label,
Value: req.Value,
Extend: req.Extend,
Status: req.Status,
Sort: req.Sort,
SysDictionaryID: req.SysDictionaryID,
ParentID: req.ParentID,
}
if err := dictionaryUsecase.CreateDictionaryDetail(c.Request.Context(), detail); err != nil {
response.FailWithMessage("创建失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysDictionaryDetail 删除字典详情
func (d *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
var req DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := dictionaryUsecase.DeleteDictionaryDetail(c.Request.Context(), req.ID); err != nil {
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// UpdateSysDictionaryDetail 更新字典详情
func (d *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
var req DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
detail := &system.DictionaryDetail{
ID: req.ID,
Label: req.Label,
Value: req.Value,
Extend: req.Extend,
Status: req.Status,
Sort: req.Sort,
SysDictionaryID: req.SysDictionaryID,
ParentID: req.ParentID,
}
if err := dictionaryUsecase.UpdateDictionaryDetail(c.Request.Context(), detail); err != nil {
response.FailWithMessage("更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysDictionaryDetail 根据ID获取字典详情
func (d *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
idStr := c.Query("ID")
if idStr == "" {
response.FailWithMessage("缺少参数: ID", c)
return
}
id, _ := strconv.ParseUint(idStr, 10, 32)
detail, err := dictionaryUsecase.GetDictionaryDetail(c.Request.Context(), uint(id))
if err != nil {
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"reSysDictionaryDetail": toDictionaryDetailResponse(detail)}, "查询成功", c)
}
// GetSysDictionaryDetailList 分页获取字典详情列表
func (d *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
var req DictionaryDetailSearchRequest
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
}
filters := make(map[string]interface{})
if req.Label != "" {
filters["label"] = req.Label
}
if req.Value != "" {
filters["value"] = req.Value
}
if req.Status != nil {
filters["status"] = req.Status
}
if req.SysDictionaryID != 0 {
filters["sysDictionaryID"] = req.SysDictionaryID
}
if req.ParentID != nil {
filters["parentId"] = req.ParentID
}
if req.Level != nil {
filters["level"] = req.Level
}
list, total, err := dictionaryUsecase.GetDictionaryDetailList(c.Request.Context(), req.Page, req.PageSize, filters)
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
result := make([]map[string]interface{}, len(list))
for i, item := range list {
result[i] = toDictionaryDetailResponse(item)
}
response.OkWithDetailed(response.PageResult{
List: result,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// GetDictionaryTreeList 获取字典详情树形结构
func (d *DictionaryDetailApi) GetDictionaryTreeList(c *gin.Context) {
sysDictionaryID := c.Query("sysDictionaryID")
if sysDictionaryID == "" {
response.FailWithMessage("字典ID不能为空", c)
return
}
id, err := strconv.ParseUint(sysDictionaryID, 10, 32)
if err != nil {
response.FailWithMessage("字典ID格式错误", c)
return
}
list, err := dictionaryUsecase.GetDictionaryTreeList(c.Request.Context(), uint(id))
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"list": toDictionaryDetailTreeResponse(list)}, "获取成功", c)
}
// GetDictionaryTreeListByType 根据字典类型获取字典详情树形结构
func (d *DictionaryDetailApi) GetDictionaryTreeListByType(c *gin.Context) {
dictType := c.Query("type")
if dictType == "" {
response.FailWithMessage("字典类型不能为空", c)
return
}
list, err := dictionaryUsecase.GetDictionaryTreeListByType(c.Request.Context(), dictType)
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"list": toDictionaryDetailTreeResponse(list)}, "获取成功", c)
}
// GetDictionaryDetailsByParent 根据父级ID获取字典详情
func (d *DictionaryDetailApi) GetDictionaryDetailsByParent(c *gin.Context) {
var req GetDictionaryDetailsByParentRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := dictionaryUsecase.GetDictionaryDetailsByParent(c.Request.Context(), req.SysDictionaryID, req.ParentID, req.IncludeChildren)
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"list": toDictionaryDetailTreeResponse(list)}, "获取成功", c)
}
// GetDictionaryPath 获取字典详情的完整路径
func (d *DictionaryDetailApi) GetDictionaryPath(c *gin.Context) {
idStr := c.Query("id")
if idStr == "" {
response.FailWithMessage("字典详情ID不能为空", c)
return
}
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
response.FailWithMessage("字典详情ID格式错误", c)
return
}
path, err := dictionaryUsecase.GetDictionaryPath(c.Request.Context(), uint(id))
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
result := make([]map[string]interface{}, len(path))
for i, item := range path {
result[i] = toDictionaryDetailResponse(item)
}
response.OkWithDetailed(gin.H{"path": result}, "获取成功", c)
}
func toDictionaryDetailResponse(d *system.DictionaryDetail) map[string]interface{} {
return map[string]interface{}{
"id": d.ID,
"label": d.Label,
"value": d.Value,
"extend": d.Extend,
"status": d.Status,
"sort": d.Sort,
"sysDictionaryID": d.SysDictionaryID,
"parentId": d.ParentID,
"level": d.Level,
"path": d.Path,
"disabled": d.Disabled,
}
}
func toDictionaryDetailTreeResponse(list []*system.DictionaryDetail) []map[string]interface{} {
result := make([]map[string]interface{}, len(list))
for i, item := range list {
resp := toDictionaryDetailResponse(item)
if len(item.Children) > 0 {
resp["children"] = toDictionaryDetailTreeResponse(item.Children)
}
result[i] = resp
}
return result
}
+194
View File
@@ -0,0 +1,194 @@
package system
import (
"time"
"kra/internal/biz/system"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type ErrorApi struct{}
// SysErrorRequest 错误日志请求
type SysErrorRequest struct {
ID int64 `json:"id"`
Form string `json:"form" binding:"required"`
Info string `json:"info"`
Level string `json:"level"`
Solution string `json:"solution"`
Status string `json:"status"`
}
// SysErrorSearchRequest 错误日志搜索请求
type SysErrorSearchRequest struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
Form *string `json:"form" form:"form"`
Info *string `json:"info" form:"info"`
CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"`
}
// CreateSysError 创建错误日志
func (e *ErrorApi) CreateSysError(c *gin.Context) {
var req SysErrorRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
sysError := &system.SysError{
Form: req.Form,
Info: req.Info,
Level: req.Level,
Status: "未处理",
}
if err := errorUsecase.CreateSysError(c.Request.Context(), sysError); err != nil {
response.FailWithMessage("创建失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysError 删除错误日志
func (e *ErrorApi) DeleteSysError(c *gin.Context) {
id := c.Query("ID")
if id == "" {
response.FailWithMessage("缺少参数: ID", c)
return
}
if err := errorUsecase.DeleteSysError(c.Request.Context(), id); err != nil {
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysErrorByIds 批量删除错误日志
func (e *ErrorApi) DeleteSysErrorByIds(c *gin.Context) {
ids := c.QueryArray("IDs[]")
if len(ids) == 0 {
response.FailWithMessage("缺少参数: IDs", c)
return
}
if err := errorUsecase.DeleteSysErrorByIds(c.Request.Context(), ids); err != nil {
response.FailWithMessage("批量删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// UpdateSysError 更新错误日志
func (e *ErrorApi) UpdateSysError(c *gin.Context) {
var req SysErrorRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
sysError := &system.SysError{
ID: req.ID,
Form: req.Form,
Info: req.Info,
Level: req.Level,
Solution: req.Solution,
Status: req.Status,
}
if err := errorUsecase.UpdateSysError(c.Request.Context(), sysError); err != nil {
response.FailWithMessage("更新失败:"+err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysError 根据ID获取错误日志
func (e *ErrorApi) FindSysError(c *gin.Context) {
id := c.Query("ID")
if id == "" {
response.FailWithMessage("缺少参数: ID", c)
return
}
sysError, err := errorUsecase.GetSysError(c.Request.Context(), id)
if err != nil {
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(toErrorResponse(sysError), c)
}
// GetSysErrorList 分页获取错误日志列表
func (e *ErrorApi) GetSysErrorList(c *gin.Context) {
var req SysErrorSearchRequest
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
}
searchReq := &system.ErrorSearchReq{
Page: req.Page,
PageSize: req.PageSize,
Form: req.Form,
Info: req.Info,
CreatedAtRange: req.CreatedAtRange,
}
list, total, err := errorUsecase.GetSysErrorInfoList(c.Request.Context(), searchReq)
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
result := make([]map[string]interface{}, len(list))
for i, item := range list {
result[i] = toErrorResponse(item)
}
response.OkWithDetailed(response.PageResult{
List: result,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// GetSysErrorSolution 触发AI处理错误日志
func (e *ErrorApi) GetSysErrorSolution(c *gin.Context) {
id := c.Query("id")
if id == "" {
response.FailWithMessage("缺少参数: id", c)
return
}
if err := errorUsecase.GetSysErrorSolution(c.Request.Context(), id); err != nil {
response.FailWithMessage("处理触发失败:"+err.Error(), c)
return
}
response.OkWithMessage("已提交至AI处理", c)
}
// toErrorResponse 转换错误日志响应
func toErrorResponse(e *system.SysError) map[string]interface{} {
return map[string]interface{}{
"id": e.ID,
"form": e.Form,
"info": e.Info,
"level": e.Level,
"solution": e.Solution,
"status": e.Status,
"createdAt": e.CreatedAt,
"updatedAt": e.UpdatedAt,
}
}
@@ -0,0 +1,539 @@
package system
import (
"fmt"
"net/http"
"net/url"
"sync"
"time"
"kra/internal/biz/system"
"kra/pkg/response"
"kra/pkg/utils"
"github.com/gin-gonic/gin"
)
// 用于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 ExportTemplateApi struct{}
// CreateSysExportTemplateRequest 创建导出模板请求
type CreateSysExportTemplateRequest struct {
DBName string `json:"dbName"`
Name string `json:"name" binding:"required"`
TableName string `json:"tableName"`
TemplateID string `json:"templateID"`
TemplateInfo string `json:"templateInfo"`
Limit *int `json:"limit"`
Order string `json:"order"`
Conditions []*ConditionRequest `json:"conditions"`
JoinTemplate []*JoinTemplateRequest `json:"joinTemplate"`
}
type ConditionRequest struct {
TemplateID string `json:"templateID"`
From string `json:"from"`
Column string `json:"column"`
Operator string `json:"operator"`
}
type JoinTemplateRequest struct {
TemplateID string `json:"templateID"`
Joins string `json:"joins"`
Table string `json:"table"`
On string `json:"on"`
}
// UpdateSysExportTemplateRequest 更新导出模板请求
type UpdateSysExportTemplateRequest struct {
ID uint `json:"ID" binding:"required"`
DBName string `json:"dbName"`
Name string `json:"name" binding:"required"`
TableName string `json:"tableName"`
TemplateID string `json:"templateID"`
TemplateInfo string `json:"templateInfo"`
Limit *int `json:"limit"`
Order string `json:"order"`
Conditions []*ConditionRequest `json:"conditions"`
JoinTemplate []*JoinTemplateRequest `json:"joinTemplate"`
}
// DeleteSysExportTemplateRequest 删除导出模板请求
type DeleteSysExportTemplateRequest struct {
ID uint `json:"ID" binding:"required"`
}
// DeleteSysExportTemplateByIdsRequest 批量删除导出模板请求
type DeleteSysExportTemplateByIdsRequest struct {
Ids []uint `json:"ids" binding:"required"`
}
// GetSysExportTemplateListRequest 获取导出模板列表请求
type GetSysExportTemplateListRequest struct {
Page int `form:"page"`
PageSize int `form:"pageSize"`
Name string `form:"name"`
TableName string `form:"tableName"`
TemplateID string `form:"templateID"`
StartCreatedAt *time.Time `form:"startCreatedAt"`
EndCreatedAt *time.Time `form:"endCreatedAt"`
}
// CreateSysExportTemplate 创建导出模板
func (e *ExportTemplateApi) CreateSysExportTemplate(c *gin.Context) {
var req CreateSysExportTemplateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
template := &system.ExportTemplate{
DBName: req.DBName,
Name: req.Name,
TableName: req.TableName,
TemplateID: req.TemplateID,
TemplateInfo: req.TemplateInfo,
Limit: req.Limit,
Order: req.Order,
}
// 转换条件
if len(req.Conditions) > 0 {
template.Conditions = make([]*system.Condition, len(req.Conditions))
for i, c := range req.Conditions {
template.Conditions[i] = &system.Condition{
TemplateID: c.TemplateID,
From: c.From,
Column: c.Column,
Operator: c.Operator,
}
}
}
// 转换关联
if len(req.JoinTemplate) > 0 {
template.JoinTemplate = make([]*system.JoinTemplate, len(req.JoinTemplate))
for i, j := range req.JoinTemplate {
template.JoinTemplate[i] = &system.JoinTemplate{
TemplateID: j.TemplateID,
Joins: j.Joins,
Table: j.Table,
On: j.On,
}
}
}
if err := exportTemplateUsecase.CreateExportTemplate(c, template); err != nil {
response.FailWithMessage("创建失败", c)
return
}
response.OkWithMessage("创建成功", c)
}
// DeleteSysExportTemplate 删除导出模板
func (e *ExportTemplateApi) DeleteSysExportTemplate(c *gin.Context) {
var req DeleteSysExportTemplateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := exportTemplateUsecase.DeleteExportTemplate(c, req.ID); err != nil {
response.FailWithMessage("删除失败", c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysExportTemplateByIds 批量删除导出模板
func (e *ExportTemplateApi) DeleteSysExportTemplateByIds(c *gin.Context) {
var req DeleteSysExportTemplateByIdsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if err := exportTemplateUsecase.DeleteExportTemplateByIds(c, req.Ids); err != nil {
response.FailWithMessage("批量删除失败", c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// UpdateSysExportTemplate 更新导出模板
func (e *ExportTemplateApi) UpdateSysExportTemplate(c *gin.Context) {
var req UpdateSysExportTemplateRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
template := &system.ExportTemplate{
ID: req.ID,
DBName: req.DBName,
Name: req.Name,
TableName: req.TableName,
TemplateID: req.TemplateID,
TemplateInfo: req.TemplateInfo,
Limit: req.Limit,
Order: req.Order,
}
// 转换条件
if len(req.Conditions) > 0 {
template.Conditions = make([]*system.Condition, len(req.Conditions))
for i, c := range req.Conditions {
template.Conditions[i] = &system.Condition{
TemplateID: c.TemplateID,
From: c.From,
Column: c.Column,
Operator: c.Operator,
}
}
}
// 转换关联
if len(req.JoinTemplate) > 0 {
template.JoinTemplate = make([]*system.JoinTemplate, len(req.JoinTemplate))
for i, j := range req.JoinTemplate {
template.JoinTemplate[i] = &system.JoinTemplate{
TemplateID: j.TemplateID,
Joins: j.Joins,
Table: j.Table,
On: j.On,
}
}
}
if err := exportTemplateUsecase.UpdateExportTemplate(c, template); err != nil {
response.FailWithMessage("更新失败", c)
return
}
response.OkWithMessage("更新成功", c)
}
// FindSysExportTemplate 根据ID获取导出模板
func (e *ExportTemplateApi) FindSysExportTemplate(c *gin.Context) {
var req struct {
ID uint `form:"ID" binding:"required"`
}
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
template, err := exportTemplateUsecase.GetExportTemplate(c, req.ID)
if err != nil {
response.FailWithMessage("查询失败", c)
return
}
response.OkWithData(gin.H{"resysExportTemplate": toExportTemplateResponse(template)}, c)
}
// GetSysExportTemplateList 分页获取导出模板列表
func (e *ExportTemplateApi) GetSysExportTemplateList(c *gin.Context) {
var req GetSysExportTemplateListRequest
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
searchReq := &system.ExportTemplateSearchReq{
Page: req.Page,
PageSize: req.PageSize,
Name: req.Name,
TableName: req.TableName,
TemplateID: req.TemplateID,
StartCreatedAt: req.StartCreatedAt,
EndCreatedAt: req.EndCreatedAt,
}
list, total, err := exportTemplateUsecase.GetExportTemplateList(c, searchReq)
if err != nil {
response.FailWithMessage("获取失败", c)
return
}
respList := make([]interface{}, len(list))
for i, t := range list {
respList[i] = toExportTemplateResponse(t)
}
response.OkWithDetailed(response.PageResult{
List: respList,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// ExportExcel 导出Excel(获取token)
func (e *ExportTemplateApi) 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)
// 记录本次请求参数
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 通过token导出Excel
func (e *ExportTemplateApi) 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) {
response.FailWithMessage("导出token无效或已过期", c)
return
}
// 从token获取参数
exportParams, ok := exportParamsRaw.(map[string]interface{})
if !ok {
response.FailWithMessage("解析导出参数失败", c)
return
}
// 获取导出参数
templateID := exportParams["templateID"].(string)
queryParams := exportParams["queryParams"].(url.Values)
// 清理一次性token
tokenMutex.Lock()
delete(exportTokenCache, token)
delete(exportTokenExpiration, token)
tokenMutex.Unlock()
// 导出
file, name, err := exportTemplateUsecase.ExportExcel(c, templateID, queryParams)
if err != nil {
response.FailWithMessage("获取失败", c)
return
}
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+utils.RandomString(6)+".csv"))
c.Header("success", "true")
c.Data(http.StatusOK, "text/csv", file.Bytes())
}
// PreviewSQL 预览SQL
func (e *ExportTemplateApi) PreviewSQL(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
queryParams := c.Request.URL.Query()
sqlPreview, err := exportTemplateUsecase.PreviewSQL(c, templateID, queryParams)
if err != nil {
response.FailWithMessage("获取失败", c)
return
}
response.OkWithData(gin.H{"sql": sqlPreview}, c)
}
// ExportTemplate 导出Excel模板(获取token)
func (e *ExportTemplateApi) ExportTemplate(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
// 创造一次性token
token := utils.RandomString(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导出模板
func (e *ExportTemplateApi) 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) {
response.FailWithMessage("导出token无效或已过期", c)
return
}
// 从token获取参数
exportParams, ok := exportParamsRaw.(map[string]interface{})
if !ok {
response.FailWithMessage("解析导出参数失败", c)
return
}
// 检查是否为模板导出
isTemplate, _ := exportParams["isTemplate"].(bool)
if !isTemplate {
response.FailWithMessage("token类型错误", c)
return
}
// 获取导出参数
templateID := exportParams["templateID"].(string)
// 清理一次性token
tokenMutex.Lock()
delete(exportTokenCache, token)
delete(exportTokenExpiration, token)
tokenMutex.Unlock()
// 导出模板
file, name, err := exportTemplateUsecase.ExportTemplate(c, templateID)
if err != nil {
response.FailWithMessage("获取失败", c)
return
}
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name+"模板.csv"))
c.Header("success", "true")
c.Data(http.StatusOK, "text/csv", file.Bytes())
}
// ImportExcel 导入Excel
func (e *ExportTemplateApi) ImportExcel(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
response.FailWithMessage("模板ID不能为空", c)
return
}
file, err := c.FormFile("file")
if err != nil {
response.FailWithMessage("文件获取失败", c)
return
}
if err := exportTemplateUsecase.ImportExcel(c, templateID, file); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithMessage("导入成功", c)
}
// 转换为响应结构
func toExportTemplateResponse(t *system.ExportTemplate) map[string]interface{} {
resp := map[string]interface{}{
"ID": t.ID,
"dbName": t.DBName,
"name": t.Name,
"tableName": t.TableName,
"templateID": t.TemplateID,
"templateInfo": t.TemplateInfo,
"limit": t.Limit,
"order": t.Order,
"createdAt": t.CreatedAt,
"updatedAt": t.UpdatedAt,
}
if len(t.Conditions) > 0 {
conditions := make([]map[string]interface{}, len(t.Conditions))
for i, c := range t.Conditions {
conditions[i] = map[string]interface{}{
"ID": c.ID,
"templateID": c.TemplateID,
"from": c.From,
"column": c.Column,
"operator": c.Operator,
}
}
resp["conditions"] = conditions
}
if len(t.JoinTemplate) > 0 {
joins := make([]map[string]interface{}, len(t.JoinTemplate))
for i, j := range t.JoinTemplate {
joins[i] = map[string]interface{}{
"ID": j.ID,
"templateID": j.TemplateID,
"joins": j.Joins,
"table": j.Table,
"on": j.On,
}
}
resp["joinTemplate"] = joins
}
return resp
}
@@ -0,0 +1,36 @@
package system
import (
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type DBApi struct{}
// InitDBRequest 初始化数据库请求
type InitDBRequest struct {
AdminPassword string `json:"adminPassword" binding:"required"`
DBType string `json:"dbType"`
Host string `json:"host"`
Port string `json:"port"`
UserName string `json:"userName"`
Password string `json:"password"`
DBName string `json:"dbName"`
DBPath string `json:"dbPath"`
}
// InitDB 初始化数据库
// 注意:KRA 项目使用 Kratos 框架,数据库配置通过 config.yaml 管理
// 此接口主要用于兼容 GVA 前端
func (d *DBApi) InitDB(c *gin.Context) {
// KRA 项目数据库已通过配置文件初始化
// 此接口返回成功以兼容前端
response.OkWithMessage("数据库已初始化", c)
}
// CheckDB 检测是否需要初始化数据库
func (d *DBApi) CheckDB(c *gin.Context) {
// KRA 项目数据库通过配置文件管理,无需初始化
response.OkWithDetailed(gin.H{"needInit": false}, "数据库无需初始化", c)
}
@@ -0,0 +1,43 @@
package system
import (
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type SystemApi struct{}
// GetServerInfo 获取服务器信息
func (s *SystemApi) GetServerInfo(c *gin.Context) {
serverInfo, err := systemUsecase.GetServerInfo(c.Request.Context())
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
response.OkWithDetailed(gin.H{"server": serverInfo}, "获取成功", c)
}
// GetSystemConfig 获取配置文件内容
func (s *SystemApi) GetSystemConfig(c *gin.Context) {
// 简化实现:返回基本配置信息
response.OkWithDetailed(gin.H{
"config": gin.H{
"system": gin.H{
"env": "production",
},
},
}, "获取成功", c)
}
// SetSystemConfig 设置配置文件内容
func (s *SystemApi) SetSystemConfig(c *gin.Context) {
// 简化实现:配置设置功能
response.OkWithMessage("设置成功", c)
}
// ReloadSystem 重载系统
func (s *SystemApi) ReloadSystem(c *gin.Context) {
// 简化实现:系统重载功能
response.OkWithMessage("重载系统成功", c)
}
@@ -0,0 +1,338 @@
package system
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"kra/internal/biz/system"
"kra/pkg/response"
"github.com/gin-gonic/gin"
)
type VersionApi struct{}
// VersionSearchRequest 版本搜索请求
type VersionSearchRequest struct {
Page int `json:"page" form:"page"`
PageSize int `json:"pageSize" form:"pageSize"`
VersionName *string `json:"versionName" form:"versionName"`
VersionCode *string `json:"versionCode" form:"versionCode"`
CreatedAtRange []time.Time `json:"createdAtRange" form:"createdAtRange[]"`
}
// ExportVersionRequest 导出版本请求
type ExportVersionRequest struct {
VersionName string `json:"versionName" binding:"required"`
VersionCode string `json:"versionCode" binding:"required"`
Description string `json:"description"`
MenuIds []uint `json:"menuIds"`
ApiIds []uint `json:"apiIds"`
DictIds []uint `json:"dictIds"`
}
// VersionInfo 版本信息
type VersionInfo struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
ExportTime string `json:"exportTime"`
}
// ExportVersionResponse 导出版本响应
type ExportVersionResponse struct {
Version VersionInfo `json:"version"`
Menus []map[string]interface{} `json:"menus"`
Apis []map[string]interface{} `json:"apis"`
Dictionaries []map[string]interface{} `json:"dictionaries"`
}
// DeleteSysVersion 删除版本
func (v *VersionApi) DeleteSysVersion(c *gin.Context) {
id := c.Query("ID")
if id == "" {
response.FailWithMessage("缺少参数: ID", c)
return
}
if err := versionUsecase.DeleteSysVersion(c.Request.Context(), id); err != nil {
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
}
// DeleteSysVersionByIds 批量删除版本
func (v *VersionApi) DeleteSysVersionByIds(c *gin.Context) {
ids := c.QueryArray("IDs[]")
if len(ids) == 0 {
response.FailWithMessage("缺少参数: IDs", c)
return
}
if err := versionUsecase.DeleteSysVersionByIds(c.Request.Context(), ids); err != nil {
response.FailWithMessage("批量删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
}
// FindSysVersion 根据ID获取版本
func (v *VersionApi) FindSysVersion(c *gin.Context) {
id := c.Query("ID")
if id == "" {
response.FailWithMessage("缺少参数: ID", c)
return
}
version, err := versionUsecase.GetSysVersion(c.Request.Context(), id)
if err != nil {
response.FailWithMessage("查询失败:"+err.Error(), c)
return
}
response.OkWithData(toVersionResponse(version), c)
}
// GetSysVersionList 分页获取版本列表
func (v *VersionApi) GetSysVersionList(c *gin.Context) {
var req VersionSearchRequest
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
}
searchReq := &system.VersionSearchReq{
Page: req.Page,
PageSize: req.PageSize,
VersionName: req.VersionName,
VersionCode: req.VersionCode,
CreatedAtRange: req.CreatedAtRange,
}
list, total, err := versionUsecase.GetSysVersionInfoList(c.Request.Context(), searchReq)
if err != nil {
response.FailWithMessage("获取失败:"+err.Error(), c)
return
}
result := make([]map[string]interface{}, len(list))
for i, item := range list {
result[i] = toVersionResponse(item)
}
response.OkWithDetailed(response.PageResult{
List: result,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, "获取成功", c)
}
// GetSysVersionPublic 公开版本接口
func (v *VersionApi) GetSysVersionPublic(c *gin.Context) {
response.OkWithDetailed(gin.H{
"info": "不需要鉴权的版本管理接口信息",
}, "获取成功", c)
}
// ExportVersion 创建发版数据
func (v *VersionApi) ExportVersion(c *gin.Context) {
var req ExportVersionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
ctx := c.Request.Context()
// 获取选中的菜单数据
var menuData []map[string]interface{}
if len(req.MenuIds) > 0 {
menus, err := versionUsecase.GetMenusByIds(ctx, req.MenuIds)
if err != nil {
response.FailWithMessage("获取菜单数据失败:"+err.Error(), c)
return
}
for _, m := range menus {
menuData = append(menuData, map[string]interface{}{
"path": m.Path,
"name": m.Name,
"hidden": m.Hidden,
"component": m.Component,
"sort": m.Sort,
})
}
}
// 获取选中的API数据
var apiData []map[string]interface{}
if len(req.ApiIds) > 0 {
apis, err := versionUsecase.GetApisByIds(ctx, req.ApiIds)
if err != nil {
response.FailWithMessage("获取API数据失败:"+err.Error(), c)
return
}
for _, a := range apis {
apiData = append(apiData, map[string]interface{}{
"path": a.Path,
"description": a.Description,
"apiGroup": a.ApiGroup,
"method": a.Method,
})
}
}
// 获取选中的字典数据
var dictData []map[string]interface{}
if len(req.DictIds) > 0 {
dicts, err := versionUsecase.GetDictionariesByIds(ctx, req.DictIds)
if err != nil {
response.FailWithMessage("获取字典数据失败:"+err.Error(), c)
return
}
for _, d := range dicts {
dictData = append(dictData, map[string]interface{}{
"name": d.Name,
"type": d.Type,
"status": d.Status,
"desc": d.Desc,
})
}
}
// 构建导出数据
exportData := ExportVersionResponse{
Version: VersionInfo{
Name: req.VersionName,
Code: req.VersionCode,
Description: req.Description,
ExportTime: time.Now().Format("2006-01-02 15:04:05"),
},
Menus: menuData,
Apis: apiData,
Dictionaries: dictData,
}
// 转换为JSON
jsonData, err := json.MarshalIndent(exportData, "", " ")
if err != nil {
response.FailWithMessage("JSON序列化失败:"+err.Error(), c)
return
}
// 保存版本记录
version := &system.SysVersion{
VersionName: req.VersionName,
VersionCode: req.VersionCode,
Description: req.Description,
VersionData: string(jsonData),
}
if err := versionUsecase.CreateSysVersion(ctx, version); err != nil {
response.FailWithMessage("保存版本记录失败:"+err.Error(), c)
return
}
response.OkWithMessage("创建发版成功", c)
}
// DownloadVersionJson 下载版本JSON数据
func (v *VersionApi) DownloadVersionJson(c *gin.Context) {
id := c.Query("ID")
if id == "" {
response.FailWithMessage("版本ID不能为空", c)
return
}
version, err := versionUsecase.GetSysVersion(c.Request.Context(), id)
if err != nil {
response.FailWithMessage("获取版本记录失败:"+err.Error(), c)
return
}
var jsonData []byte
if version.VersionData != "" {
jsonData = []byte(version.VersionData)
} else {
basicData := ExportVersionResponse{
Version: VersionInfo{
Name: version.VersionName,
Code: version.VersionCode,
Description: version.Description,
ExportTime: version.CreatedAt.Format("2006-01-02 15:04:05"),
},
}
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 导入版本数据
func (v *VersionApi) ImportVersion(c *gin.Context) {
var importData map[string]interface{}
if err := c.ShouldBindJSON(&importData); err != nil {
response.FailWithMessage("解析JSON数据失败:"+err.Error(), c)
return
}
// 验证数据格式
versionInfo, ok := importData["version"].(map[string]interface{})
if !ok {
response.FailWithMessage("版本信息格式错误", c)
return
}
name, _ := versionInfo["name"].(string)
code, _ := versionInfo["code"].(string)
desc, _ := versionInfo["description"].(string)
if name == "" || code == "" {
response.FailWithMessage("版本名称和版本号不能为空", c)
return
}
// 创建导入记录
jsonData, _ := json.Marshal(importData)
version := &system.SysVersion{
VersionName: name,
VersionCode: fmt.Sprintf("%s_imported_%s", code, time.Now().Format("20060102150405")),
Description: fmt.Sprintf("导入版本: %s", desc),
VersionData: string(jsonData),
}
if err := versionUsecase.CreateSysVersion(c.Request.Context(), version); err != nil {
response.FailWithMessage("保存导入记录失败:"+err.Error(), c)
return
}
response.OkWithMessage("导入成功", c)
}
// toVersionResponse 转换版本响应
func toVersionResponse(v *system.SysVersion) map[string]interface{} {
return map[string]interface{}{
"id": v.ID,
"versionName": v.VersionName,
"versionCode": v.VersionCode,
"description": v.Description,
"versionData": v.VersionData,
"createdAt": v.CreatedAt,
"updatedAt": v.UpdatedAt,
}
}