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 }