2023-12-30 11:57:36 +00:00
|
|
|
|
package system
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"encoding/json"
|
2025-04-29 05:21:22 +00:00
|
|
|
|
"errors"
|
2023-12-30 11:57:36 +00:00
|
|
|
|
"fmt"
|
2024-12-21 16:13:22 +00:00
|
|
|
|
"mime/multipart"
|
|
|
|
|
|
"net/url"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2023-12-30 11:57:36 +00:00
|
|
|
|
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
|
|
|
|
|
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
|
|
|
|
|
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
|
|
|
|
|
|
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
|
2024-03-15 03:38:34 +00:00
|
|
|
|
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
2023-12-30 11:57:36 +00:00
|
|
|
|
"github.com/xuri/excelize/v2"
|
2024-01-05 15:09:07 +00:00
|
|
|
|
"gorm.io/gorm"
|
2023-12-30 11:57:36 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type SysExportTemplateService struct {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-21 03:33:25 +00:00
|
|
|
|
var SysExportTemplateServiceApp = new(SysExportTemplateService)
|
|
|
|
|
|
|
2023-12-30 11:57:36 +00:00
|
|
|
|
// CreateSysExportTemplate 创建导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) CreateSysExportTemplate(sysExportTemplate *system.SysExportTemplate) (err error) {
|
|
|
|
|
|
err = global.GVA_DB.Create(sysExportTemplate).Error
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteSysExportTemplate 删除导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
|
|
|
|
|
err = global.GVA_DB.Delete(&sysExportTemplate).Error
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteSysExportTemplateByIds 批量删除导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) DeleteSysExportTemplateByIds(ids request.IdsReq) (err error) {
|
|
|
|
|
|
err = global.GVA_DB.Delete(&[]system.SysExportTemplate{}, "id in ?", ids.Ids).Error
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateSysExportTemplate 更新导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplate(sysExportTemplate system.SysExportTemplate) (err error) {
|
2024-03-05 15:29:59 +00:00
|
|
|
|
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
|
conditions := sysExportTemplate.Conditions
|
|
|
|
|
|
e := tx.Delete(&[]system.Condition{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
|
|
|
|
|
if e != nil {
|
|
|
|
|
|
return e
|
|
|
|
|
|
}
|
|
|
|
|
|
sysExportTemplate.Conditions = nil
|
2024-04-09 14:05:12 +00:00
|
|
|
|
|
|
|
|
|
|
joins := sysExportTemplate.JoinTemplate
|
|
|
|
|
|
e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error
|
|
|
|
|
|
if e != nil {
|
|
|
|
|
|
return e
|
|
|
|
|
|
}
|
|
|
|
|
|
sysExportTemplate.JoinTemplate = nil
|
|
|
|
|
|
|
2024-03-05 15:29:59 +00:00
|
|
|
|
e = tx.Updates(&sysExportTemplate).Error
|
|
|
|
|
|
if e != nil {
|
|
|
|
|
|
return e
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(conditions) > 0 {
|
|
|
|
|
|
for i := range conditions {
|
|
|
|
|
|
conditions[i].ID = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
e = tx.Create(&conditions).Error
|
|
|
|
|
|
}
|
2024-04-09 14:05:12 +00:00
|
|
|
|
if len(joins) > 0 {
|
|
|
|
|
|
for i := range joins {
|
|
|
|
|
|
joins[i].ID = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
e = tx.Create(&joins).Error
|
|
|
|
|
|
}
|
2024-03-05 15:29:59 +00:00
|
|
|
|
return e
|
|
|
|
|
|
})
|
2023-12-30 11:57:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSysExportTemplate 根据id获取导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) {
|
2024-04-09 14:05:12 +00:00
|
|
|
|
err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error
|
2023-12-30 11:57:36 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSysExportTemplateInfoList 分页获取导出模板记录
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateInfoList(info systemReq.SysExportTemplateSearch) (list []system.SysExportTemplate, total int64, err error) {
|
|
|
|
|
|
limit := info.PageSize
|
|
|
|
|
|
offset := info.PageSize * (info.Page - 1)
|
|
|
|
|
|
// 创建db
|
|
|
|
|
|
db := global.GVA_DB.Model(&system.SysExportTemplate{})
|
|
|
|
|
|
var sysExportTemplates []system.SysExportTemplate
|
|
|
|
|
|
// 如果有条件搜索 下方会自动创建搜索语句
|
|
|
|
|
|
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
|
|
|
|
|
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
|
|
|
|
|
}
|
|
|
|
|
|
if info.Name != "" {
|
|
|
|
|
|
db = db.Where("name LIKE ?", "%"+info.Name+"%")
|
|
|
|
|
|
}
|
|
|
|
|
|
if info.TableName != "" {
|
|
|
|
|
|
db = db.Where("table_name = ?", info.TableName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if info.TemplateID != "" {
|
|
|
|
|
|
db = db.Where("template_id = ?", info.TemplateID)
|
|
|
|
|
|
}
|
|
|
|
|
|
err = db.Count(&total).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if limit != 0 {
|
|
|
|
|
|
db = db.Limit(limit).Offset(offset)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = db.Find(&sysExportTemplates).Error
|
|
|
|
|
|
return sysExportTemplates, total, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-05 15:09:07 +00:00
|
|
|
|
// ExportExcel 导出Excel
|
2023-12-30 11:57:36 +00:00
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
2024-03-05 15:29:59 +00:00
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) {
|
2025-05-21 09:11:23 +00:00
|
|
|
|
var params = values.Get("params")
|
|
|
|
|
|
paramsValues, err := url.ParseQuery(params)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", fmt.Errorf("解析 params 参数失败: %v", err)
|
|
|
|
|
|
}
|
2023-12-30 11:57:36 +00:00
|
|
|
|
var template system.SysExportTemplate
|
2024-04-09 14:05:12 +00:00
|
|
|
|
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
2023-12-30 11:57:36 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
f := excelize.NewFile()
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
// Create a new sheet.
|
|
|
|
|
|
index, err := f.NewSheet("Sheet1")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
var templateInfoMap = make(map[string]string)
|
2024-03-15 03:38:34 +00:00
|
|
|
|
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
2023-12-30 11:57:36 +00:00
|
|
|
|
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
var tableTitle []string
|
2024-07-27 09:59:00 +00:00
|
|
|
|
var selectKeyFmt []string
|
2024-03-15 03:38:34 +00:00
|
|
|
|
for _, key := range columns {
|
2024-12-21 16:13:22 +00:00
|
|
|
|
selectKeyFmt = append(selectKeyFmt, key)
|
2023-12-30 11:57:36 +00:00
|
|
|
|
tableTitle = append(tableTitle, templateInfoMap[key])
|
|
|
|
|
|
}
|
2024-07-27 09:59:00 +00:00
|
|
|
|
|
|
|
|
|
|
selects := strings.Join(selectKeyFmt, ", ")
|
2023-12-30 11:57:36 +00:00
|
|
|
|
var tableMap []map[string]interface{}
|
2024-03-15 15:22:20 +00:00
|
|
|
|
db := global.GVA_DB
|
|
|
|
|
|
if template.DBName != "" {
|
|
|
|
|
|
db = global.MustGetGlobalDBByDBName(template.DBName)
|
|
|
|
|
|
}
|
2024-04-09 14:05:12 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 如果有自定义SQL,则优先使用自定义SQL
|
|
|
|
|
|
if template.SQL != "" {
|
|
|
|
|
|
// 将 url.Values 转换为 map[string]interface{} 以支持 GORM 的命名参数
|
|
|
|
|
|
sqlParams := make(map[string]interface{})
|
|
|
|
|
|
for k, v := range paramsValues {
|
|
|
|
|
|
if len(v) > 0 {
|
|
|
|
|
|
sqlParams[k] = v[0]
|
|
|
|
|
|
}
|
2024-04-09 14:05:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 执行原生 SQL,支持 @key 命名参数
|
|
|
|
|
|
err = db.Raw(template.SQL, sqlParams).Scan(&tableMap).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if len(template.JoinTemplate) > 0 {
|
|
|
|
|
|
for _, join := range template.JoinTemplate {
|
|
|
|
|
|
db = db.Joins(join.JOINS + " " + join.Table + " ON " + join.ON)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-03-05 15:29:59 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
db = db.Select(selects).Table(template.TableName)
|
2025-04-18 03:40:03 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
filterDeleted := false
|
2025-04-18 03:40:03 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
filterParam := paramsValues.Get("filterDeleted")
|
|
|
|
|
|
if filterParam == "true" {
|
|
|
|
|
|
filterDeleted = true
|
|
|
|
|
|
}
|
2025-04-18 03:40:03 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if filterDeleted {
|
|
|
|
|
|
// 自动过滤主表的软删除
|
|
|
|
|
|
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤关联表的软删除(如果有)
|
|
|
|
|
|
if len(template.JoinTemplate) > 0 {
|
|
|
|
|
|
for _, join := range template.JoinTemplate {
|
|
|
|
|
|
// 检查关联表是否有deleted_at字段
|
|
|
|
|
|
hasDeletedAt := sysExportTemplateService.hasDeletedAtColumn(join.Table)
|
|
|
|
|
|
if hasDeletedAt {
|
|
|
|
|
|
db = db.Where(fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
|
|
|
|
|
}
|
2025-04-18 03:40:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if len(template.Conditions) > 0 {
|
|
|
|
|
|
for _, condition := range template.Conditions {
|
|
|
|
|
|
sql := fmt.Sprintf("%s %s ?", condition.Column, condition.Operator)
|
|
|
|
|
|
value := paramsValues.Get(condition.From)
|
2025-05-21 09:41:54 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if condition.Operator == "IN" || condition.Operator == "NOT IN" {
|
|
|
|
|
|
sql = fmt.Sprintf("%s %s (?)", condition.Column, condition.Operator)
|
|
|
|
|
|
}
|
2025-05-21 09:41:54 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if condition.Operator == "BETWEEN" {
|
|
|
|
|
|
sql = fmt.Sprintf("%s BETWEEN ? AND ?", condition.Column)
|
|
|
|
|
|
startValue := paramsValues.Get("start" + condition.From)
|
|
|
|
|
|
endValue := paramsValues.Get("end" + condition.From)
|
|
|
|
|
|
if startValue != "" && endValue != "" {
|
|
|
|
|
|
db = db.Where(sql, startValue, endValue)
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
2025-11-10 03:05:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if value != "" {
|
|
|
|
|
|
if condition.Operator == "LIKE" {
|
|
|
|
|
|
value = "%" + value + "%"
|
|
|
|
|
|
}
|
|
|
|
|
|
db = db.Where(sql, value)
|
2024-03-05 15:29:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 通过参数传入limit
|
|
|
|
|
|
limit := paramsValues.Get("limit")
|
|
|
|
|
|
if limit != "" {
|
|
|
|
|
|
l, e := strconv.Atoi(limit)
|
|
|
|
|
|
if e == nil {
|
|
|
|
|
|
db = db.Limit(l)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 模板的默认limit
|
|
|
|
|
|
if limit == "" && template.Limit != nil && *template.Limit != 0 {
|
|
|
|
|
|
db = db.Limit(*template.Limit)
|
2024-03-12 13:08:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 通过参数传入offset
|
|
|
|
|
|
offset := paramsValues.Get("offset")
|
|
|
|
|
|
if offset != "" {
|
|
|
|
|
|
o, e := strconv.Atoi(offset)
|
|
|
|
|
|
if e == nil {
|
|
|
|
|
|
db = db.Offset(o)
|
|
|
|
|
|
}
|
2024-03-12 13:08:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 获取当前表的所有字段
|
|
|
|
|
|
table := template.TableName
|
|
|
|
|
|
orderColumns, err := db.Migrator().ColumnTypes(table)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
2024-06-02 13:13:51 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 创建一个 map 来存储字段名
|
|
|
|
|
|
fields := make(map[string]bool)
|
2024-06-02 13:13:51 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
for _, column := range orderColumns {
|
|
|
|
|
|
fields[column.Name()] = true
|
|
|
|
|
|
}
|
2024-06-02 13:13:51 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 通过参数传入order
|
|
|
|
|
|
order := paramsValues.Get("order")
|
2024-06-02 13:13:51 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if order == "" && template.Order != "" {
|
|
|
|
|
|
// 如果没有order入参,这里会使用模板的默认排序
|
|
|
|
|
|
order = template.Order
|
|
|
|
|
|
}
|
2024-06-02 13:13:51 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if order != "" {
|
|
|
|
|
|
checkOrderArr := strings.Split(order, " ")
|
|
|
|
|
|
orderStr := ""
|
|
|
|
|
|
// 检查请求的排序字段是否在字段列表中
|
|
|
|
|
|
if _, ok := fields[checkOrderArr[0]]; !ok {
|
|
|
|
|
|
return nil, "", fmt.Errorf("order by %s is not in the fields", order)
|
2024-06-02 13:13:51 +00:00
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
orderStr = checkOrderArr[0]
|
|
|
|
|
|
if len(checkOrderArr) > 1 {
|
|
|
|
|
|
if checkOrderArr[1] != "asc" && checkOrderArr[1] != "desc" {
|
|
|
|
|
|
return nil, "", fmt.Errorf("order by %s is not secure", order)
|
|
|
|
|
|
}
|
|
|
|
|
|
orderStr = orderStr + " " + checkOrderArr[1]
|
|
|
|
|
|
}
|
|
|
|
|
|
db = db.Order(orderStr)
|
2024-06-02 13:13:51 +00:00
|
|
|
|
}
|
2024-03-12 13:08:11 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
err = db.Debug().Find(&tableMap).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
2023-12-30 11:57:36 +00:00
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
|
2023-12-30 11:57:36 +00:00
|
|
|
|
var rows [][]string
|
|
|
|
|
|
rows = append(rows, tableTitle)
|
2024-07-10 09:52:53 +00:00
|
|
|
|
for _, exTable := range tableMap {
|
2023-12-30 11:57:36 +00:00
|
|
|
|
var row []string
|
|
|
|
|
|
for _, column := range columns {
|
2024-11-06 03:19:17 +00:00
|
|
|
|
column = strings.ReplaceAll(column, "\"", "")
|
|
|
|
|
|
column = strings.ReplaceAll(column, "`", "")
|
2024-04-09 14:05:12 +00:00
|
|
|
|
if len(template.JoinTemplate) > 0 {
|
2024-05-31 02:45:39 +00:00
|
|
|
|
columnAs := strings.Split(column, " as ")
|
|
|
|
|
|
if len(columnAs) > 1 {
|
|
|
|
|
|
column = strings.TrimSpace(strings.Split(column, " as ")[1])
|
|
|
|
|
|
} else {
|
|
|
|
|
|
columnArr := strings.Split(column, ".")
|
|
|
|
|
|
if len(columnArr) > 1 {
|
|
|
|
|
|
column = strings.Split(column, ".")[1]
|
|
|
|
|
|
}
|
2024-04-09 14:05:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-07-10 09:52:53 +00:00
|
|
|
|
// 需要对时间类型特殊处理
|
|
|
|
|
|
if t, ok := exTable[column].(time.Time); ok {
|
|
|
|
|
|
row = append(row, t.Format("2006-01-02 15:04:05"))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
row = append(row, fmt.Sprintf("%v", exTable[column]))
|
|
|
|
|
|
}
|
2023-12-30 11:57:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
rows = append(rows, row)
|
|
|
|
|
|
}
|
|
|
|
|
|
for i, row := range rows {
|
|
|
|
|
|
for j, colCell := range row {
|
2025-06-05 03:29:35 +00:00
|
|
|
|
cell := fmt.Sprintf("%s%d", getColumnName(j+1), i+1)
|
|
|
|
|
|
|
2025-11-10 03:05:03 +00:00
|
|
|
|
var sErr error
|
|
|
|
|
|
if v, err := strconv.ParseFloat(colCell, 64); err == nil {
|
|
|
|
|
|
sErr = f.SetCellValue("Sheet1", cell, v)
|
|
|
|
|
|
} else if v, err := strconv.ParseInt(colCell, 10, 64); err == nil {
|
|
|
|
|
|
sErr = f.SetCellValue("Sheet1", cell, v)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sErr = f.SetCellValue("Sheet1", cell, colCell)
|
|
|
|
|
|
}
|
2025-06-05 03:29:35 +00:00
|
|
|
|
|
2024-07-10 09:52:53 +00:00
|
|
|
|
if sErr != nil {
|
|
|
|
|
|
return nil, "", sErr
|
2023-12-30 11:57:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
f.SetActiveSheet(index)
|
|
|
|
|
|
file, err = f.WriteToBuffer()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return file, template.Name, nil
|
|
|
|
|
|
}
|
2024-01-05 15:09:07 +00:00
|
|
|
|
|
2025-11-10 03:05:03 +00:00
|
|
|
|
// PreviewSQL 预览最终生成的 SQL(不执行查询,仅返回 SQL 字符串)
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax) & [trae-ai]
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) PreviewSQL(templateID string, values url.Values) (sqlPreview string, err error) {
|
2026-01-11 06:41:19 +00:00
|
|
|
|
// 解析 params(与导出逻辑保持一致)
|
|
|
|
|
|
var params = values.Get("params")
|
|
|
|
|
|
paramsValues, _ := url.ParseQuery(params)
|
|
|
|
|
|
|
|
|
|
|
|
// 加载模板
|
|
|
|
|
|
var template system.SysExportTemplate
|
|
|
|
|
|
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析模板列
|
|
|
|
|
|
var templateInfoMap = make(map[string]string)
|
|
|
|
|
|
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
var selectKeyFmt []string
|
|
|
|
|
|
for _, key := range columns {
|
|
|
|
|
|
selectKeyFmt = append(selectKeyFmt, key)
|
|
|
|
|
|
}
|
|
|
|
|
|
selects := strings.Join(selectKeyFmt, ", ")
|
|
|
|
|
|
|
|
|
|
|
|
// 生成 FROM 与 JOIN 片段
|
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
|
sb.WriteString("SELECT ")
|
|
|
|
|
|
sb.WriteString(selects)
|
|
|
|
|
|
sb.WriteString(" FROM ")
|
|
|
|
|
|
sb.WriteString(template.TableName)
|
|
|
|
|
|
|
|
|
|
|
|
if len(template.JoinTemplate) > 0 {
|
|
|
|
|
|
for _, join := range template.JoinTemplate {
|
|
|
|
|
|
sb.WriteString(" ")
|
|
|
|
|
|
sb.WriteString(join.JOINS)
|
|
|
|
|
|
sb.WriteString(" ")
|
|
|
|
|
|
sb.WriteString(join.Table)
|
|
|
|
|
|
sb.WriteString(" ON ")
|
|
|
|
|
|
sb.WriteString(join.ON)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WHERE 条件
|
|
|
|
|
|
var wheres []string
|
|
|
|
|
|
|
|
|
|
|
|
// 软删除过滤
|
|
|
|
|
|
filterDeleted := false
|
|
|
|
|
|
if paramsValues != nil {
|
|
|
|
|
|
filterParam := paramsValues.Get("filterDeleted")
|
|
|
|
|
|
if filterParam == "true" {
|
|
|
|
|
|
filterDeleted = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if filterDeleted {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", template.TableName))
|
|
|
|
|
|
if len(template.JoinTemplate) > 0 {
|
|
|
|
|
|
for _, join := range template.JoinTemplate {
|
|
|
|
|
|
if sysExportTemplateService.hasDeletedAtColumn(join.Table) {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s.deleted_at IS NULL", join.Table))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 模板条件(保留与 ExportExcel 同步的解析规则)
|
|
|
|
|
|
if len(template.Conditions) > 0 {
|
|
|
|
|
|
for _, condition := range template.Conditions {
|
|
|
|
|
|
op := strings.ToUpper(strings.TrimSpace(condition.Operator))
|
|
|
|
|
|
col := strings.TrimSpace(condition.Column)
|
|
|
|
|
|
|
|
|
|
|
|
// 预览优先展示传入值,没有则展示占位符
|
|
|
|
|
|
val := ""
|
|
|
|
|
|
if paramsValues != nil {
|
|
|
|
|
|
val = paramsValues.Get(condition.From)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch op {
|
|
|
|
|
|
case "BETWEEN":
|
|
|
|
|
|
startValue := ""
|
|
|
|
|
|
endValue := ""
|
|
|
|
|
|
if paramsValues != nil {
|
|
|
|
|
|
startValue = paramsValues.Get("start" + condition.From)
|
|
|
|
|
|
endValue = paramsValues.Get("end" + condition.From)
|
|
|
|
|
|
}
|
|
|
|
|
|
if startValue != "" && endValue != "" {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s BETWEEN '%s' AND '%s'", col, startValue, endValue))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s BETWEEN {start%s} AND {end%s}", col, condition.From, condition.From))
|
|
|
|
|
|
}
|
|
|
|
|
|
case "IN", "NOT IN":
|
|
|
|
|
|
if val != "" {
|
|
|
|
|
|
// 逗号分隔值做简单展示
|
|
|
|
|
|
parts := strings.Split(val, ",")
|
|
|
|
|
|
for i := range parts {
|
|
|
|
|
|
parts[i] = strings.TrimSpace(parts[i])
|
|
|
|
|
|
}
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s %s ('%s')", col, op, strings.Join(parts, "','")))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s %s ({%s})", col, op, condition.From))
|
|
|
|
|
|
}
|
|
|
|
|
|
case "LIKE":
|
|
|
|
|
|
if val != "" {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s LIKE '%%%s%%'", col, val))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s LIKE {%%%s%%}", col, condition.From))
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
if val != "" {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s %s '%s'", col, op, val))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
wheres = append(wheres, fmt.Sprintf("%s %s {%s}", col, op, condition.From))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(wheres) > 0 {
|
|
|
|
|
|
sb.WriteString(" WHERE ")
|
|
|
|
|
|
sb.WriteString(strings.Join(wheres, " AND "))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 排序
|
|
|
|
|
|
order := ""
|
|
|
|
|
|
if paramsValues != nil {
|
|
|
|
|
|
order = paramsValues.Get("order")
|
|
|
|
|
|
}
|
|
|
|
|
|
if order == "" && template.Order != "" {
|
|
|
|
|
|
order = template.Order
|
|
|
|
|
|
}
|
|
|
|
|
|
if order != "" {
|
|
|
|
|
|
sb.WriteString(" ORDER BY ")
|
|
|
|
|
|
sb.WriteString(order)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// limit/offset(如果传入或默认值为0,则不生成)
|
|
|
|
|
|
limitStr := ""
|
|
|
|
|
|
offsetStr := ""
|
|
|
|
|
|
if paramsValues != nil {
|
|
|
|
|
|
limitStr = paramsValues.Get("limit")
|
|
|
|
|
|
offsetStr = paramsValues.Get("offset")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理模板默认limit(仅当非0时)
|
|
|
|
|
|
if limitStr == "" && template.Limit != nil && *template.Limit != 0 {
|
|
|
|
|
|
limitStr = strconv.Itoa(*template.Limit)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析为数值,用于判断是否生成
|
|
|
|
|
|
limitInt := 0
|
|
|
|
|
|
offsetInt := 0
|
|
|
|
|
|
if limitStr != "" {
|
|
|
|
|
|
if v, e := strconv.Atoi(limitStr); e == nil {
|
|
|
|
|
|
limitInt = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if offsetStr != "" {
|
|
|
|
|
|
if v, e := strconv.Atoi(offsetStr); e == nil {
|
|
|
|
|
|
offsetInt = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if limitInt > 0 {
|
|
|
|
|
|
sb.WriteString(" LIMIT ")
|
|
|
|
|
|
sb.WriteString(strconv.Itoa(limitInt))
|
|
|
|
|
|
if offsetInt > 0 {
|
|
|
|
|
|
sb.WriteString(" OFFSET ")
|
|
|
|
|
|
sb.WriteString(strconv.Itoa(offsetInt))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 当limit未设置或为0时,仅当offset>0才生成OFFSET
|
|
|
|
|
|
if offsetInt > 0 {
|
|
|
|
|
|
sb.WriteString(" OFFSET ")
|
|
|
|
|
|
sb.WriteString(strconv.Itoa(offsetInt))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sb.String(), nil
|
2025-11-10 03:05:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-05 15:09:07 +00:00
|
|
|
|
// ExportTemplate 导出Excel模板
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templateID string) (file *bytes.Buffer, name string, err error) {
|
|
|
|
|
|
var template system.SysExportTemplate
|
|
|
|
|
|
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
f := excelize.NewFile()
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
// Create a new sheet.
|
|
|
|
|
|
index, err := f.NewSheet("Sheet1")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
var templateInfoMap = make(map[string]string)
|
2024-05-06 13:10:01 +00:00
|
|
|
|
|
|
|
|
|
|
columns, err := utils.GetJSONKeys(template.TemplateInfo)
|
|
|
|
|
|
|
2024-01-05 15:09:07 +00:00
|
|
|
|
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
var tableTitle []string
|
2024-05-06 13:10:01 +00:00
|
|
|
|
for _, key := range columns {
|
2024-01-05 15:09:07 +00:00
|
|
|
|
tableTitle = append(tableTitle, templateInfoMap[key])
|
|
|
|
|
|
}
|
2024-05-06 13:10:01 +00:00
|
|
|
|
|
2024-01-06 08:16:50 +00:00
|
|
|
|
for i := range tableTitle {
|
2024-02-20 07:06:17 +00:00
|
|
|
|
fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i])
|
2024-01-06 08:16:50 +00:00
|
|
|
|
if fErr != nil {
|
|
|
|
|
|
return nil, "", fErr
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-01-05 15:09:07 +00:00
|
|
|
|
f.SetActiveSheet(index)
|
|
|
|
|
|
file, err = f.WriteToBuffer()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return file, template.Name, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-18 03:40:03 +00:00
|
|
|
|
// 辅助函数:检查表是否有deleted_at列
|
|
|
|
|
|
func (s *SysExportTemplateService) hasDeletedAtColumn(tableName string) bool {
|
|
|
|
|
|
var count int64
|
|
|
|
|
|
global.GVA_DB.Raw("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = 'deleted_at'", tableName).Count(&count)
|
|
|
|
|
|
return count > 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-05 15:09:07 +00:00
|
|
|
|
// ImportExcel 导入Excel
|
|
|
|
|
|
// Author [piexlmax](https://github.com/piexlmax)
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID string, file *multipart.FileHeader) (err error) {
|
|
|
|
|
|
var template system.SysExportTemplate
|
|
|
|
|
|
err = global.GVA_DB.First(&template, "template_id = ?", templateID).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
src, err := file.Open()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
defer src.Close()
|
|
|
|
|
|
|
|
|
|
|
|
f, err := excelize.OpenReader(src)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rows, err := f.GetRows("Sheet1")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2025-04-29 05:21:22 +00:00
|
|
|
|
if len(rows) < 2 {
|
|
|
|
|
|
return errors.New("Excel data is not enough.\nIt should contain title row and data")
|
|
|
|
|
|
}
|
2024-01-05 15:09:07 +00:00
|
|
|
|
|
|
|
|
|
|
var templateInfoMap = make(map[string]string)
|
|
|
|
|
|
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-15 15:22:20 +00:00
|
|
|
|
db := global.GVA_DB
|
|
|
|
|
|
if template.DBName != "" {
|
|
|
|
|
|
db = global.MustGetGlobalDBByDBName(template.DBName)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
items, err := sysExportTemplateService.parseExcelToMap(rows, templateInfoMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-15 15:22:20 +00:00
|
|
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if template.ImportSQL != "" {
|
|
|
|
|
|
return sysExportTemplateService.importBySQL(tx, template.ImportSQL, items)
|
|
|
|
|
|
}
|
|
|
|
|
|
return sysExportTemplateService.importByGORM(tx, template.TableName, items)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2024-03-30 12:54:34 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) parseExcelToMap(rows [][]string, templateInfoMap map[string]string) ([]map[string]interface{}, error) {
|
|
|
|
|
|
var titleKeyMap = make(map[string]string)
|
|
|
|
|
|
for key, title := range templateInfoMap {
|
|
|
|
|
|
titleKeyMap[title] = key
|
|
|
|
|
|
}
|
2024-05-17 02:26:47 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
excelTitle := rows[0]
|
|
|
|
|
|
for i, str := range excelTitle {
|
|
|
|
|
|
excelTitle[i] = strings.TrimSpace(str)
|
|
|
|
|
|
}
|
|
|
|
|
|
values := rows[1:]
|
|
|
|
|
|
items := make([]map[string]interface{}, 0, len(values))
|
|
|
|
|
|
for _, row := range values {
|
|
|
|
|
|
var item = make(map[string]interface{})
|
|
|
|
|
|
for ii, value := range row {
|
|
|
|
|
|
if ii >= len(excelTitle) {
|
|
|
|
|
|
continue
|
2024-05-17 02:26:47 +00:00
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
if _, ok := titleKeyMap[excelTitle[ii]]; !ok {
|
|
|
|
|
|
continue // excel中多余的标题,在模板信息中没有对应的字段,因此key为空,必须跳过
|
2024-05-17 02:26:47 +00:00
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
key := titleKeyMap[excelTitle[ii]]
|
|
|
|
|
|
item[key] = value
|
|
|
|
|
|
}
|
|
|
|
|
|
items = append(items, item)
|
|
|
|
|
|
}
|
|
|
|
|
|
return items, nil
|
|
|
|
|
|
}
|
2024-03-15 06:55:20 +00:00
|
|
|
|
|
2026-01-11 06:41:19 +00:00
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) importBySQL(tx *gorm.DB, sql string, items []map[string]interface{}) error {
|
|
|
|
|
|
for _, item := range items {
|
|
|
|
|
|
if err := tx.Exec(sql, item).Error; err != nil {
|
|
|
|
|
|
return err
|
2024-01-05 15:09:07 +00:00
|
|
|
|
}
|
2026-01-11 06:41:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (sysExportTemplateService *SysExportTemplateService) importByGORM(tx *gorm.DB, tableName string, items []map[string]interface{}) error {
|
|
|
|
|
|
needCreated := tx.Migrator().HasColumn(tableName, "created_at")
|
|
|
|
|
|
needUpdated := tx.Migrator().HasColumn(tableName, "updated_at")
|
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items {
|
|
|
|
|
|
if item["created_at"] == nil && needCreated {
|
|
|
|
|
|
item["created_at"] = time.Now()
|
|
|
|
|
|
}
|
|
|
|
|
|
if item["updated_at"] == nil && needUpdated {
|
|
|
|
|
|
item["updated_at"] = time.Now()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return tx.Table(tableName).CreateInBatches(&items, 1000).Error
|
2024-01-05 15:09:07 +00:00
|
|
|
|
}
|
2024-01-25 11:41:52 +00:00
|
|
|
|
|
|
|
|
|
|
func getColumnName(n int) string {
|
|
|
|
|
|
columnName := ""
|
|
|
|
|
|
for n > 0 {
|
|
|
|
|
|
n--
|
|
|
|
|
|
columnName = string(rune('A'+n%26)) + columnName
|
|
|
|
|
|
n /= 26
|
|
|
|
|
|
}
|
|
|
|
|
|
return columnName
|
|
|
|
|
|
}
|