DIFY合同生成全流程开发实践(三、后端接口以及优化方向)

一、目标

前面两篇文章,实现了从合同模板到新的合同文字内容的生成,这篇文章提供后端接口实现以及docker一键部署,实现dify http节点传入合同文本,并返回word合同文本下载链接。

二、架构

  • nginx

nginx实现生成的word文本对外提供下载访问链接

  • python

fastApi对外发布文字转word文档接口

  • docker

部署nginx以及python服务。

  • docker-compose.yml

通过编排,实现通过命令行一键自动下载镜像以及部署服务。

bash 复制代码
docker-compose up -d

三、具体实现

创建好如下目录:

全部目录以及文件如下:

nginx配置文件:

javascript 复制代码
server{					#以下配置只在http部分的server部分中生效
	listen 80;
	server_name  localhost;
	location /app/wordSave{			#以下配置只在http/server部分的location部分中生效
		root  /usr/share/nginx/html;
		index index.html  index.htm;
	}
	
}

python文件:

mdToword_tool_http_pypandoc.py:

python 复制代码
#!/usr/bin/env python3
"""
FastAPI服务器工具:将Markdown格式数据生成Word文档(使用pypandoc版本)
"""

from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
import os
import base64
import tempfile
import pypandoc
from typing import Dict, Any
from datetime import datetime
from pydantic import BaseModel

# 创建FastAPI应用实例
# 修改应用创建部分,移除请求体大小限制
app = FastAPI(
    title="Markdown转Word服务", 
    version="2.1.0",
    description="使用pypandoc的Markdown转Word文档服务",
    # 移除请求体大小限制
    body_size_limit=None
)

# 定义请求体模型
class ConvertRequest(BaseModel):
    markdown_content: str  # base64加密后的Markdown内容
    output_filename: str = None

# 定义响应模型
class ConvertResponse(BaseModel):
    success: bool
    message: str
    output_path: str
    local_path: str
    output_filename: str
    content_length: int
    error: str = None

# 定义信息响应模型
class InfoResponse(BaseModel):
    service_name: str
    version: str
    description: str
    supported_formats: list
    output_format: str
    features: list

def process_markdown_to_word(markdown_content: str, output_filename: str = None) -> Dict[str, Any]:
    """
    使用pypandoc将Markdown内容转换为Word文档
    
    Args:
        markdown_content: base64加密后的Markdown格式的文本内容
        output_filename: 输出文件名(可选)
        
    Returns:
        包含转换结果的字典
    """
    try:
        # 生成当前时间戳(格式:年-月-日_时-分-秒)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # 如果没有指定输出文件名,生成默认文件名
        if not output_filename:
            output_filename = f"output_document_{timestamp}.docx"
        else:
            # 处理用户提供的文件名,添加时间戳
            name, ext = os.path.splitext(output_filename)
            if not ext:
                ext = '.docx'
            output_filename = f"{name}_{timestamp}{ext}"
        
        # 获取当前文件所在目录
        current_dir = os.path.dirname(os.path.abspath(__file__))
        # 设置wordSave文件夹路径
        word_save_dir = os.path.join(current_dir, 'wordSave')
        
        # 确保wordSave目录存在
        if not os.path.exists(word_save_dir):
            os.makedirs(word_save_dir)
        
        # 构建完整的输出路径
        output_path = os.path.join(word_save_dir, output_filename)
        
        # 构建nginx可用的相对路径
        nginx_path = f"app/wordSave/{output_filename}"
        
        # 解码base64内容
        try:
            decoded_content = base64.b64decode(markdown_content).decode('utf-8')
        except Exception as e:
            raise ValueError(f"Base64解码失败: {str(e)}")
        
      
        
       
        try:
            # 使用pypandoc转换Markdown到Word
            pypandoc.convert_text(
                decoded_content,
                'docx',
                format='markdown',
                outputfile=output_path,
                # 添加额外的转换选项以获得更好的结果
                extra_args=[
                    '--standalone',
                    '--wrap=preserve',
                    # 修改为更适合办公文档的字体设置,确保WPS完全兼容
                    '-V', 'CJKmainfont=SimSun',        # 主字体:宋体(WPS最兼容的基础字体)
                    '-V', 'CJKsansfont=SimHei',        # 无衬线字体:黑体
                    '-V', 'CJKseriffont=KaiTi',         # 衬线字体:楷体
                    '-V', 'CJKmonofont=FangSong',       # 等宽字体:仿宋
                    '-V', 'mainfont=Times New Roman',   # 西文字体:Times New Roman
                    # 字体大小设置
                    '-V', 'fontsize=12pt',              # 正文字体大小
                    # 目录设置
                    '--toc',  # 添加目录生成
                    '--toc-depth=6',  # 确保捕获所有6级标题
                    # 标题页设置
                    '-V', 'title-page=true',  # 启用标题页
                    '-V', 'title=文档标题',  # 文档标题
                    '-V', 'subtitle=副标题',  # 副标题
                    '-V', 'author=作者名称',  # 作者
                    '-V', 'date=\\today',  # 当前日期
                    # 页面设置
                    '-V', 'header-includes=\\pagenumbering{gobble}',
                    # Markdown格式设置
                    '--markdown-headings=atx',  # 明确指定使用atx格式的标题(#开头)
                    # 保留更多的格式信息
                    '-f', 'markdown+smart+auto_identifiers+header_attributes+raw_html'
                ]
            )
            
            # 检查文件是否成功创建
            if not os.path.exists(output_path):
                raise Exception("转换失败:未生成Word文件")
                
            return {
                "success": True,
                "message": "Markdown转Word成功(使用pypandoc)",
                "output_path": "http://127.0.0.1:9999/" + nginx_path,  # 返回nginx可用的路径
                "local_path": output_path,  # 保留本地完整路径用于调试
                "output_filename": output_filename,
                "content_length": len(markdown_content)
            }
            
        except Exception as e:
            return {
                "message": "转换过程中出现错误",
                "error": str(e),
            
            }
                
    except Exception as e:
        return {
            "success": False,
            "error": str(e),
            "message": "转换过程中出现错误",
            "output_path": "",
            "local_path": "",
            "output_filename": "",
            "content_length": 0
        }

@app.post("/convert", response_model=ConvertResponse)
async def convert_markdown_to_word_endpoint(request: ConvertRequest):
    """
    将Markdown格式数据转换为Word文档的POST接口
    
    Args:
        request: 包含markdown_content(必需)和output_filename(可选)的请求体
        
    Returns:
        转换结果,包含成功状态、文件路径等信息
    """
    print("接收到转换请求")
    if not request.markdown_content:
        raise HTTPException(status_code=400, detail="缺少markdown_content参数")
    
    result = process_markdown_to_word(request.markdown_content, request.output_filename)
    print(f"转换结果: {result}")
    if not result["success"]:
        raise HTTPException(status_code=500, detail=result.get("error", "转换失败"))
    
    return result

@app.get("/info", response_model=InfoResponse)
async def get_conversion_info_endpoint():
    """
    获取转换服务信息的GET接口
    
    Returns:
        服务信息,包括版本、功能等
    """
    return {
        "service_name": "Markdown转Word服务",
        "version": "2.1.0",
        "description": "使用pypandoc和FastAPI框架的Markdown转Word文档服务",
        "supported_formats": ["markdown", "md"],
        "output_format": "docx",
        "features": [
            "使用pypandoc实现高质量转换",
            "支持复杂Markdown语法",
            "保留原始格式和样式",
            "支持表格、代码块、数学公式",
            "文件名自动添加时间戳",
            "支持base64编码内容输入",
            "可选的目录生成功能"
        ]
    }

@app.get("/")
async def root():
    """
    根路径,返回服务基本信息
    """
    return {
        "message": "欢迎使用Markdown转Word服务(pypandoc版)",
        "version": "2.1.0",
        "endpoints": {
            "convert": "POST /convert - 将Markdown转换为Word",
            "info": "GET /info - 获取服务信息"
        }
    }

if __name__ == "__main__":
    import uvicorn
    # 运行FastAPI服务器
    uvicorn.run(
        "mdToword_tool_http_pypandoc:app",
        host="127.0.0.1",
        port=7777,
        reload=True  # 开发环境开启热重载
    )

Dockerfile:以官方python镜像为基础,创建包含python接口镜像的

bash 复制代码
# 使用官方Python镜像作为基础镜像
FROM python:3.13.5

# 维护者信息
MAINTAINER 944658413@qq.com

# 设置工作目录
WORKDIR /app


# 复制requirements.txt到工作目录
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制Python代码到工作目录
#COPY mdToword_tool_http.py .
COPY mdToword_tool_http_pypandoc.py .
# 创建wordSave目录用于保存生成的Word文件
RUN mkdir -p /app/wordSave

# 安装pandoc(pypandoc的系统依赖)
RUN apt-get update && \
    apt-get install -y pandoc && \
    rm -rf /var/lib/apt/lists/*
# 设置卷挂载,将容器中的wordSave目录挂载到外部
VOLUME ["./nginx/wordSave"]

# 暴露Python应用端口(与代码中设置的7777端口一致)
EXPOSE 7777

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# 启动Python应用
CMD ["uvicorn", "mdToword_tool_http_pypandoc:app", "--host", "0.0.0.0", "--port", "7777"]

requirements.txt:依赖

python 复制代码
# Markdown转Word服务依赖列表

# Web框架
fastapi>=0.95.0
uvicorn[standard]>=0.22.0

# 文档处理
python-docx>=0.8.11
markdown>=3.4.0
pypandoc>=1.11

# HTML解析
beautifulsoup4>=4.11.0

# 数据验证
pydantic>=1.10.0

# 可选依赖:提高性能
httpx>=0.23.0  # 异步HTTP客户端
python-multipart>=0.0.6  # 表单数据支持

docker-compose:

PS:

两个虚拟机同时挂载同一个wordSave文件夹,可实现生成的word文件在nginx对外发布。

bash 复制代码
version: '3.8'
services:
  wb-dify-nginx:
    image: nginx:latest
    environment:
      - TZ=Asia/Shanghai
    container_name: wb-dify-nginx
    ports:
      - "9999:80"
    volumes:
      - ./nginx/wordSave:/usr/share/nginx/html/app/wordSave
      - ./nginx/logs:/var/log/nginx
      - ./nginx/conf.d:/etc/nginx/conf.d
    restart: unless-stopped
  wb-dify-python:
    build:
      context: ./python
      dockerfile: Dockerfile
    environment:
      - TZ=Asia/Shanghai
    image: wb-dify-python
    container_name: wb-dify-python
    ports:
      - "7777:7777"
    volumes:
      - ./nginx/wordSave:/app/wordSave
    restart: unless-stopped  
    
# extra_hosts:
#  - "somehost:162.242.195.82"
# 以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:162.242.195.82  somehost

四、结果演示

dify节点,http得到返回以后,后面跟一个内容优化节点达到显示美观的效果

实际演示:

五、其他

docker安装与部署,参考我的这篇文章,很详细了:

轻松搞定Docker安装与虚拟化设置

dify的部署:

暂时没写,基本上都是梯子去github 克隆以后,去docker文件夹,运行类似的docker-compose.yml一键部署。

相关推荐
zyu671 小时前
03-Docker存储和网络
网络·docker·容器
牛奔2 小时前
Docker Compose 两种安装与使用方式详解(适用于 Docker 19.03 版本)
运维·docker·云原生·容器·eureka
青州从事5218 小时前
20260108【mac】【brew】【docker】安装
macos·docker·eureka
菜鸟思维9 小时前
优化NextJs 项目的Docker 镜像 从3.62G 优化到 296.85M
docker
怣疯knight9 小时前
Docker Desktop 4.55.0版本安装成功教程
windows·docker
东方佑10 小时前
使用Docker Compose一键部署OnlyOffice:完整指南与配置解析
运维·docker·容器
赵文宇(温玉)10 小时前
Docker的价值、特点、创新与关键技术
运维·docker·容器
Coder码匠12 小时前
Docker Compose 部署 Spring Boot 应用完全指南
spring boot·docker·容器
可爱又迷人的反派角色“yang”13 小时前
k8s(二)
linux·运维·docker·云原生·容器·kubernetes·云计算
计算机小手13 小时前
内网穿透系列十六:使用 wg-easy 快速搭建基于 wireguard 的虚拟局域网,支持Docker部署
经验分享·网络协议·docker·开源软件