后端重构
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Db 数据库信息
|
||||
type Db struct {
|
||||
Database string `json:"database" gorm:"column:database"`
|
||||
}
|
||||
|
||||
// Table 表信息
|
||||
type Table struct {
|
||||
TableName string `json:"tableName" gorm:"column:table_name"`
|
||||
}
|
||||
|
||||
// Column 列信息
|
||||
type Column struct {
|
||||
ColumnName string `json:"columnName" gorm:"column:column_name"`
|
||||
DataType string `json:"dataType" gorm:"column:data_type"`
|
||||
DataTypeLong string `json:"dataTypeLong" gorm:"column:data_type_long"`
|
||||
ColumnComment string `json:"columnComment" gorm:"column:column_comment"`
|
||||
PrimaryKey int `json:"primaryKey" gorm:"column:primary_key"`
|
||||
}
|
||||
|
||||
// AutoCodeRepo 自动代码仓储接口
|
||||
type AutoCodeRepo interface {
|
||||
GetDB(ctx context.Context) ([]Db, error)
|
||||
GetTables(ctx context.Context, dbName string) ([]Table, error)
|
||||
GetColumn(ctx context.Context, tableName, dbName string) ([]Column, error)
|
||||
}
|
||||
|
||||
// AutoCodeUsecase 自动代码用例
|
||||
type AutoCodeUsecase struct {
|
||||
repo AutoCodeRepo
|
||||
}
|
||||
|
||||
// NewAutoCodeUsecase 创建自动代码用例
|
||||
func NewAutoCodeUsecase(repo AutoCodeRepo) *AutoCodeUsecase {
|
||||
return &AutoCodeUsecase{repo: repo}
|
||||
}
|
||||
|
||||
// GetDB 获取所有数据库
|
||||
func (uc *AutoCodeUsecase) GetDB(ctx context.Context) ([]Db, error) {
|
||||
return uc.repo.GetDB(ctx)
|
||||
}
|
||||
|
||||
// GetTables 获取指定数据库的所有表
|
||||
func (uc *AutoCodeUsecase) GetTables(ctx context.Context, dbName string) ([]Table, error) {
|
||||
return uc.repo.GetTables(ctx, dbName)
|
||||
}
|
||||
|
||||
// GetColumn 获取指定表的所有列
|
||||
func (uc *AutoCodeUsecase) GetColumn(ctx context.Context, tableName, dbName string) ([]Column, error) {
|
||||
return uc.repo.GetColumn(ctx, tableName, dbName)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SysAutoCodeHistory 自动代码生成历史
|
||||
type SysAutoCodeHistory struct {
|
||||
ID uint `json:"ID" gorm:"primarykey"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Table string `json:"tableName" gorm:"column:table_name;comment:表名"`
|
||||
Package string `json:"package" gorm:"column:package;comment:模块名/插件名"`
|
||||
Request string `json:"request" gorm:"type:text;column:request;comment:前端传入的结构化信息"`
|
||||
StructName string `json:"structName" gorm:"column:struct_name;comment:结构体名称"`
|
||||
Abbreviation string `json:"abbreviation" gorm:"column:abbreviation;comment:结构体名称缩写"`
|
||||
BusinessDB string `json:"businessDb" gorm:"column:business_db;comment:业务库"`
|
||||
Description string `json:"description" gorm:"column:description;comment:Struct中文名称"`
|
||||
Templates map[string]string `json:"template" gorm:"serializer:json;type:text;column:templates;comment:模板信息"`
|
||||
Injections map[string]string `json:"injections" gorm:"serializer:json;type:text;column:Injections;comment:注入路径"`
|
||||
Flag int `json:"flag" gorm:"column:flag;comment:[0:创建,1:回滚]"`
|
||||
ApiIDs []uint `json:"apiIDs" gorm:"serializer:json;column:api_ids;comment:api表注册内容"`
|
||||
MenuID uint `json:"menuId" gorm:"column:menu_id;comment:菜单ID"`
|
||||
ExportTemplateID uint `json:"exportTemplateID" gorm:"column:export_template_id;comment:导出模板ID"`
|
||||
PackageID uint `json:"packageID" gorm:"column:package_id;comment:包ID"`
|
||||
}
|
||||
|
||||
func (SysAutoCodeHistory) TableName() string {
|
||||
return "sys_auto_code_histories"
|
||||
}
|
||||
|
||||
// AutoCodeHistorySearchReq 历史搜索请求
|
||||
type AutoCodeHistorySearchReq struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
PageSize int `json:"pageSize" form:"pageSize"`
|
||||
}
|
||||
|
||||
// SysAutoHistoryRollBack 回滚请求
|
||||
type SysAutoHistoryRollBack struct {
|
||||
ID uint `json:"id"`
|
||||
DeleteApi bool `json:"deleteApi"`
|
||||
DeleteMenu bool `json:"deleteMenu"`
|
||||
DeleteTable bool `json:"deleteTable"`
|
||||
}
|
||||
|
||||
// AutoCodeHistoryRepo 自动代码历史仓储接口
|
||||
type AutoCodeHistoryRepo interface {
|
||||
First(ctx context.Context, id uint) (string, error)
|
||||
Delete(ctx context.Context, id uint) error
|
||||
GetList(ctx context.Context, page, pageSize int) ([]SysAutoCodeHistory, int64, error)
|
||||
GetByID(ctx context.Context, id uint) (*SysAutoCodeHistory, error)
|
||||
UpdateFlag(ctx context.Context, id uint, flag int) error
|
||||
DropTable(ctx context.Context, businessDB, tableName string) error
|
||||
}
|
||||
|
||||
// AutoCodeHistoryUsecase 自动代码历史用例
|
||||
type AutoCodeHistoryUsecase struct {
|
||||
repo AutoCodeHistoryRepo
|
||||
}
|
||||
|
||||
// NewAutoCodeHistoryUsecase 创建自动代码历史用例
|
||||
func NewAutoCodeHistoryUsecase(repo AutoCodeHistoryRepo) *AutoCodeHistoryUsecase {
|
||||
return &AutoCodeHistoryUsecase{repo: repo}
|
||||
}
|
||||
|
||||
// First 根据id获取meta信息
|
||||
func (uc *AutoCodeHistoryUsecase) First(ctx context.Context, id uint) (string, error) {
|
||||
return uc.repo.First(ctx, id)
|
||||
}
|
||||
|
||||
// Delete 删除历史记录
|
||||
func (uc *AutoCodeHistoryUsecase) Delete(ctx context.Context, id uint) error {
|
||||
return uc.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// GetList 获取历史记录列表
|
||||
func (uc *AutoCodeHistoryUsecase) GetList(ctx context.Context, page, pageSize int) ([]SysAutoCodeHistory, int64, error) {
|
||||
return uc.repo.GetList(ctx, page, pageSize)
|
||||
}
|
||||
|
||||
// RollBack 回滚自动生成代码
|
||||
// 注意:完整的回滚功能需要AST工具支持,这里只实现基础功能
|
||||
func (uc *AutoCodeHistoryUsecase) RollBack(ctx context.Context, info SysAutoHistoryRollBack) error {
|
||||
// 获取历史记录
|
||||
history, err := uc.repo.GetByID(ctx, info.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除表(如果需要)
|
||||
if info.DeleteTable && history.Table != "" {
|
||||
err = uc.repo.DropTable(ctx, history.BusinessDB, history.Table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 更新标记为已回滚
|
||||
return uc.repo.UpdateFlag(ctx, info.ID, 1)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrErrorNotFound = errors.New("错误日志不存在")
|
||||
)
|
||||
|
||||
// SysError 错误日志实体
|
||||
type SysError struct {
|
||||
ID int64
|
||||
Form string
|
||||
Info string
|
||||
Level string
|
||||
Solution string
|
||||
Status string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// ErrorSearchReq 错误日志搜索请求
|
||||
type ErrorSearchReq struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Form *string
|
||||
Info *string
|
||||
CreatedAtRange []time.Time
|
||||
}
|
||||
|
||||
// ErrorRepo 错误日志仓储接口
|
||||
type ErrorRepo interface {
|
||||
Create(ctx context.Context, sysError *SysError) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
DeleteByIds(ctx context.Context, ids []string) error
|
||||
Update(ctx context.Context, sysError *SysError) error
|
||||
GetById(ctx context.Context, id string) (*SysError, error)
|
||||
GetList(ctx context.Context, req *ErrorSearchReq) ([]*SysError, int64, error)
|
||||
UpdateStatus(ctx context.Context, id string, status string) error
|
||||
UpdateSolution(ctx context.Context, id string, status string, solution string) error
|
||||
}
|
||||
|
||||
// ErrorUsecase 错误日志用例
|
||||
type ErrorUsecase struct {
|
||||
repo ErrorRepo
|
||||
}
|
||||
|
||||
// NewErrorUsecase 创建错误日志用例
|
||||
func NewErrorUsecase(repo ErrorRepo) *ErrorUsecase {
|
||||
return &ErrorUsecase{repo: repo}
|
||||
}
|
||||
|
||||
// CreateSysError 创建错误日志
|
||||
func (uc *ErrorUsecase) CreateSysError(ctx context.Context, sysError *SysError) error {
|
||||
return uc.repo.Create(ctx, sysError)
|
||||
}
|
||||
|
||||
// DeleteSysError 删除错误日志
|
||||
func (uc *ErrorUsecase) DeleteSysError(ctx context.Context, id string) error {
|
||||
return uc.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteSysErrorByIds 批量删除错误日志
|
||||
func (uc *ErrorUsecase) DeleteSysErrorByIds(ctx context.Context, ids []string) error {
|
||||
return uc.repo.DeleteByIds(ctx, ids)
|
||||
}
|
||||
|
||||
// UpdateSysError 更新错误日志
|
||||
func (uc *ErrorUsecase) UpdateSysError(ctx context.Context, sysError *SysError) error {
|
||||
return uc.repo.Update(ctx, sysError)
|
||||
}
|
||||
|
||||
// GetSysError 根据ID获取错误日志
|
||||
func (uc *ErrorUsecase) GetSysError(ctx context.Context, id string) (*SysError, error) {
|
||||
return uc.repo.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// GetSysErrorInfoList 分页获取错误日志列表
|
||||
func (uc *ErrorUsecase) GetSysErrorInfoList(ctx context.Context, req *ErrorSearchReq) ([]*SysError, int64, error) {
|
||||
return uc.repo.GetList(ctx, req)
|
||||
}
|
||||
|
||||
// GetSysErrorSolution 异步处理错误(触发AI生成解决方案)
|
||||
func (uc *ErrorUsecase) GetSysErrorSolution(ctx context.Context, id string) error {
|
||||
// 立即更新为处理中
|
||||
if err := uc.repo.UpdateStatus(ctx, id, "处理中"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 异步处理(简化版本,不调用LLM)
|
||||
go func(errorId string) {
|
||||
// 这里可以集成AI服务生成解决方案
|
||||
// 目前简化为直接标记处理完成
|
||||
_ = uc.repo.UpdateStatus(context.Background(), errorId, "处理完成")
|
||||
}(id)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ExportTemplate 导出模板实体
|
||||
type ExportTemplate struct {
|
||||
ID uint
|
||||
DBName string
|
||||
Name string
|
||||
TableName string
|
||||
TemplateID string
|
||||
TemplateInfo string
|
||||
Limit *int
|
||||
Order string
|
||||
Conditions []*Condition
|
||||
JoinTemplate []*JoinTemplate
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// Condition 条件实体
|
||||
type Condition struct {
|
||||
ID uint
|
||||
TemplateID string
|
||||
From string
|
||||
Column string
|
||||
Operator string
|
||||
}
|
||||
|
||||
// JoinTemplate 关联模板实体
|
||||
type JoinTemplate struct {
|
||||
ID uint
|
||||
TemplateID string
|
||||
Joins string
|
||||
Table string
|
||||
On string
|
||||
}
|
||||
|
||||
// ExportTemplateSearchReq 导出模板搜索请求
|
||||
type ExportTemplateSearchReq struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Name string
|
||||
TableName string
|
||||
TemplateID string
|
||||
StartCreatedAt *time.Time
|
||||
EndCreatedAt *time.Time
|
||||
}
|
||||
|
||||
// ExportTemplateRepo 导出模板仓储接口
|
||||
type ExportTemplateRepo interface {
|
||||
Create(ctx context.Context, template *ExportTemplate) error
|
||||
Update(ctx context.Context, template *ExportTemplate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
FindByID(ctx context.Context, id uint) (*ExportTemplate, error)
|
||||
FindByTemplateID(ctx context.Context, templateID string) (*ExportTemplate, error)
|
||||
GetList(ctx context.Context, req *ExportTemplateSearchReq) ([]*ExportTemplate, int64, error)
|
||||
DeleteConditionsByTemplateID(ctx context.Context, templateID string) error
|
||||
DeleteJoinsByTemplateID(ctx context.Context, templateID string) error
|
||||
CreateConditions(ctx context.Context, conditions []*Condition) error
|
||||
CreateJoins(ctx context.Context, joins []*JoinTemplate) error
|
||||
// Excel 相关
|
||||
GetDB(dbName string) interface{}
|
||||
ExecuteQuery(ctx context.Context, dbName, sql string, args ...interface{}) ([]map[string]interface{}, error)
|
||||
GetColumnTypes(ctx context.Context, dbName, tableName string) ([]string, error)
|
||||
HasDeletedAtColumn(ctx context.Context, tableName string) bool
|
||||
ImportData(ctx context.Context, dbName, tableName string, items []map[string]interface{}) error
|
||||
}
|
||||
|
||||
// ExportTemplateUsecase 导出模板用例
|
||||
type ExportTemplateUsecase struct {
|
||||
repo ExportTemplateRepo
|
||||
}
|
||||
|
||||
// NewExportTemplateUsecase 创建导出模板用例
|
||||
func NewExportTemplateUsecase(repo ExportTemplateRepo) *ExportTemplateUsecase {
|
||||
return &ExportTemplateUsecase{repo: repo}
|
||||
}
|
||||
|
||||
// CreateExportTemplate 创建导出模板
|
||||
func (uc *ExportTemplateUsecase) CreateExportTemplate(ctx context.Context, template *ExportTemplate) error {
|
||||
return uc.repo.Create(ctx, template)
|
||||
}
|
||||
|
||||
// DeleteExportTemplate 删除导出模板
|
||||
func (uc *ExportTemplateUsecase) DeleteExportTemplate(ctx context.Context, id uint) error {
|
||||
return uc.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteExportTemplateByIds 批量删除导出模板
|
||||
func (uc *ExportTemplateUsecase) DeleteExportTemplateByIds(ctx context.Context, ids []uint) error {
|
||||
for _, id := range ids {
|
||||
if err := uc.repo.Delete(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateExportTemplate 更新导出模板
|
||||
func (uc *ExportTemplateUsecase) UpdateExportTemplate(ctx context.Context, template *ExportTemplate) error {
|
||||
// 删除旧的条件和关联
|
||||
if err := uc.repo.DeleteConditionsByTemplateID(ctx, template.TemplateID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.repo.DeleteJoinsByTemplateID(ctx, template.TemplateID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 保存条件
|
||||
conditions := template.Conditions
|
||||
template.Conditions = nil
|
||||
|
||||
// 保存关联
|
||||
joins := template.JoinTemplate
|
||||
template.JoinTemplate = nil
|
||||
|
||||
// 更新模板
|
||||
if err := uc.repo.Update(ctx, template); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 创建新的条件
|
||||
if len(conditions) > 0 {
|
||||
for i := range conditions {
|
||||
conditions[i].ID = 0
|
||||
}
|
||||
if err := uc.repo.CreateConditions(ctx, conditions); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的关联
|
||||
if len(joins) > 0 {
|
||||
for i := range joins {
|
||||
joins[i].ID = 0
|
||||
}
|
||||
if err := uc.repo.CreateJoins(ctx, joins); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExportTemplate 根据ID获取导出模板
|
||||
func (uc *ExportTemplateUsecase) GetExportTemplate(ctx context.Context, id uint) (*ExportTemplate, error) {
|
||||
return uc.repo.FindByID(ctx, id)
|
||||
}
|
||||
|
||||
// GetExportTemplateList 分页获取导出模板列表
|
||||
func (uc *ExportTemplateUsecase) GetExportTemplateList(ctx context.Context, req *ExportTemplateSearchReq) ([]*ExportTemplate, int64, error) {
|
||||
return uc.repo.GetList(ctx, req)
|
||||
}
|
||||
|
||||
// ExportExcel 导出Excel
|
||||
func (uc *ExportTemplateUsecase) ExportExcel(ctx context.Context, templateID string, values url.Values) (*bytes.Buffer, string, error) {
|
||||
params := values.Get("params")
|
||||
paramsValues, err := url.ParseQuery(params)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("解析 params 参数失败: %v", err)
|
||||
}
|
||||
|
||||
template, err := uc.repo.FindByTemplateID(ctx, templateID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// 解析模板信息
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := getJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var tableTitle []string
|
||||
var selectKeyFmt []string
|
||||
for _, key := range columns {
|
||||
selectKeyFmt = append(selectKeyFmt, key)
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
selects := strings.Join(selectKeyFmt, ", ")
|
||||
|
||||
// 构建SQL
|
||||
var sb strings.Builder
|
||||
sb.WriteString("SELECT ")
|
||||
sb.WriteString(selects)
|
||||
sb.WriteString(" FROM ")
|
||||
sb.WriteString(template.TableName)
|
||||
|
||||
// JOIN
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.Joins)
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.Table)
|
||||
sb.WriteString(" ON ")
|
||||
sb.WriteString(join.On)
|
||||
}
|
||||
}
|
||||
|
||||
// WHERE
|
||||
var wheres []string
|
||||
var args []interface{}
|
||||
|
||||
// 软删除过滤
|
||||
filterDeleted := paramsValues.Get("filterDeleted") == "true"
|
||||
if filterDeleted {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
||||
for _, join := range template.JoinTemplate {
|
||||
if uc.repo.HasDeletedAtColumn(ctx, join.Table) {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 条件
|
||||
for _, condition := range template.Conditions {
|
||||
value := paramsValues.Get(condition.From)
|
||||
op := strings.ToUpper(strings.TrimSpace(condition.Operator))
|
||||
|
||||
if op == "BETWEEN" {
|
||||
startValue := paramsValues.Get("start" + condition.From)
|
||||
endValue := paramsValues.Get("end" + condition.From)
|
||||
if startValue != "" && endValue != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s BETWEEN ? AND ?", condition.Column))
|
||||
args = append(args, startValue, endValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if op == "LIKE" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s LIKE ?", condition.Column))
|
||||
args = append(args, "%"+value+"%")
|
||||
} else if op == "IN" || op == "NOT IN" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s (?)", condition.Column, op))
|
||||
args = append(args, value)
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s ?", condition.Column, op))
|
||||
args = append(args, value)
|
||||
}
|
||||
}
|
||||
|
||||
if len(wheres) > 0 {
|
||||
sb.WriteString(" WHERE ")
|
||||
sb.WriteString(strings.Join(wheres, " AND "))
|
||||
}
|
||||
|
||||
// ORDER
|
||||
order := paramsValues.Get("order")
|
||||
if order == "" && template.Order != "" {
|
||||
order = template.Order
|
||||
}
|
||||
if order != "" {
|
||||
sb.WriteString(" ORDER BY ")
|
||||
sb.WriteString(order)
|
||||
}
|
||||
|
||||
// LIMIT
|
||||
limit := paramsValues.Get("limit")
|
||||
if limit == "" && template.Limit != nil && *template.Limit != 0 {
|
||||
limit = strconv.Itoa(*template.Limit)
|
||||
}
|
||||
if limit != "" {
|
||||
sb.WriteString(" LIMIT ")
|
||||
sb.WriteString(limit)
|
||||
}
|
||||
|
||||
// OFFSET
|
||||
offset := paramsValues.Get("offset")
|
||||
if offset != "" {
|
||||
sb.WriteString(" OFFSET ")
|
||||
sb.WriteString(offset)
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
tableMap, err := uc.repo.ExecuteQuery(ctx, template.DBName, sb.String(), args...)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// 生成Excel
|
||||
return uc.generateExcel(tableMap, columns, tableTitle, template)
|
||||
}
|
||||
|
||||
// generateExcel 生成Excel文件
|
||||
func (uc *ExportTemplateUsecase) generateExcel(tableMap []map[string]interface{}, columns, tableTitle []string, template *ExportTemplate) (*bytes.Buffer, string, error) {
|
||||
// 简化实现:生成CSV格式
|
||||
var buf bytes.Buffer
|
||||
|
||||
// 写入标题行
|
||||
buf.WriteString(strings.Join(tableTitle, ","))
|
||||
buf.WriteString("\n")
|
||||
|
||||
// 写入数据行
|
||||
for _, row := range tableMap {
|
||||
var values []string
|
||||
for _, col := range columns {
|
||||
col = strings.ReplaceAll(col, "\"", "")
|
||||
col = strings.ReplaceAll(col, "`", "")
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
columnAs := strings.Split(col, " as ")
|
||||
if len(columnAs) > 1 {
|
||||
col = strings.TrimSpace(columnAs[1])
|
||||
} else {
|
||||
columnArr := strings.Split(col, ".")
|
||||
if len(columnArr) > 1 {
|
||||
col = columnArr[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if t, ok := row[col].(time.Time); ok {
|
||||
values = append(values, t.Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
values = append(values, fmt.Sprintf("%v", row[col]))
|
||||
}
|
||||
}
|
||||
buf.WriteString(strings.Join(values, ","))
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
return &buf, template.Name, nil
|
||||
}
|
||||
|
||||
// PreviewSQL 预览SQL
|
||||
func (uc *ExportTemplateUsecase) PreviewSQL(ctx context.Context, templateID string, values url.Values) (string, error) {
|
||||
params := values.Get("params")
|
||||
paramsValues, _ := url.ParseQuery(params)
|
||||
|
||||
template, err := uc.repo.FindByTemplateID(ctx, templateID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 解析模板信息
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := getJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var selectKeyFmt []string
|
||||
for _, key := range columns {
|
||||
selectKeyFmt = append(selectKeyFmt, key)
|
||||
}
|
||||
selects := strings.Join(selectKeyFmt, ", ")
|
||||
|
||||
// 构建SQL
|
||||
var sb strings.Builder
|
||||
sb.WriteString("SELECT ")
|
||||
sb.WriteString(selects)
|
||||
sb.WriteString(" FROM ")
|
||||
sb.WriteString(template.TableName)
|
||||
|
||||
// JOIN
|
||||
if len(template.JoinTemplate) > 0 {
|
||||
for _, join := range template.JoinTemplate {
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.Joins)
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(join.Table)
|
||||
sb.WriteString(" ON ")
|
||||
sb.WriteString(join.On)
|
||||
}
|
||||
}
|
||||
|
||||
// WHERE
|
||||
var wheres []string
|
||||
|
||||
filterDeleted := false
|
||||
if paramsValues != nil {
|
||||
filterDeleted = paramsValues.Get("filterDeleted") == "true"
|
||||
}
|
||||
if filterDeleted {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
||||
for _, join := range template.JoinTemplate {
|
||||
if uc.repo.HasDeletedAtColumn(ctx, join.Table) {
|
||||
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 条件
|
||||
for _, condition := range template.Conditions {
|
||||
op := strings.ToUpper(strings.TrimSpace(condition.Operator))
|
||||
col := strings.TrimSpace(condition.Column)
|
||||
|
||||
val := ""
|
||||
if paramsValues != nil {
|
||||
val = paramsValues.Get(condition.From)
|
||||
}
|
||||
|
||||
switch op {
|
||||
case "BETWEEN":
|
||||
startValue := ""
|
||||
endValue := ""
|
||||
if paramsValues != nil {
|
||||
startValue = paramsValues.Get("start" + condition.From)
|
||||
endValue = paramsValues.Get("end" + condition.From)
|
||||
}
|
||||
if startValue != "" && endValue != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s BETWEEN '%s' AND '%s'", col, startValue, endValue))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s BETWEEN {start%s} AND {end%s}", col, condition.From, condition.From))
|
||||
}
|
||||
case "IN", "NOT IN":
|
||||
if val != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s ('%s')", col, op, val))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s ({%s})", col, op, condition.From))
|
||||
}
|
||||
case "LIKE":
|
||||
if val != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s LIKE '%%%s%%'", col, val))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s LIKE {%%%s%%}", col, condition.From))
|
||||
}
|
||||
default:
|
||||
if val != "" {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s '%s'", col, op, val))
|
||||
} else {
|
||||
wheres = append(wheres, fmt.Sprintf("%s %s {%s}", col, op, condition.From))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(wheres) > 0 {
|
||||
sb.WriteString(" WHERE ")
|
||||
sb.WriteString(strings.Join(wheres, " AND "))
|
||||
}
|
||||
|
||||
// ORDER
|
||||
order := ""
|
||||
if paramsValues != nil {
|
||||
order = paramsValues.Get("order")
|
||||
}
|
||||
if order == "" && template.Order != "" {
|
||||
order = template.Order
|
||||
}
|
||||
if order != "" {
|
||||
sb.WriteString(" ORDER BY ")
|
||||
sb.WriteString(order)
|
||||
}
|
||||
|
||||
// LIMIT/OFFSET
|
||||
limitStr := ""
|
||||
offsetStr := ""
|
||||
if paramsValues != nil {
|
||||
limitStr = paramsValues.Get("limit")
|
||||
offsetStr = paramsValues.Get("offset")
|
||||
}
|
||||
if limitStr == "" && template.Limit != nil && *template.Limit != 0 {
|
||||
limitStr = strconv.Itoa(*template.Limit)
|
||||
}
|
||||
|
||||
limitInt := 0
|
||||
offsetInt := 0
|
||||
if limitStr != "" {
|
||||
if v, e := strconv.Atoi(limitStr); e == nil {
|
||||
limitInt = v
|
||||
}
|
||||
}
|
||||
if offsetStr != "" {
|
||||
if v, e := strconv.Atoi(offsetStr); e == nil {
|
||||
offsetInt = v
|
||||
}
|
||||
}
|
||||
|
||||
if limitInt > 0 {
|
||||
sb.WriteString(" LIMIT ")
|
||||
sb.WriteString(strconv.Itoa(limitInt))
|
||||
if offsetInt > 0 {
|
||||
sb.WriteString(" OFFSET ")
|
||||
sb.WriteString(strconv.Itoa(offsetInt))
|
||||
}
|
||||
} else if offsetInt > 0 {
|
||||
sb.WriteString(" OFFSET ")
|
||||
sb.WriteString(strconv.Itoa(offsetInt))
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
// ExportTemplate 导出Excel模板
|
||||
func (uc *ExportTemplateUsecase) ExportTemplate(ctx context.Context, templateID string) (*bytes.Buffer, string, error) {
|
||||
template, err := uc.repo.FindByTemplateID(ctx, templateID)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var templateInfoMap = make(map[string]string)
|
||||
columns, err := getJSONKeys(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var tableTitle []string
|
||||
for _, key := range columns {
|
||||
tableTitle = append(tableTitle, templateInfoMap[key])
|
||||
}
|
||||
|
||||
// 生成CSV模板
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(strings.Join(tableTitle, ","))
|
||||
buf.WriteString("\n")
|
||||
|
||||
return &buf, template.Name, nil
|
||||
}
|
||||
|
||||
// ImportExcel 导入Excel
|
||||
func (uc *ExportTemplateUsecase) ImportExcel(ctx context.Context, templateID string, file *multipart.FileHeader) error {
|
||||
template, err := uc.repo.FindByTemplateID(ctx, templateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
// 简化实现:读取CSV格式
|
||||
var templateInfoMap = make(map[string]string)
|
||||
if err := json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var titleKeyMap = make(map[string]string)
|
||||
for key, title := range templateInfoMap {
|
||||
titleKeyMap[title] = key
|
||||
}
|
||||
|
||||
// 这里需要实际的Excel解析逻辑
|
||||
return errors.New("Excel导入功能需要excelize库支持")
|
||||
}
|
||||
|
||||
// getJSONKeys 获取JSON对象的键(保持顺序)
|
||||
func getJSONKeys(jsonStr string) ([]string, error) {
|
||||
var result []string
|
||||
dec := json.NewDecoder(strings.NewReader(jsonStr))
|
||||
|
||||
// 读取开始的 {
|
||||
t, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t != json.Delim('{') {
|
||||
return nil, errors.New("expected {")
|
||||
}
|
||||
|
||||
for dec.More() {
|
||||
// 读取键
|
||||
t, err := dec.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, ok := t.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expected string key")
|
||||
}
|
||||
result = append(result, key)
|
||||
|
||||
// 跳过值
|
||||
var value interface{}
|
||||
if err := dec.Decode(&value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"kra/pkg/server"
|
||||
)
|
||||
|
||||
// SystemUsecase 系统配置用例
|
||||
type SystemUsecase struct {
|
||||
diskMountPoints []string
|
||||
}
|
||||
|
||||
// NewSystemUsecase 创建系统配置用例
|
||||
func NewSystemUsecase() *SystemUsecase {
|
||||
// 默认磁盘挂载点,可通过配置扩展
|
||||
return &SystemUsecase{
|
||||
diskMountPoints: []string{"/"},
|
||||
}
|
||||
}
|
||||
|
||||
// GetServerInfo 获取服务器信息
|
||||
func (uc *SystemUsecase) GetServerInfo(ctx context.Context) (*server.Server, error) {
|
||||
return server.GetServerInfo(uc.diskMountPoints)
|
||||
}
|
||||
|
||||
// SetDiskMountPoints 设置磁盘挂载点
|
||||
func (uc *SystemUsecase) SetDiskMountPoints(mountPoints []string) {
|
||||
uc.diskMountPoints = mountPoints
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVersionNotFound = errors.New("版本不存在")
|
||||
)
|
||||
|
||||
// SysVersion 版本管理实体
|
||||
type SysVersion struct {
|
||||
ID int64
|
||||
VersionName string
|
||||
VersionCode string
|
||||
Description string
|
||||
VersionData string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// VersionSearchReq 版本搜索请求
|
||||
type VersionSearchReq struct {
|
||||
Page int
|
||||
PageSize int
|
||||
VersionName *string
|
||||
VersionCode *string
|
||||
CreatedAtRange []time.Time
|
||||
}
|
||||
|
||||
// VersionRepo 版本仓储接口
|
||||
type VersionRepo interface {
|
||||
Create(ctx context.Context, version *SysVersion) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
DeleteByIds(ctx context.Context, ids []string) error
|
||||
GetById(ctx context.Context, id string) (*SysVersion, error)
|
||||
GetList(ctx context.Context, req *VersionSearchReq) ([]*SysVersion, int64, error)
|
||||
}
|
||||
|
||||
// VersionUsecase 版本用例
|
||||
type VersionUsecase struct {
|
||||
repo VersionRepo
|
||||
menuRepo MenuRepo
|
||||
apiRepo ApiRepo
|
||||
dictRepo DictionaryRepo
|
||||
}
|
||||
|
||||
// NewVersionUsecase 创建版本用例
|
||||
func NewVersionUsecase(repo VersionRepo, menuRepo MenuRepo, apiRepo ApiRepo, dictRepo DictionaryRepo) *VersionUsecase {
|
||||
return &VersionUsecase{
|
||||
repo: repo,
|
||||
menuRepo: menuRepo,
|
||||
apiRepo: apiRepo,
|
||||
dictRepo: dictRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSysVersion 创建版本
|
||||
func (uc *VersionUsecase) CreateSysVersion(ctx context.Context, version *SysVersion) error {
|
||||
return uc.repo.Create(ctx, version)
|
||||
}
|
||||
|
||||
// DeleteSysVersion 删除版本
|
||||
func (uc *VersionUsecase) DeleteSysVersion(ctx context.Context, id string) error {
|
||||
return uc.repo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteSysVersionByIds 批量删除版本
|
||||
func (uc *VersionUsecase) DeleteSysVersionByIds(ctx context.Context, ids []string) error {
|
||||
return uc.repo.DeleteByIds(ctx, ids)
|
||||
}
|
||||
|
||||
// GetSysVersion 根据ID获取版本
|
||||
func (uc *VersionUsecase) GetSysVersion(ctx context.Context, id string) (*SysVersion, error) {
|
||||
return uc.repo.GetById(ctx, id)
|
||||
}
|
||||
|
||||
// GetSysVersionInfoList 分页获取版本列表
|
||||
func (uc *VersionUsecase) GetSysVersionInfoList(ctx context.Context, req *VersionSearchReq) ([]*SysVersion, int64, error) {
|
||||
return uc.repo.GetList(ctx, req)
|
||||
}
|
||||
|
||||
// GetMenusByIds 根据ID列表获取菜单数据
|
||||
func (uc *VersionUsecase) GetMenusByIds(ctx context.Context, ids []uint) ([]*Menu, error) {
|
||||
// 转换为string类型
|
||||
strIds := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
strIds[i] = strconv.FormatUint(uint64(id), 10)
|
||||
}
|
||||
baseMenus, err := uc.menuRepo.FindBaseMenusByIds(ctx, strIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 转换为Menu类型
|
||||
menus := make([]*Menu, len(baseMenus))
|
||||
for i, bm := range baseMenus {
|
||||
menus[i] = &Menu{
|
||||
BaseMenu: *bm,
|
||||
MenuId: bm.ID,
|
||||
}
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
// GetApisByIds 根据ID列表获取API数据
|
||||
func (uc *VersionUsecase) GetApisByIds(ctx context.Context, ids []uint) ([]*Api, error) {
|
||||
return uc.apiRepo.FindByIds(ctx, ids)
|
||||
}
|
||||
|
||||
// GetDictionariesByIds 根据ID列表获取字典数据(简化实现)
|
||||
func (uc *VersionUsecase) GetDictionariesByIds(ctx context.Context, ids []uint) ([]*Dictionary, error) {
|
||||
var result []*Dictionary
|
||||
for _, id := range ids {
|
||||
dict, err := uc.dictRepo.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, dict)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user