Skip to content

Markdown to DOCX 转换工具 - 开发指南 ​

1. 开发环境设置 ​

环境要求 ​

  • Python: 3.8+
  • 操作系统: Linux/macOS/Windows
  • 依赖管理: pip + virtualenv

快速开始 ​

bash
# 1. 克隆项目
git clone https://github.com/wangqiqi/md2docx.git
cd md2docx

# 2. 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
# 或 venv\Scripts\activate  # Windows

# 3. 安装依赖
pip install -e ".[dev]"

# 4. 运行测试验证环境
pytest tests/

2. 项目结构详解 ​

核心模块说明 ​

src/mddocx/converter/base.py ​

  • BaseConverter类: 核心转换器,协调所有元素转换
  • 错误处理: MD2DocxError、ParseError、ConvertError
  • 调试支持: 可选的调试信息输出

src/mddocx/converter/elements/ ​

  • ElementConverter: 基础转换器接口
  • 各元素转换器: 针对不同Markdown元素的专门处理

src/cli.py ​

  • 命令行接口: 单文件转换支持
  • 参数处理: 输入输出路径、调试选项
  • 错误处理: 文件占用等异常情况处理

src/mddocx/webui/app.py (新增) ​

  • Flask应用: Web服务器主入口
  • 路由处理: / (主页), /convert (转换), /preview (预览)
  • 文件处理: 上传、转换、下载临时文件管理
  • 错误处理: 用户友好的错误提示

src/mddocx/webui/templates/ (新增) ​

  • Jinja2模板: HTML页面模板
  • base.html: 基础布局,包含导航和样式
  • index.html: 主页,文件上传和文本输入
  • preview.html: 预览页,源码和样式预览

src/mddocx/webui/static/ (新增) ​

  • CSS样式: 现代化响应式设计
  • JavaScript: 前端交互和表单验证
  • 资源文件: 图标、字体等静态资源

测试结构 ​

tests/
├── unit/           # 单元测试
│   └── test_elements/  # 各元素转换器测试
├── integration/    # 集成测试
│   └── test_*.py   # 端到端转换测试
└── samples/        # 测试样例
    ├── basic/      # 基础语法样例
    └── advanced/   # 高级语法样例

3. 开发工作流 ​

分支策略 ​

master (主分支)
├── 稳定版本发布
└── 生产就绪代码

dev (开发分支)
├── 日常开发
├── 功能集成
└── 从master创建feature分支

feature/* (功能分支)
├── 新功能开发
├── 从dev分支创建
└── 完成后合并回dev

提交规范 ​

bash
# 格式: <type>(<scope>): <subject>
# 例如:
feat: 添加图片转换功能
fix: 修复表格对齐问题
docs: 更新开发指南
test: 添加列表转换测试
refactor: 重构错误处理逻辑

代码审查流程 ​

  1. 创建功能分支: git checkout -b feature/new-feature
  2. 编写代码: 遵循代码规范
  3. 编写测试: 确保测试覆盖
  4. 提交代码: git commit -m "feat: 功能描述"
  5. 推送分支: git push origin feature/new-feature
  6. 创建PR: 在GitHub上创建Pull Request
  7. 代码审查: 团队成员审查代码
  8. 合并代码: 审查通过后合并到dev分支

4. 代码规范 ​

Python代码风格 ​

python
# 使用Black进行代码格式化
# 类型注解必须完整
# 函数必须有文档字符串

def convert_element(self, element: Any) -> Any:
    """转换元素为DOCX格式

    Args:
        element: 要转换的元素

    Returns:
        转换后的DOCX元素
    """
    pass

命名约定 ​

  • 类名: PascalCase (如 BaseConverter, HeadingConverter)
  • 方法名: snake_case (如 convert_heading, process_text)
  • 变量名: snake_case (如 input_file, output_path)
  • 常量: UPPER_CASE (如 MAX_RETRY_COUNT)

错误处理 ​

python
try:
    # 可能出错的操作
    result = risky_operation()
except SpecificError as e:
    # 具体错误处理
    logger.error(f"具体错误: {e}")
    raise ConvertError(f"转换失败: {e}") from e
except Exception as e:
    # 通用错误处理
    logger.error(f"未知错误: {e}")
    raise MD2DocxError(f"系统错误: {e}") from e

5. 测试开发 ​

单元测试编写 ​

python
import pytest
from src.converter.elements.heading import HeadingConverter

class TestHeadingConverter:
    def test_h1_conversion(self):
        converter = HeadingConverter()
        # 测试H1标题转换
        pass

    def test_h6_conversion(self):
        # 测试H6标题转换
        pass

集成测试编写 ​

python
def test_full_markdown_conversion():
    """测试完整Markdown文档转换"""
    input_md = """
    # 标题
    这是一个**粗体**文本。

    - 项目1
    - 项目2
    """

    converter = BaseConverter()
    doc = converter.convert(input_md)

    # 验证转换结果
    assert doc is not None

测试运行 ​

bash
# 运行所有测试
pytest

# 运行单元测试
pytest tests/unit/

# 运行集成测试
pytest tests/integration/

# 生成覆盖率报告
pytest --cov=src --cov-report=html

6. 调试技巧 ​

调试模式 ​

bash
# 启用调试输出
python -m src.cli input.md output.docx --debug

日志配置 ​

python
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

转换指标(ConvertMetrics) ​

每次 BaseConverter.convert() / convert_file() 结束后,可通过 converter.last_metrics 读取程序可读快照(与日志 convert_done 字段对齐):

字段类型说明
duration_msfloat墙钟耗时(毫秒)
input_bytesintMarkdown UTF-8 字节数
chunkedbool是否走分块转换路径
python
from mddocx.converter import BaseConverter, ConvertMetrics

c = BaseConverter()
c.convert("# Hi")
m: ConvertMetrics = c.last_metrics
print(m.duration_ms, m.input_bytes, m.chunked)

转换前 last_metrics 为 None;异常路径在 finally 中仍会写入指标。

ConvertMetrics 性能基线(CI) ​

入库基线:tests/baselines/convert_metrics.json(小样例 basic/headings.md + large/chunked.md 强制分块)。字段含 duration_ms / input_bytes / chunked。

命令作用
python scripts/collect_convert_metrics.py打印当前快照
python scripts/collect_convert_metrics.py --write更新入库基线(样例内容或预期耗时变了再跑)
python scripts/collect_convert_metrics.py --compare结构字段(input_bytes/chunked/路径)须与基线一致
bash scripts/check_convert_metrics.sh结构硬失败 + 耗时相对阈值(默认 soft)

耗时门禁:live > max(baseline × METRICS_MAX_RATIO, METRICS_FLOOR_MS) 才告警。默认 ratio=5 · floor=200ms · METRICS_SOFT=1(只 WARN、退出 0,避免 CI runner 抖动假红)。本地要硬失败:METRICS_SOFT=0 bash scripts/check_convert_metrics.sh。

失败解读:

  • --compare FAIL → 样例体积或分块路径变了;先确认是否故意改样例,再 --write 更新基线并同 PR 说明。
  • 耗时 WARN/FAIL → 热路径明显变慢;先本地复现,勿仅因微小波动改基线。

CI quality job 已调用 check_convert_metrics.sh。

常见调试场景 ​

  1. 转换结果异常: 检查元素转换器的输出
  2. 样式问题: 验证DOCX样式设置
  3. 解析错误: 检查Markdown解析结果
  4. 性能问题: 使用cProfile分析性能瓶颈;回归用上表基线门禁

7. 性能优化 ​

大文件与分块转换 ​

  • 体积上限:MAX_MARKDOWN_BYTES = 16MB(converter/security.py,与 WebUI 上传限制一致)
  • 自动分块:UTF-8 体积 ≥ CHUNKED_THRESHOLD(512KB)时,按 一级标题 # 切分后逐段 parse + TokenProcessor,追加同一 DOCX
  • 文件入口:BaseConverter.convert_file(Path) 流式统计字节数后再读取;CLI 已接入
  • 显式控制:convert(md, chunked=True/False) 可强制开启或关闭分块
python
from pathlib import Path
from mddocx.converter.base import BaseConverter

converter = BaseConverter()
doc = converter.convert_file(Path("large.md"))  # 大文件自动分块
doc = converter.convert(text, chunked=False)   # 强制单次 parse

代码优化 ​

  • 避免重复解析: 缓存解析结果
  • 批量处理: 支持多个文件批量转换
  • 内存管理: 大文件处理时的内存优化

工具使用 ​

bash
# 性能分析
python -m cProfile -s time script.py

# 内存分析
pip install memory_profiler
python -m memory_profiler script.py

8. 扩展开发 ​

添加新元素转换器 ​

  1. 创建转换器类: 继承ElementConverter
  2. 实现转换逻辑: convert方法
  3. 注册转换器: 在BaseConverter中注册
  4. 编写测试: 创建对应的单元测试
  5. 更新文档: 在README中添加新功能说明

示例:添加数学公式支持 ​

python
# src/converter/elements/math.py
class MathConverter(ElementConverter):
    def convert(self, math_block):
        # 实现数学公式转换逻辑
        pass

# 在BaseConverter中注册
self.register_converter('math', MathConverter())

9. 发布准备 ​

版本发布流程 ​

  1. 更新版本号: 修改pyproject.toml和__init__.py
  2. 更新文档: 完善CHANGELOG和README
  3. 运行测试: 确保所有测试通过
  4. 创建标签: git tag v1.0.0
  5. 打包发布: 使用PyInstaller打包

打包配置 ​

bash
# 使用PyInstaller打包
pyinstaller --onefile --name md2docx src/cli.py

# 或使用setuptools
python setup.py sdist bdist_wheel

9. Web界面开发 ​

Web界面概述 ​

项目提供完整的Web界面,基于Flask框架实现,支持:

  • 文件上传和文本输入
  • 实时预览和转换
  • 响应式设计 (桌面/移动)
  • RESTful API接口

启动Web界面 ​

bash
# 方法1:使用命令行工具(推荐)
mddocx-webui

# 方法2:直接运行模块
python -m src.mddocx.webui.app

# 方法3:运行启动脚本
python src/mddocx/webui/start_webui.py

# 访问 http://localhost:5000

Web界面架构 ​

src/mddocx/webui/
├── __init__.py         # 包初始化
├── app.py              # Flask应用核心
├── config.py           # 应用配置
├── start_webui.py      # WebUI启动脚本
├── templates/          # Jinja2模板
│   ├── base.html      # 基础布局
│   ├── index.html     # 主页
│   └── preview.html   # 预览页
├── static/            # 静态资源
│   ├── css/
│   │   └── styles.css # 样式文件
│   ├── js/
│   │   └── app.js     # 前端脚本
│   └── img/           # 图片资源
├── tests/             # WebUI测试
│   ├── __init__.py
│   └── test_basic.py
└── utils/             # 工具模块

添加新功能 ​

  1. 后端路由: 在 webui/app.py 中添加新路由
  2. 前端页面: 创建新的HTML模板
  3. 样式增强: 更新CSS文件
  4. 交互功能: 扩展JavaScript代码

API接口 ​

Web界面提供了REST API接口:

  • POST /convert - 文档转换
  • POST /preview - 预览功能
  • 详细 API 文档见 API 与部署

部署Web界面 ​

bash
# 生产环境部署
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 webui.app:app

# Docker部署
docker build -t md2docx .
docker run -p 8000:8000 md2docx

10. 故障排除 ​

常见问题 ​

  1. 依赖安装失败: 检查Python版本和pip配置
  2. 测试失败: 运行pytest -v查看详细错误
  3. 转换结果异常: 使用--debug选项查看调试信息
  4. 性能问题: 使用性能分析工具定位瓶颈

获取帮助 ​

  • 问题反馈: 在GitHub Issues中提交问题
  • 贡献指南: 查看CONTRIBUTING.md
  • 开发讨论: 参与GitHub Discussions

此开发指南将随着项目发展持续更新。如有问题或建议,请提交Issue或PR。

MIT Licensed