自动化文档生成工具(亲测可运行)

本文介绍了一个用Java编写的自动化文档生成工具,通过读取开发清单文本自动生成格式规范的Word文档。该工具的主要特点包括:

  1. 采用Apache POI库处理Word文档,支持多级标题和段落自动生成
  2. 实现中文数字转换功能,将编号转换为"一、二、三"等样式
  3. 预设标准的文档结构(输入/流程/输出/财务/异常/源代码等章节)
  4. 通过Maven打包成可执行JAR,只需将清单文本与JAR同目录即可运行

使用该工具可将原本繁琐的手动文档编写工作自动化,显著提高开发文档的编写效率。

单位要写开发文档,文档大致结构是上面有一个表格,填入开发内容清单,比如是这样:

|---|------|
| 1 | 新增用户 |
| 2 | 删除用户 |
| 3 | 查询用户 |

下面需要有对应的二级标题和三级标题,我就需要每次都去复制粘贴,改编号,很是麻烦。干脆用java写个脚本来处理吧。

核心思路是,把开发清单复制到txt文本,然后去读取,生成一个新的word,带上这些格式,再拷贝回去即可,省了不少时间。

pom.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com</groupId>
    <artifactId>doc-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <!-- 其他插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.5.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>main.WordGenerator</mainClass> <!-- 替换为你的主类 -->
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

WordGenerator.java

复制代码
package main;


import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.*;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


public class WordGenerator {

    /**
     * 使用BufferedReader按行读取文件
     */
    public static List<String> readLinesWithBufferedReader(String filePath) {
        List<String> lines = new ArrayList<>();


        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        } catch (IOException e) {
            System.err.println("读取文件失败: " + e.getMessage());
        }
        return lines;
    }

    static class Transaction {
        private String num;
        private String content;

        public Transaction(String num, String content) {
            this.num = num;
            this.content = content;
        }

        public String getNum() {
            return num;
        }

        public String getContent() {
            return content;
        }

        @Override
        public String toString() {
            return "Transaction{" +
                    "num='" + num + '\'' +
                    ", content='" + content + '\'' +
                    '}';
        }
    }

    public static String getPath()
    {
        String path = WordGenerator.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        System.out.println("原始路径:" +  path);
        if(System.getProperty("os.name").contains("dows"))
        {
            //path -> file:/D:/Haier/hlmcheckup-admin.jar!/BOOT-INF/lib/hlmcheckup-common-1.0.0.jar!/
            //如果是windows系统,前面有可能有"file:"要替换掉。
            path = path.replace("file:", "");
            path = path.substring(1,path.length());
            System.out.println("原始路径:" +  path);
        }
        if(path.contains("jar"))
        {
            //这里根据你jar包名称分割
            //D:/Haier/hlmcheckup-admin.jar!/BOOT-INF/lib/hlmcheckup-common-1.0.0.jar!/
            // -> D:/Haier/
            String[] split = path.split("doc-server-1.0-SNAPSHOT-jar-with-dependencies.jar");
            System.out.println("jar包路径:" +  split[0]);
            return split[0];
        }
        //"hlmcheckup-common/target/classes/" 为当前类路径 linux与macos需要替换掉
        return path.replace("hlmcheckup-common/target/classes/", "");
    }

    public static String getParentDirectory(String jarFilePath) {
        if (jarFilePath == null || jarFilePath.isEmpty()) {
            return null;
        }

        File jarFile = new File(jarFilePath);
        File parentDir = jarFile.getParentFile();

        if (parentDir != null && parentDir.exists() && parentDir.isDirectory()) {
            return parentDir.getAbsolutePath();
        }

        return null;
    }

    public static void main(String[] args) throws Exception {
        File jarFile = new File( getPath() + "\\1.txt" );
        System.out.println(jarFile.getAbsoluteFile());
        List<String> lines = readLinesWithBufferedReader(jarFile.getAbsolutePath());

        List<Transaction> transactionList = new ArrayList<>();
        System.out.println("读取的行数: " + lines.size());
        for (int i = 0; i <  lines.size(); i++) {
            transactionList.add(new Transaction(lines.get(i).split("\\t")[0],lines.get(i).split("\\t")[2]));
        }

        System.out.println(transactionList);



        // 创建新的Word文档
        try (XWPFDocument document = new XWPFDocument()) {

            ensureHeadingStylesExist(document);

            for (Transaction transaction: transactionList) {
                int index = 1;
                String chinese = NumberToChinese.toChinese(Long.parseLong(transaction.getNum()));
                // 创建一级标题
                createHeading(document, " 交易" + chinese + ":" + transaction.getContent(), 2);

                createHeading(document, " 输入" , 3 );
                createParagraph(document, " " + transaction.getContent() + " 输入参数" );

                createHeading(document, " 流程" , 3 );
                createParagraph(document, " 开始 -> " + transaction.getContent() + " -> 结束");

                createHeading(document, " 输出" , 3 );
                createParagraph(document, " " + transaction.getContent() + " 输出参数");

                createHeading(document, " 财务" , 3 );
                createParagraph(document, " 无");

                createHeading(document, " 异常" , 3 );
                createParagraph(document, " 无");

                createHeading(document,  " 源代码" , 3 );
                createParagraph(document, " 无");

            }


            // 保存文档
            try (FileOutputStream out = new FileOutputStream(new File("out.docx"))) {
                document.write(out);
                System.out.println("Word文档生成成功!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建标题段落
     * @param document Word文档对象
     * @param text 标题文本
     * @param level 标题级别(1-9)
     */
    private static void createHeading(XWPFDocument document, String text, int level) {
        XWPFParagraph paragraph = document.createParagraph();
        paragraph.setStyle("Heading" + level); // 设置标题样式

        XWPFRun run = paragraph.createRun();
        run.setText(text);
        run.setFontSize(16 - (level - 1) * 2); // 标题1:16pt, 标题2:14pt, 依此类推
        run.setBold(true);
    }

    /**
     * 创建正文段落
     * @param document Word文档对象
     * @param text 段落文本
     */
    private static void createParagraph(XWPFDocument document, String text) {
        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run = paragraph.createRun();
        run.setText(text);
        run.setFontSize(12); // 正文默认12pt
    }

    // 确保文档包含必要的标题样式
    private static void ensureHeadingStylesExist(XWPFDocument document) {
        try {
            XWPFStyles styles = document.getStyles();
            if (styles == null) {
                styles = document.createStyles();
            }

            // 检查Heading 1样式是否存在,不存在则创建
            if (!hasStyle(styles, "Heading1")) {
                createHeadingStyle(document, "Heading1", "Heading 1", 1, 24);
            }

            if (!hasStyle(styles, "Heading2")) {
                createHeadingStyle(document, "Heading2", "Heading 2", 2, 18);
            }

            if (!hasStyle(styles, "Heading3")) {
                createHeadingStyle(document, "Heading3", "Heading 3", 3, 14);
            }

        } catch (Exception e) {
            System.err.println("创建标题样式失败: " + e.getMessage());
        }
    }

    // 检查样式是否存在
    private static boolean hasStyle(XWPFStyles styles, String styleId) {
        return styles.getStyle(styleId) != null;
    }

    // 创建标题样式(兼容所有POI版本)
    private static void createHeadingStyle(XWPFDocument document, String styleId, String styleName, int level, int fontSize) {
        XWPFStyles styles = document.getStyles();

        // 创建新样式
        CTStyle ctStyle = CTStyle.Factory.newInstance();
        ctStyle.setStyleId(styleId);

        // 设置样式名称
        CTString name = CTString.Factory.newInstance();
        name.setVal(styleName);
        ctStyle.setName(name);

        // 设置样式类型为段落
        ctStyle.setType(STStyleType.PARAGRAPH);

        // 设置为标题样式
        CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
        indentNumber.setVal(BigInteger.valueOf(level));
        ctStyle.setUiPriority(indentNumber);

        // 设置基于Normal样式
        ctStyle.setBasedOn(CTString.Factory.newInstance());
        ctStyle.getBasedOn().setVal("Normal");

        // 设置段落属性
        CTPPr ppr = ctStyle.addNewPPr();
        ppr.addNewSpacing().setAfter(BigInteger.valueOf(100)); // 段后间距

        // 设置字体和大小(兼容旧版POI)
        CTRPr rpr = ctStyle.addNewRPr();

        CTFonts fonts = rpr.addNewRFonts();
        fonts.setAscii("宋体");
        fonts.setEastAsia("宋体");

        rpr.addNewSz().setVal(BigInteger.valueOf(fontSize * 2)); // 字体大小(Twips单位)
        rpr.addNewB().setVal(STOnOff.TRUE); // 加粗

        // 添加样式到文档
        XWPFStyle newStyle = new XWPFStyle(ctStyle);
        styles.addStyle(newStyle);
    }
}

NumberToChinese.java 工具类

复制代码
package main;

import java.math.BigDecimal;

public class NumberToChinese {
    // 中文数字字符映射
    private static final String[] CN_NUMBERS = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
    private static final String[] CN_INTEGER_UNITS = {"", "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "兆"};
    private static final String[] CN_DECIMAL_UNITS = {"角", "分", "厘", "毫"};

    // 中文金额单位映射(用于财务场景)
    private static final String[] CN_FINANCIAL_UNITS = {"", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆"};
    private static final String CN_ZERO = "零";
    private static final String CN_INTEGER = "整";
    private static final String CN_NEGATIVE = "负";

    /**
     * 将数字转换为中文大写(普通模式)
     * 例如:1234 → 一千二百三十四
     */
    public static String toChinese(long number) {
        if (number == 0) {
            return CN_NUMBERS[0];
        }

        StringBuilder result = new StringBuilder();
        boolean isNegative = number < 0;
        if (isNegative) {
            number = -number;
            result.append(CN_NEGATIVE);
        }

        String numStr = String.valueOf(number);
        int length = numStr.length();

        for (int i = 0; i < length; i++) {
            int digit = numStr.charAt(i) - '0';
            int unitIndex = length - i - 1;

            // 处理零的情况
            if (digit == 0) {
                // 避免多个连续的零
                if (i > 0 && numStr.charAt(i - 1) != '0') {
                    result.append(CN_NUMBERS[0]);
                }
                // 处理万亿等单位
                if (unitIndex % 4 == 0 && i < length - 1 && numStr.charAt(i + 1) != '0') {
                    result.append(CN_INTEGER_UNITS[unitIndex]);
                }
                continue;
            }

            result.append(CN_NUMBERS[digit]);
            result.append(CN_INTEGER_UNITS[unitIndex]);
        }

        return result.toString();
    }

    /**
     * 将数字转换为中文大写(财务模式)
     * 例如:1234.56 → 壹仟贰佰叁拾肆元伍角陆分
     */
    public static String toFinancialChinese(double number) {
        // 使用BigDecimal避免浮点数精度问题
        BigDecimal bd = new BigDecimal(String.valueOf(number));
        boolean isNegative = bd.signum() < 0;
        if (isNegative) {
            bd = bd.negate();
        }

        // 分离整数和小数部分
        BigDecimal integerPart = bd.setScale(0, BigDecimal.ROUND_DOWN);
        BigDecimal decimalPart = bd.subtract(integerPart).multiply(new BigDecimal(100)); // 转换为分

        StringBuilder result = new StringBuilder();
        if (isNegative) {
            result.append(CN_NEGATIVE);
        }

        // 处理整数部分
        String integerStr = integerPart.toPlainString();
        if (integerStr.equals("0")) {
            result.append(CN_NUMBERS[0]);
        } else {
            int length = integerStr.length();
            for (int i = 0; i < length; i++) {
                int digit = integerStr.charAt(i) - '0';
                int unitIndex = length - i - 1;

                // 处理零的情况
                if (digit == 0) {
                    if (i > 0 && integerStr.charAt(i - 1) != '0') {
                        result.append(CN_ZERO);
                    }
                    if (unitIndex % 4 == 0 && i < length - 1 && integerStr.charAt(i + 1) != '0') {
                        result.append(CN_FINANCIAL_UNITS[unitIndex]);
                    }
                    continue;
                }

                result.append(CN_NUMBERS[digit]);
                result.append(CN_FINANCIAL_UNITS[unitIndex]);
            }
        }

        result.append("元");

        // 处理小数部分
        int decimalInt = decimalPart.intValue();
        if (decimalInt == 0) {
            result.append(CN_INTEGER);
        } else {
            int jiao = decimalInt / 10;
            int fen = decimalInt % 10;

            if (jiao > 0) {
                result.append(CN_NUMBERS[jiao]).append(CN_DECIMAL_UNITS[0]);
            } else if (decimalInt > 9) {
                result.append(CN_ZERO);
            }

            if (fen > 0) {
                result.append(CN_NUMBERS[fen]).append(CN_DECIMAL_UNITS[1]);
            }
        }

        return result.toString();
    }

    // 测试示例
    public static void main(String[] args) {
        System.out.println("普通模式:");
        System.out.println("1234 → " + toChinese(1234));
        System.out.println("10001 → " + toChinese(10001));
        System.out.println("10030 → " + toChinese(10030));
        System.out.println("100000001 → " + toChinese(100000001));
        System.out.println("-123 → " + toChinese(-123));

        System.out.println("\n财务模式:");
        System.out.println("1234.56 → " + toFinancialChinese(1234.56));
        System.out.println("1000.01 → " + toFinancialChinese(1000.01));
        System.out.println("0.50 → " + toFinancialChinese(0.50));
        System.out.println("1000000 → " + toFinancialChinese(1000000));
        System.out.println("-123.45 → " + toFinancialChinese(-123.45));
    }
}

用maven打包后,将1.txt和jar放在同一个目录,双击jar,就会生成目标word文件。

1.txt

复制代码
1    无    新增用户
2    无    删除用户
3    无    查询用户

生成out

目录大纲也是正常的

相关推荐
Code季风1 小时前
跨语言RPC:使用Java客户端调用Go服务端的HTTP-RPC服务
java·网络协议·http·rpc·golang
盖世英雄酱581361 小时前
时间设置的是23点59分59秒,数据库却存的是第二天00:00:00
java·数据库·后端
clmm1232 小时前
Java动态生成Nginx服务配置
java·开发语言·nginx
东方芷兰2 小时前
Leetcode 刷题记录 17 —— 堆
java·c++·b树·算法·leetcode·职场和发展
草履虫建模3 小时前
Web开发全栈流程 - Spring boot +Vue 前后端分离
java·前端·vue.js·spring boot·阿里云·elementui·mybatis
code bean3 小时前
【C#】 C#中 nameof 和 ToString () 的用法与区别详解
android·java·c#
圆仔0073 小时前
【Java生成指定背景图片的PDF文件】
java
小猫咪怎么会有坏心思呢3 小时前
华为OD机考-分班问题/幼儿园分班-字符串(JAVA 2025B卷)
java·开发语言·华为od
在未来等你4 小时前
设计模式精讲 Day 4:建造者模式(Builder Pattern)
java·: design-patterns·builder-pattern·software-design·object-oriented-programming
今天我要乾重生4 小时前
java基础学习(三十)
java·开发语言·学习