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一键部署。

相关推荐
wxjlkh5 小时前
docker 搭建 grafana+prometheus 监控主机资源之node_exporter
docker·grafana·prometheus
杨浦老苏7 小时前
安全共享敏感信息的共享工具Hemmelig.app
docker·群晖·密码·阅后即焚
眠りたいです7 小时前
Docker:容器虚拟化技术基础-namespace,cgroups,资源管理与LXC
运维·docker·中间件·容器
java_logo9 小时前
宝塔 Linux 面板 Docker 容器化部署指南
linux·运维·docker·宝塔·docker部署宝塔·宝塔部署教程·docker部署baota
用户3521802454759 小时前
🚀 Milvus 实战部署全记录
数据库·docker·ai编程
令狐囱9 小时前
宝塔docker 运行 go-zero-looklook项目
docker·容器·golang
yBmZlQzJ9 小时前
内网穿透 + 域名解析:到底解决了什么核心问题?
运维·经验分享·网络协议·docker·容器
小挪号底迪滴9 小时前
Docker容器化实践:从开发到生产的完整流程
运维·docker·容器
gordon~99 小时前
Docker常用命令
运维·docker·容器