11 示例项目 - ZeroHawkeye/wordZero GitHub Wiki

WordZero 示例项目

本文档提供 WordZero 库的完整示例项目,展示实际应用场景和最佳实践。

📋 目录

基础示例

1. 简单文档生成器

创建一个简单的文档生成器,展示基本功能的使用。

package main

import (
    "fmt"
    "log"
    
    "github.com/ZeroHawkeye/wordZero/pkg/document"
    "github.com/ZeroHawkeye/wordZero/pkg/style"
)

func main() {
    // 创建新文档
    doc := document.New()
    
    // 添加标题
    title := doc.AddParagraph("我的第一个Word文档")
    title.SetStyle(style.StyleTitle)
    
    // 添加副标题
    subtitle := doc.AddParagraph("使用WordZero创建")
    subtitle.SetStyle(style.StyleSubtitle)
    
    // 添加正文
    content := doc.AddParagraph("这是一个使用WordZero库创建的简单文档示例。")
    content.SetStyle(style.StyleNormal)
    
    // 保存文档
    err := doc.Save("my_first_document.docx")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("文档创建成功!")
}

2. 格式化文本示例

展示各种文本格式化选项。

package main

import (
    "github.com/ZeroHawkeye/wordZero/pkg/document"
)

func main() {
    doc := document.New()
    
    // 创建不同格式的文本
    formats := []*document.TextFormat{
        {FontName: "Arial", FontSize: 12, Bold: true},
        {FontName: "Times New Roman", FontSize: 14, Italic: true},
        {FontName: "Courier New", FontSize: 10, FontColor: "FF0000"},
        {FontName: "Calibri", FontSize: 16, Bold: true, Italic: true},
    }
    
    texts := []string{
        "这是粗体文本",
        "这是斜体文本", 
        "这是红色文本",
        "这是粗体斜体文本",
    }
    
    for i, text := range texts {
        para := doc.AddFormattedParagraph(text, formats[i])
        para.SetAlignment(document.AlignCenter)
    }
    
    doc.Save("formatted_text_example.docx")
}

实际应用场景

1. 报告生成器

创建一个完整的报告生成器,包含目录、表格、图表等。

package main

import (
	"fmt"
	"time"

	"github.com/ZeroHawkeye/wordZero/pkg/document"
	"github.com/ZeroHawkeye/wordZero/pkg/style"
)

type ReportData struct {
	Title     string
	Author    string
	Date      time.Time
	Summary   string
	Sections  []Section
	TableData [][]string
}

type Section struct {
	Title   string
	Content string
	Level   int
}

func GenerateReport(data ReportData) error {
	doc := document.New()

	// 设置文档属性
	doc.SetTitle(data.Title)
	doc.SetAuthor(data.Author)

	// 添加封面
	addCoverPage(doc, data)

	// 生成目录
	generateTOC(doc)

	// 添加摘要
	addSummary(doc, data.Summary)

	// 添加各个章节
	for _, section := range data.Sections {
		addSection(doc, section)
	}

	// 添加数据表格
	addDataTable(doc, data.TableData)

	// 添加页眉页脚
	addHeaderFooter(doc, data.Title)

	// 保存文档
	filename := fmt.Sprintf("report_%s.docx",
		time.Now().Format("20060102_150405"))
	return doc.Save(filename)
}

func addCoverPage(doc *document.Document, data ReportData) {
	// 标题
	title := doc.AddParagraph(data.Title)
	title.SetStyle(style.StyleTitle)
	title.SetAlignment(document.AlignCenter)

	// 作者和日期
	author := doc.AddParagraph(fmt.Sprintf("作者:%s", data.Author))
	author.SetAlignment(document.AlignCenter)

	date := doc.AddParagraph(fmt.Sprintf("日期:%s",
		data.Date.Format("2006年01月02日")))
	date.SetAlignment(document.AlignCenter)
}

func generateTOC(doc *document.Document) {
	config := &document.TOCConfig{
		Title:       "目录",
		MaxLevel:    3,
		ShowPageNum: true,
	}
	doc.GenerateTOC(config)
}

func addSummary(doc *document.Document, summary string) {
	heading := doc.AddParagraph("摘要")
	heading.SetStyle(style.StyleHeading1)

	content := doc.AddParagraph(summary)
	content.SetStyle(style.StyleNormal)
}

func addSection(doc *document.Document, section Section) {
	var styleID string
	switch section.Level {
	case 1:
		styleID = style.StyleHeading1
	case 2:
		styleID = style.StyleHeading2
	case 3:
		styleID = style.StyleHeading3
	default:
		styleID = style.StyleHeading1
	}

	heading := doc.AddParagraph(section.Title)
	heading.SetStyle(styleID)

	content := doc.AddParagraph(section.Content)
	content.SetStyle(style.StyleNormal)
}

func addDataTable(doc *document.Document, data [][]string) {
	if len(data) == 0 {
		return
	}

	heading := doc.AddParagraph("数据表格")
	heading.SetStyle(style.StyleHeading1)

	config := &document.TableConfig{
		Rows:  len(data),
		Cols:  len(data[0]),
		Width: 8000, // 设置表格宽度为8000磅(约11英寸)
		Data:  data,
	}

	table := doc.AddTable(config)
	if table != nil {
		// 应用表格样式
		styleConfig := &document.TableStyleConfig{
			Template:       document.TableStyleTemplateGrid,
			FirstRowHeader: true,
		}
		table.ApplyTableStyle(styleConfig)
	}
}

func addHeaderFooter(doc *document.Document, title string) {
	// 添加页眉
	err := doc.AddHeaderWithPageNumber(document.HeaderFooterTypeDefault, title, true)
	if err != nil {
		log.Printf("添加页眉失败: %v", err)
	}

	// 添加页脚
	err = doc.AddFooterWithPageNumber(document.HeaderFooterTypeDefault, "机密文档", true)
	if err != nil {
		log.Printf("添加页脚失败: %v", err)
	}
}

func main() {
	data := ReportData{
		Title:   "2024年度业务报告",
		Author:  "张三",
		Date:    time.Now(),
		Summary: "本报告总结了2024年度的业务发展情况...",
		Sections: []Section{
			{Title: "业务概况", Content: "业务发展良好...", Level: 1},
			{Title: "财务状况", Content: "财务状况稳定...", Level: 1},
			{Title: "市场分析", Content: "市场前景乐观...", Level: 2},
		},
		TableData: [][]string{
			{"月份", "收入", "支出", "利润"},
			{"1月", "100万", "80万", "20万"},
			{"2月", "120万", "90万", "30万"},
			{"3月", "110万", "85万", "25万"},
		},
	}

	err := GenerateReport(data)
	if err != nil {
		fmt.Printf("生成报告失败: %v\n", err)
	} else {
		fmt.Println("报告生成成功!")
	}
}

2. 合同生成器

创建一个合同文档生成器。

package main

import (
    "fmt"
    "time"
    
    "github.com/ZeroHawkeye/wordZero/pkg/document"
    "github.com/ZeroHawkeye/wordZero/pkg/style"
)

type ContractData struct {
    ContractNumber string
    PartyA         Party
    PartyB         Party
    Subject        string
    Amount         float64
    StartDate      time.Time
    EndDate        time.Time
    Terms          []string
    SignDate       time.Time
}

type Party struct {
    Name    string
    Address string
    Contact string
    Phone   string
}

func GenerateContract(data ContractData) error {
    doc := document.New()
    
    // 设置页面
    doc.SetPageMargins(2.5, 2.0, 2.5, 2.0) // 厘米
    
    // 合同标题
    title := doc.AddParagraph("服务合同")
    title.SetStyle(style.StyleTitle)
    title.SetAlignment(document.AlignCenter)
    
    // 合同编号
    contractNum := doc.AddParagraph(fmt.Sprintf("合同编号:%s", 
        data.ContractNumber))
    contractNum.SetAlignment(document.AlignCenter)
    
    // 甲乙双方信息
    addPartyInfo(doc, "甲方", data.PartyA)
    addPartyInfo(doc, "乙方", data.PartyB)
    
    // 合同正文
    addContractBody(doc, data)
    
    // 合同条款
    addContractTerms(doc, data.Terms)
    
    // 签署信息
    addSignatureSection(doc, data)
    
    filename := fmt.Sprintf("contract_%s.docx", data.ContractNumber)
    return doc.Save(filename)
}

func addPartyInfo(doc *document.Document, partyType string, party Party) {
    info := fmt.Sprintf(`%s:
名称:%s
地址:%s
联系人:%s
电话:%s`, partyType, party.Name, party.Address, party.Contact, party.Phone)
    
    para := doc.AddParagraph(info)
    para.SetStyle(style.StyleNormal)
}

func addContractBody(doc *document.Document, data ContractData) {
    heading := doc.AddParagraph("合同内容")
    heading.SetStyle(style.StyleHeading1)
    
    content := fmt.Sprintf(`根据《中华人民共和国合同法》及相关法律法规,甲乙双方在平等、自愿的基础上,就%s事宜达成如下协议:

合同金额:%.2f元
合同期限:%s 至 %s`,
        data.Subject,
        data.Amount,
        data.StartDate.Format("2006年01月02日"),
        data.EndDate.Format("2006年01月02日"))
    
    para := doc.AddParagraph(content)
    para.SetStyle(style.StyleNormal)
}

func addContractTerms(doc *document.Document, terms []string) {
    heading := doc.AddParagraph("合同条款")
    heading.SetStyle(style.StyleHeading1)
    
    for i, term := range terms {
        termText := fmt.Sprintf("%d. %s", i+1, term)
        para := doc.AddParagraph(termText)
        para.SetStyle(style.StyleNormal)
        para.SetIndentation(0.5, 0, 0) // 首行缩进
    }
}

func addSignatureSection(doc *document.Document, data ContractData) {
    heading := doc.AddParagraph("签署")
    heading.SetStyle(style.StyleHeading1)
    
    signText := fmt.Sprintf(`甲方签字:________________    日期:%s

乙方签字:________________    日期:%s

本合同一式两份,甲乙双方各执一份,具有同等法律效力。`,
        data.SignDate.Format("2006年01月02日"),
        data.SignDate.Format("2006年01月02日"))
    
    para := doc.AddParagraph(signText)
    para.SetStyle(style.StyleNormal)
}

func main() {
    data := ContractData{
        ContractNumber: "HT2024001",
        PartyA: Party{
            Name:    "ABC科技有限公司",
            Address: "北京市朝阳区xxx路xxx号",
            Contact: "李经理",
            Phone:   "010-12345678",
        },
        PartyB: Party{
            Name:    "XYZ软件开发公司",
            Address: "上海市浦东新区xxx路xxx号",
            Contact: "王工程师",
            Phone:   "021-87654321",
        },
        Subject:   "软件开发服务",
        Amount:    500000.00,
        StartDate: time.Now(),
        EndDate:   time.Now().AddDate(0, 6, 0),
        Terms: []string{
            "甲方负责提供详细的需求文档",
            "乙方负责按时交付高质量的软件产品",
            "项目分三个阶段进行,每个阶段完成后进行验收",
            "如有争议,双方应友好协商解决",
        },
        SignDate: time.Now(),
    }
    
    err := GenerateContract(data)
    if err != nil {
        fmt.Printf("生成合同失败: %v\n", err)
    } else {
        fmt.Println("合同生成成功!")
    }
}

完整项目模板

文档生成服务

创建一个完整的文档生成微服务。

// main.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"
    
    "github.com/ZeroHawkeye/wordZero/pkg/document"
    "github.com/ZeroHawkeye/wordZero/pkg/style"
)

type DocumentService struct {
    // 移除模板系统,直接处理不同类型的请求
}

type DocumentRequest struct {
    Type string                 `json:"type"`     // 文档类型:report, contract, invoice
    Data map[string]interface{} `json:"data"`     // 文档数据
}

func NewDocumentService() *DocumentService {
    return &DocumentService{}
}

func (ds *DocumentService) GenerateDocument(w http.ResponseWriter, r *http.Request) {
    var req DocumentRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }
    
    var doc *document.Document
    var err error
    
    // 根据类型生成不同的文档
    switch req.Type {
    case "report":
        doc, err = ds.generateReport(req.Data)
    case "contract":
        doc, err = ds.generateContract(req.Data)
    case "invoice":
        doc, err = ds.generateInvoice(req.Data)
    default:
        http.Error(w, "Unknown document type", http.StatusBadRequest)
        return
    }
    
    if err != nil {
        http.Error(w, fmt.Sprintf("Generation error: %v", err), 
            http.StatusInternalServerError)
        return
    }
    
    // 保存到临时文件
    filename := fmt.Sprintf("temp_%d.docx", time.Now().Unix())
    if err := doc.Save(filename); err != nil {
        http.Error(w, "Failed to save document", 
            http.StatusInternalServerError)
        return
    }
    
    // 返回文件
    w.Header().Set("Content-Type", 
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
    w.Header().Set("Content-Disposition", 
        fmt.Sprintf("attachment; filename=%s", filename))
    
    http.ServeFile(w, r, filename)
}

func (ds *DocumentService) generateReport(data map[string]interface{}) (*document.Document, error) {
    doc := document.New()
    
    // 基本报告生成逻辑
    title, ok := data["title"].(string)
    if !ok {
        title = "默认报告"
    }
    
    titlePara := doc.AddParagraph(title)
    titlePara.SetStyle(style.StyleTitle)
    titlePara.SetAlignment(document.AlignCenter)
    
    content, ok := data["content"].(string)
    if !ok {
        content = "报告内容..."
    }
    
    contentPara := doc.AddParagraph(content)
    contentPara.SetStyle(style.StyleNormal)
    
    return doc, nil
}

func (ds *DocumentService) generateContract(data map[string]interface{}) (*document.Document, error) {
    doc := document.New()
    
    // 基本合同生成逻辑
    title := doc.AddParagraph("服务合同")
    title.SetStyle(style.StyleTitle)
    title.SetAlignment(document.AlignCenter)
    
    // 添加合同编号
    contractNum, ok := data["contractNumber"].(string)
    if !ok {
        contractNum = "HT2024XXX"
    }
    
    numPara := doc.AddParagraph(fmt.Sprintf("合同编号:%s", contractNum))
    numPara.SetAlignment(document.AlignCenter)
    
    return doc, nil
}

func (ds *DocumentService) generateInvoice(data map[string]interface{}) (*document.Document, error) {
    doc := document.New()
    
    // 基本发票生成逻辑
    title := doc.AddParagraph("发票")
    title.SetStyle(style.StyleTitle)
    title.SetAlignment(document.AlignCenter)
    
    // 添加发票信息
    invoiceNum, ok := data["invoiceNumber"].(string)
    if !ok {
        invoiceNum = "INV2024XXX"
    }
    
    numPara := doc.AddParagraph(fmt.Sprintf("发票编号:%s", invoiceNum))
    numPara.SetAlignment(document.AlignCenter)
    
    return doc, nil
}

func main() {
    service := NewDocumentService()
    
    http.HandleFunc("/generate", service.GenerateDocument)
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("OK"))
    })
    
    fmt.Println("文档生成服务启动在端口 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

性能优化示例

批量文档生成

package main

import (
    "fmt"
    "sync"
    "time"
    
    "github.com/ZeroHawkeye/wordZero/pkg/document"
    "github.com/ZeroHawkeye/wordZero/pkg/style"
)

type BatchProcessor struct {
    workerCount int
    jobs        chan DocumentJob
    results     chan DocumentResult
    wg          sync.WaitGroup
}

type DocumentJob struct {
    ID   int
    Data interface{}
}

type DocumentResult struct {
    ID       int
    Filename string
    Error    error
    Duration time.Duration
}

func NewBatchProcessor(workerCount int) *BatchProcessor {
    return &BatchProcessor{
        workerCount: workerCount,
        jobs:        make(chan DocumentJob, 100),
        results:     make(chan DocumentResult, 100),
    }
}

func (bp *BatchProcessor) Start() {
    for i := 0; i < bp.workerCount; i++ {
        bp.wg.Add(1)
        go bp.worker()
    }
}

func (bp *BatchProcessor) worker() {
    defer bp.wg.Done()
    
    for job := range bp.jobs {
        start := time.Now()
        filename, err := bp.processDocument(job)
        duration := time.Since(start)
        
        bp.results <- DocumentResult{
            ID:       job.ID,
            Filename: filename,
            Error:    err,
            Duration: duration,
        }
    }
}

func (bp *BatchProcessor) processDocument(job DocumentJob) (string, error) {
    doc := document.New()
    
    // 快速文档生成逻辑
    title := doc.AddParagraph(fmt.Sprintf("文档 #%d", job.ID))
    title.SetStyle(style.StyleTitle)
    
    content := doc.AddParagraph("这是批量生成的文档内容。")
    content.SetStyle(style.StyleNormal)
    
    filename := fmt.Sprintf("batch_doc_%d.docx", job.ID)
    err := doc.Save(filename)
    return filename, err
}

func (bp *BatchProcessor) AddJob(job DocumentJob) {
    bp.jobs <- job
}

func (bp *BatchProcessor) Close() {
    close(bp.jobs)
    bp.wg.Wait()
    close(bp.results)
}

func (bp *BatchProcessor) GetResults() <-chan DocumentResult {
    return bp.results
}

func main() {
    processor := NewBatchProcessor(4) // 4个工作协程
    processor.Start()
    
    // 添加100个任务
    go func() {
        for i := 1; i <= 100; i++ {
            processor.AddJob(DocumentJob{
                ID:   i,
                Data: fmt.Sprintf("Data for document %d", i),
            })
        }
        processor.Close()
    }()
    
    // 收集结果
    var totalDuration time.Duration
    successCount := 0
    
    for result := range processor.GetResults() {
        if result.Error != nil {
            fmt.Printf("文档 %d 生成失败: %v\n", result.ID, result.Error)
        } else {
            fmt.Printf("文档 %d 生成成功: %s (耗时: %v)\n", 
                result.ID, result.Filename, result.Duration)
            successCount++
        }
        totalDuration += result.Duration
    }
    
    fmt.Printf("\n批量处理完成:\n")
    fmt.Printf("成功: %d\n", successCount)
    fmt.Printf("总耗时: %v\n", totalDuration)
    fmt.Printf("平均耗时: %v\n", totalDuration/time.Duration(successCount))
}

错误处理示例

健壮的文档处理

package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "time"
    
    "github.com/ZeroHawkeye/wordZero/pkg/document"
)

type DocumentProcessor struct {
    logger *log.Logger
}

func NewDocumentProcessor() *DocumentProcessor {
    return &DocumentProcessor{
        logger: log.New(os.Stdout, "[DocProcessor] ", log.LstdFlags),
    }
}

func (dp *DocumentProcessor) ProcessWithRetry(data interface{}, maxRetries int) error {
    var lastErr error
    
    for attempt := 1; attempt <= maxRetries; attempt++ {
        err := dp.process(data)
        if err == nil {
            dp.logger.Printf("处理成功,尝试次数: %d", attempt)
            return nil
        }
        
        lastErr = err
        dp.logger.Printf("尝试 %d/%d 失败: %v", attempt, maxRetries, err)
        
        if attempt < maxRetries {
            // 指数退避
            time.Sleep(time.Duration(attempt) * time.Second)
        }
    }
    
    return fmt.Errorf("处理失败,已重试 %d 次: %w", maxRetries, lastErr)
}

func (dp *DocumentProcessor) process(data interface{}) error {
    // 验证输入
    if err := dp.validateInput(data); err != nil {
        return fmt.Errorf("输入验证失败: %w", err)
    }
    
    // 创建文档
    doc, err := dp.createDocument(data)
    if err != nil {
        return fmt.Errorf("创建文档失败: %w", err)
    }
    
    // 保存文档
    if err := dp.saveDocument(doc, "output.docx"); err != nil {
        return fmt.Errorf("保存文档失败: %w", err)
    }
    
    return nil
}

func (dp *DocumentProcessor) validateInput(data interface{}) error {
    if data == nil {
        return errors.New("数据不能为空")
    }
    
    // 更多验证逻辑...
    return nil
}

func (dp *DocumentProcessor) createDocument(data interface{}) (*document.Document, error) {
    defer func() {
        if r := recover(); r != nil {
            dp.logger.Printf("创建文档时发生panic: %v", r)
        }
    }()
    
    doc := document.New()
    
    // 添加内容的逻辑...
    para := doc.AddParagraph("测试内容")
    if para == nil {
        return nil, errors.New("添加段落失败")
    }
    
    return doc, nil
}

func (dp *DocumentProcessor) saveDocument(doc *document.Document, filename string) error {
    // 确保目录存在
    dir := filepath.Dir(filename)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return fmt.Errorf("创建目录失败: %w", err)
    }
    
    // 检查磁盘空间
    if err := dp.checkDiskSpace(filename); err != nil {
        return fmt.Errorf("磁盘空间检查失败: %w", err)
    }
    
    // 保存文档
    if err := doc.Save(filename); err != nil {
        return fmt.Errorf("保存失败: %w", err)
    }
    
    // 验证文件是否正确保存
    if err := dp.validateSavedFile(filename); err != nil {
        return fmt.Errorf("文件验证失败: %w", err)
    }
    
    return nil
}

func (dp *DocumentProcessor) checkDiskSpace(filename string) error {
    // 简单的磁盘空间检查
    info, err := os.Stat(filepath.Dir(filename))
    if err != nil {
        return err
    }
    
    // 这里可以添加更详细的磁盘空间检查逻辑
    _ = info
    return nil
}

func (dp *DocumentProcessor) validateSavedFile(filename string) error {
    info, err := os.Stat(filename)
    if err != nil {
        return fmt.Errorf("文件不存在: %w", err)
    }
    
    if info.Size() == 0 {
        return errors.New("文件大小为0")
    }
    
    return nil
}

func main() {
    processor := NewDocumentProcessor()
    
    data := map[string]interface{}{
        "title":   "测试文档",
        "content": "这是测试内容",
    }
    
    if err := processor.ProcessWithRetry(data, 3); err != nil {
        log.Fatalf("文档处理失败: %v", err)
    }
    
    fmt.Println("文档处理成功!")
}

这些示例展示了 WordZero 在各种实际场景中的应用,从简单的文档生成到复杂的企业级应用。每个示例都包含了完整的代码和最佳实践建议。

更多示例代码可以在 examples 目录 中找到。