🎉 Initial commit
This commit is contained in:
177
util/database.go
Normal file
177
util/database.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log" // 添加 log 包
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/landaiqing/Go-TriTabula/entity"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// DBConfig 数据库配置结构
|
||||
type DBConfig struct {
|
||||
Driver string `json:"driver"`
|
||||
URL string `json:"url"`
|
||||
Database string `json:"database"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
MaxOpenConns int `json:"maxOpenConns"`
|
||||
MaxIdleConns int `json:"maxIdleConns"`
|
||||
}
|
||||
|
||||
// LoadDBConfig 从配置文件加载数据库配置
|
||||
func LoadDBConfig(configPath string) (*DBConfig, error) {
|
||||
absPath, err := filepath.Abs(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取配置文件绝对路径失败: %v", err)
|
||||
}
|
||||
|
||||
file, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开配置文件失败: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
byteValue, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
var config DBConfig
|
||||
if err := json.Unmarshal(byteValue, &config); err != nil {
|
||||
return nil, fmt.Errorf("解析配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// GetDatabaseConnection 获取数据库连接
|
||||
func GetDatabaseConnection(config *DBConfig) (*sql.DB, error) {
|
||||
// 配置MySQL连接
|
||||
mysqlConfig := mysql.Config{
|
||||
User: config.Username,
|
||||
Passwd: config.Password,
|
||||
Net: "tcp",
|
||||
Addr: config.URL,
|
||||
DBName: config.Database,
|
||||
AllowNativePasswords: true,
|
||||
ParseTime: true,
|
||||
}
|
||||
|
||||
// 打开数据库连接
|
||||
db, err := sql.Open("mysql", mysqlConfig.FormatDSN())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接数据库失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
db.SetMaxOpenConns(config.MaxOpenConns)
|
||||
db.SetMaxIdleConns(config.MaxIdleConns)
|
||||
|
||||
// 测试连接
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, fmt.Errorf("测试数据库连接失败: %v", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// GetTableDetails 获取数据库表结构详情
|
||||
func GetTableDetails(db *sql.DB, databaseName string) ([]entity.Result, error) {
|
||||
query := `
|
||||
SELECT table_schema, table_name, column_name, column_type, column_key,
|
||||
is_nullable, column_default, column_comment, character_set_name, EXTRA
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = ?
|
||||
ORDER BY table_name, ORDINAL_POSITION
|
||||
`
|
||||
|
||||
// 执行查询
|
||||
rows, err := db.Query(query, databaseName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询表结构失败: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// 用于存储结果的映射和列表
|
||||
tableMap := make(map[string]int) // Map tableName to index in results slice
|
||||
var results []entity.Result
|
||||
|
||||
// 遍历查询结果
|
||||
for rows.Next() {
|
||||
var tableSchema, tableName, columnName, columnType, columnKey string
|
||||
var isNullable, columnDefault, columnComment, characterSetName, extra sql.NullString
|
||||
|
||||
// 扫描行数据
|
||||
err := rows.Scan(
|
||||
&tableSchema, &tableName, &columnName, &columnType, &columnKey,
|
||||
&isNullable, &columnDefault, &columnComment, &characterSetName, &extra,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("扫描行数据失败: %v", err)
|
||||
}
|
||||
log.Printf("读取到行: Table=%s, Column=%s\n", tableName, columnName) // 添加行读取日志
|
||||
|
||||
// 处理表信息
|
||||
idx, exists := tableMap[tableName]
|
||||
if !exists {
|
||||
// 创建新的表结果并添加到 results slice
|
||||
newResult := entity.Result{
|
||||
TableSchema: tableSchema,
|
||||
TableName: tableName,
|
||||
TableDetails: []entity.TableDetail{},
|
||||
}
|
||||
results = append(results, newResult)
|
||||
idx = len(results) - 1 // Get the index of the newly added result
|
||||
tableMap[tableName] = idx // Store the index in the map
|
||||
}
|
||||
|
||||
// 处理列信息
|
||||
columnDefaultValue := "无"
|
||||
if columnDefault.Valid {
|
||||
columnDefaultValue = columnDefault.String
|
||||
}
|
||||
|
||||
columnKeyValue := "无"
|
||||
if columnKey != "" {
|
||||
columnKeyValue = columnKey
|
||||
}
|
||||
|
||||
columnCommentValue := ""
|
||||
if columnComment.Valid {
|
||||
columnCommentValue = columnComment.String
|
||||
}
|
||||
|
||||
isNullableValue := "NO"
|
||||
if isNullable.Valid {
|
||||
isNullableValue = isNullable.String
|
||||
}
|
||||
|
||||
// 创建列详情
|
||||
tableDetail := entity.TableDetail{
|
||||
ColumnName: columnName,
|
||||
ColumnType: columnType,
|
||||
ColumnKey: columnKeyValue,
|
||||
IsNullable: isNullableValue,
|
||||
ColumnDefault: columnDefaultValue,
|
||||
ColumnComment: columnCommentValue,
|
||||
}
|
||||
|
||||
// 添加到表的列列表 (直接修改 results 切片中的元素)
|
||||
results[idx].TableDetails = append(results[idx].TableDetails, tableDetail)
|
||||
}
|
||||
|
||||
// 检查是否有错误发生
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("遍历结果集时发生错误: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("GetTableDetails 完成, 共处理 %d 个表的结果。\n", len(results)) // 添加最终结果日志
|
||||
return results, nil
|
||||
}
|
162
util/export_word.go
Normal file
162
util/export_word.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/landaiqing/Go-TriTabula/entity"
|
||||
|
||||
"github.com/landaiqing/go-dockit/document"
|
||||
)
|
||||
|
||||
// ExportWord 用于导出Word文档的工具结构体
|
||||
type ExportWord struct{}
|
||||
|
||||
// CreateDocument 创建一个新的Word文档
|
||||
func (ew *ExportWord) CreateDocument(results []entity.Result) *document.Document {
|
||||
// 创建新文档
|
||||
doc := document.NewDocument()
|
||||
|
||||
// 设置文档属性
|
||||
doc.SetTitle("数据库三线表")
|
||||
doc.SetCreator("Go-TriTabula")
|
||||
doc.SetDescription("使用go-dockit库创建数据库三线表")
|
||||
|
||||
// 为每个表结果创建表格和标题
|
||||
for _, result := range results {
|
||||
// 创建表名标题段落
|
||||
tableTitlePara := doc.AddParagraph()
|
||||
tableTitlePara.SetAlignment("center")
|
||||
tableTitlePara.SetSpacingAfter(0)
|
||||
tableTitlePara.SetSpacingBefore(0)
|
||||
tableTitlePara.SetLineSpacing(1.5, "auto") // 设置1.5倍行距
|
||||
tableTitleRun := tableTitlePara.AddRun()
|
||||
tableTitleRun.AddText(result.TableName)
|
||||
tableTitleRun.SetBold(true)
|
||||
tableTitleRun.SetFontSize(21) // 五号字体约为10.5磅(21)
|
||||
tableTitleRun.SetFontFamily("宋体")
|
||||
|
||||
// 获取字段映射
|
||||
fieldMapping := entity.GetFieldMapping()
|
||||
fieldNames := []string{"ColumnName", "ColumnType", "IsNullable", "ColumnKey", "ColumnDefault", "ColumnComment"}
|
||||
|
||||
// 创建表格
|
||||
table := doc.AddTable(len(result.TableDetails)+1, len(fieldNames))
|
||||
table.SetWidth("100%", "pct") // 与文字齐宽
|
||||
table.SetAlignment("center")
|
||||
|
||||
// 填充表头
|
||||
for i, fieldName := range fieldNames {
|
||||
cellPara := table.Rows[0].Cells[i].AddParagraph()
|
||||
cellPara.SetAlignment("center")
|
||||
cellPara.SetLineSpacing(1.5, "auto") // 1.5倍行距
|
||||
cellRun := cellPara.AddRun()
|
||||
cellRun.AddText(fieldMapping[fieldName])
|
||||
cellRun.SetBold(false)
|
||||
cellRun.SetFontSize(21) // 五号字体
|
||||
cellRun.SetFontFamily("宋体")
|
||||
}
|
||||
|
||||
// 填充数据行
|
||||
for i, detail := range result.TableDetails {
|
||||
// 字段名
|
||||
para := table.Rows[i+1].Cells[0].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto") // 1.5倍行距
|
||||
cellRun := para.AddRun()
|
||||
cellRun.AddText(detail.ColumnName)
|
||||
cellRun.SetFontSize(21) // 五号字体
|
||||
cellRun.SetFontFamily("Times New Roman")
|
||||
|
||||
// 类型
|
||||
para = table.Rows[i+1].Cells[1].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto")
|
||||
cellRun = para.AddRun()
|
||||
cellRun.AddText(detail.ColumnType)
|
||||
cellRun.SetFontSize(21)
|
||||
cellRun.SetFontFamily("Times New Roman") // 英文使用Times New Roman
|
||||
|
||||
// 是否为空
|
||||
para = table.Rows[i+1].Cells[2].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto")
|
||||
cellRun = para.AddRun()
|
||||
// 将NO和YES转换为更易读的否和是
|
||||
isNullableText := "否"
|
||||
if detail.IsNullable == "YES" {
|
||||
isNullableText = "是"
|
||||
}
|
||||
cellRun.AddText(isNullableText)
|
||||
cellRun.SetFontSize(21)
|
||||
cellRun.SetFontFamily("宋体")
|
||||
|
||||
// 索引
|
||||
para = table.Rows[i+1].Cells[3].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto")
|
||||
cellRun = para.AddRun()
|
||||
cellRun.AddText(detail.ColumnKey)
|
||||
cellRun.SetFontSize(21)
|
||||
cellRun.SetFontFamily("宋体")
|
||||
|
||||
// 默认值
|
||||
para = table.Rows[i+1].Cells[4].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto")
|
||||
cellRun = para.AddRun()
|
||||
// 将"无"替换为"NULL",使其更符合数据库术语
|
||||
defaultValue := "NULL"
|
||||
if detail.ColumnDefault != "无" {
|
||||
defaultValue = detail.ColumnDefault
|
||||
}
|
||||
cellRun.AddText(defaultValue)
|
||||
cellRun.SetFontSize(21)
|
||||
cellRun.SetFontFamily("宋体")
|
||||
|
||||
// 说明
|
||||
para = table.Rows[i+1].Cells[5].AddParagraph()
|
||||
para.SetAlignment("center")
|
||||
para.SetLineSpacing(1.5, "auto")
|
||||
cellRun = para.AddRun()
|
||||
cellRun.AddText(detail.ColumnComment)
|
||||
cellRun.SetFontSize(21)
|
||||
cellRun.SetFontFamily("宋体")
|
||||
}
|
||||
|
||||
// 设置三线表样式
|
||||
// 1. 首先清除所有默认边框
|
||||
table.SetBorders("all", "", 0, "")
|
||||
// 2. 顶线(表格顶部边框),1.5磅
|
||||
table.SetBorders("top", "single", 10, "000000")
|
||||
// 3. 表头分隔线(第一行底部边框),1磅
|
||||
for i := 0; i < len(fieldNames); i++ {
|
||||
table.Rows[0].Cells[i].SetBorders("bottom", "single", 4, "000000")
|
||||
}
|
||||
// 4. 底线(表格底部边框),1.5磅
|
||||
table.SetBorders("bottom", "single", 10, "000000")
|
||||
// 5. 显式设置内部边框为"none"
|
||||
table.SetBorders("insideH", "none", 0, "000000")
|
||||
table.SetBorders("insideV", "none", 0, "000000")
|
||||
|
||||
// 添加空行
|
||||
doc.AddParagraph()
|
||||
}
|
||||
|
||||
// 添加页脚(页码)
|
||||
footer := doc.AddFooterWithReference("default")
|
||||
footer.AddPageNumber()
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// ExportToFile 将文档导出到文件
|
||||
func (ew *ExportWord) ExportToFile(doc *document.Document, filePath string) error {
|
||||
err := doc.Save(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存文档失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("文档已成功保存到: %s", filePath)
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user