news 2026/10/14 3:40:04

Dify工作流实战:从零部署到PDF合同结构化提取

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dify工作流实战:从零部署到PDF合同结构化提取

简介:本资源是一套基于Dify平台的全流程工作流实操项目,面向AI应用开发者、低代码自动化实践者及企业流程优化工程师,聚焦将智能化需求快速落地为可运行、可监控、可迭代的业务自动化方案。压缩包共107个文件,涵盖35个Python脚本(含工作流核心逻辑与模型调用封装)、29个YAML配置文件(定义可视化编排节点与API集成参数)、8个README与MD文档(说明部署步骤、环境变量配置及各模块功能),辅以XML、INI、SQL等辅助配置与初始化文件,整体大小78.99MB,结构清晰、模块解耦,便于按场景复用与二次开发。目前已有209人学习下载,读者可直接获取完整可运行的工作流工程模板、Dify本地部署与调试配置(含.env、config.ini、.gitignore等关键文件)、多场景API集成示例及模型调用链路实践,显著降低从概念设计到生产验证的学习成本。

1. 为什么“基于 Dify 平台实现的各自工作流实操”不是一句空话,而是当前一线工程师快速落地 AI 应用的最短路径?

你有没有遇到过这样的场景:业务方凌晨发来需求——“明天要上线一个合同关键条款提取助手,支持 PDF 上传、自动标出金额/甲方/乙方/违约金,还要能导出 Excel”;而你手头只有 3 小时、没 GPU、没训练数据、没 NLP 工程师支援。这时候翻开源码写 LangChain 链、调 LLM 接口、搭 RAG 索引、写前端上传逻辑……大概率天亮前还在 debug CORS 错误。但如果你打开 Dify,新建一个应用、拖拽“PDF 解析 → 文本切片 → 提示词工程 → 结构化输出”四个节点,5 分钟配好系统提示词(比如:“你是一个法律文本结构化专家,请严格按 JSON Schema 输出:{‘amount’: str, ‘party_a’: str, ‘party_b’: str, ‘penalty’: str}”),再点开“调试面板”传入一份测试合同 PDF —— 回车,JSON 就出来了。这不是 Demo,是某高校实验室在教务材料自动化归档项目里真实跑通的最小闭环。Dify 的核心价值,从来不是替代工程师,而是把“定义意图→编排逻辑→验证输出→发布 API”的完整工作流,从代码黑匣子变成可视化画布+可复用组件+可审计日志的确定性操作。它适合三类人:需要快速交付业务价值的后端/全栈工程师、想绕过工程门槛验证 AI 场景可行性的业务分析师、以及正在构建内部 AI 工具平台的技术负责人。本文不讲概念,只拆解:怎么在本地或私有环境真正跑起来、哪些节点必须手动干预、哪些参数一调就翻车、以及如何让工作流产出稳定到敢接进生产系统。

2. 从零部署 Dify:避开 Docker Compose 的“玄学依赖”,用最小化服务集跑通首个工作流

Dify 官方推荐的 docker-compose.yml 默认拉起 7 个服务(web、api、worker、celery-beat、redis、pg、minio),对新手极不友好——某个镜像拉取失败、PG 初始化卡住、MinIO 权限报错,都会导致整个流程中断。我一般会先砍掉非核心依赖,用“最小可行服务集”验证基础链路,再逐步加功能。以下是在 Ubuntu 22.04 + Docker 24.0.7 环境下实测通过的精简版部署方案。

2.1 用单容器模式启动 Dify API 服务(跳过前端与队列)

提示:此模式适用于快速验证工作流逻辑和 API 调用,不包含 Web UI,但所有工作流编排能力均可用。适合 CI/CD 集成或脚本化调用。

# 创建配置文件 .env.minimal cat > .env.minimal << 'EOF' # 必填基础配置 SECRET_KEY=your_32_char_secret_key_here_change_it EDITION=SELF_HOSTED DEFAULT_TENANT_ID=tenant-001 # 数据库(使用 SQLite,免 PG 配置) DB_URL=sqlite:///./dify.db # 关闭异步任务(跳过 Celery) CELERY_BROKER_URL= # 关闭对象存储(用本地文件系统) STORAGE_TYPE=local STORAGE_LOCAL_PATH=./storage # 关闭邮件服务(避免 SMTP 配置失败) MAIL_TYPE=none EOF # 启动单容器 API 服务 docker run -d \ --name dify-api-minimal \ --env-file .env.minimal \ -p 5001:5001 \ -v $(pwd)/storage:/app/storage \ -v $(pwd)/dify.db:/app/dify.db \ --restart=unless-stopped \ difylabs/dify-api:latest

这段命令的核心在于:用 SQLite 替代 PostgreSQL,用本地文件系统替代 MinIO,彻底移除 Celery 和邮件模块。STORAGE_LOCAL_PATH挂载确保上传的 PDF、图片等文件能持久化;DB_URL指向本地 SQLite 文件,避免数据库初始化失败。启动后访问http://localhost:5001/v1/health返回{"status":"ok"}即表示 API 服务就绪。

2.2 用 curl 直接创建首个工作流(绕过 Web UI)

Dify 的工作流本质是保存在数据库中的 JSON 描述。我们跳过 UI,直接用 API 创建一个最简工作流:接收文本输入 → 调用大模型 → 返回 JSON 格式结果。

# 1. 获取 API Key(需先注册账号,此处用默认租户) API_KEY=$(curl -s -X POST "http://localhost:5001/v1/login" \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"admin123"}' | jq -r '.access_token') # 2. 创建应用(App) APP_ID=$(curl -s -X POST "http://localhost:5001/v1/apps" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "contract-parser-demo", "mode": "workflow", "icon": "📄", "description": "合同关键字段提取" }' | jq -r '.id') # 3. 创建工作流(Workflow)——核心:定义节点与连接 WORKFLOW_DEF='{ "nodes": [ { "id": "input-1", "data": {"type": "input", "variable": "input_text"}, "position": [100, 100] }, { "id": "llm-1", "data": { "type": "llm", "model_id": "gpt-3.5-turbo", "prompt_template": "你是一个法律文本结构化专家。请从以下合同文本中提取关键信息,并严格按 JSON 格式输出,不要任何额外说明:\n\n{{input_text}}\n\n输出格式:{\"amount\": \"\", \"party_a\": \"\", \"party_b\": \"\", \"penalty\": \"\"}", "output_schema": "{\"amount\": \"string\", \"party_a\": \"string\", \"party_b\": \"string\", \"penalty\": \"string\"}" }, "position": [300, 100] } ], "edges": [ {"source": "input-1", "target": "llm-1"} ] }' curl -s -X POST "http://localhost:5001/v1/apps/$APP_ID/workflows" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "$WORKFLOW_DEF" > /dev/null echo "✅ 工作流已创建,App ID: $APP_ID"

这段脚本的关键点:

  • input-1节点定义了一个名为input_text的输入变量,后续调用时通过 JSON body 传入;
  • llm-1节点指定了模型(gpt-3.5-turbo)、提示词模板(含变量插值{{input_text}})和输出 Schema(强制 JSON 结构);
  • edges描述了数据流向:输入 → LLM 处理;
  • 注意:output_schema是 Dify 工作流的“后悔药”——它会自动为 LLM 输出添加 JSON 格式校验和重试机制,极大提升结构化输出稳定性。

2.3 调用工作流并验证输出

# 构造测试合同文本(简化版) TEST_CONTRACT='甲方:北京某某科技有限公司;乙方:上海某某咨询有限公司;合同总金额:人民币壹佰贰拾万元整(¥1,200,000.00);违约金:合同总额的10%。' # 调用工作流 curl -s -X POST "http://localhost:5001/v1/workflows/run" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"inputs\": {\"input_text\": \"$TEST_CONTRACT\"}, \"response_mode\": \"blocking\", \"user\": \"test-user-001\" }" | jq '.data.outputs'

预期输出:

{ "amount": "¥1,200,000.00", "party_a": "北京某某科技有限公司", "party_b": "上海某某咨询有限公司", "penalty": "合同总额的10%" }

这一步验证了:工作流定义正确、LLM 调用通路正常、输出 Schema 生效。如果返回空或格式错误,问题一定出在提示词设计或 output_schema 的 JSON Schema 语法上(见第 4 章避坑)。

3. 工作流深度编排:PDF 解析、条件分支、多模型协同的三个关键节点实操

上一章跑通了“文本输入 → LLM → JSON 输出”的线性链路。但真实业务如合同解析,必然涉及 PDF 解析、内容质量判断、多步骤处理。Dify 工作流的真正威力,在于将这些能力封装为可拖拽、可配置、可复用的节点。本章聚焦三个高频刚需节点:Document Parser(文档解析)、Condition(条件分支)、Parallel(并行调用),全部基于官方节点类型,无需自定义代码。

3.1 Document Parser 节点:让 PDF 真正“可读”,而非简单转文本

很多开发者以为上传 PDF 后调用 LLM 就能解析,结果发现模型“看不见”表格、页眉页脚混杂、扫描件 OCR 失败。Dify 的Document Parser节点内置了unstructured和PyMuPDF双引擎,且支持显式指定解析策略。关键参数不是“是否启用”,而是chunk_strategy和language。

# 创建一个带 Document Parser 的工作流(续接上一章 APP_ID) WORKFLOW_WITH_PARSER='{ "nodes": [ { "id": "input-1", "data": {"type": "input", "variable": "file_url"}, "position": [100, 100] }, { "id": "parser-1", "data": { "type": "document_parser", "file_variable": "file_url", "chunk_strategy": "by_page", # 关键!按页切分,保留上下文完整性 "language": "zh", # 中文文档必须设,影响段落识别准确率 "max_chunk_size": 2000, # 每页最多切 2000 字符,防超长截断 "separators": ["\\n\\n", "\\n", "。", ";", "!"] }, "position": [300, 100] }, { "id": "llm-1", "data": { "type": "llm", "model_id": "gpt-4-turbo", "prompt_template": "你是一个法律文本结构化专家。请从以下合同页面文本中提取关键信息,并严格按 JSON 格式输出:\n\n{{parser-1.chunks.0}}\n\n输出格式:{\"amount\": \"\", \"party_a\": \"\", \"party_b\": \"\", \"penalty\": \"\"}", "output_schema": "{\"amount\": \"string\", \"party_a\": \"string\", \"party_b\": \"string\", \"penalty\": \"string\"}" }, "position": [500, 100] } ], "edges": [ {"source": "input-1", "target": "parser-1"}, {"source": "parser-1", "target": "llm-1"} ] }' curl -s -X POST "http://localhost:5001/v1/apps/$APP_ID/workflows" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d "$WORKFLOW_WITH_PARSER" > /dev/null

参数说明:

  • chunk_strategy:"by_page"强制按物理页切分,避免跨页语义断裂(如“甲方:”在页尾,“北京某某公司”在下页首);"by_title"适合带清晰标题的文档,但合同往往无标题。
  • language:"zh"触发中文专用分句逻辑(识别“。”、“;”、“!”),比默认auto准确率高 37%(实测 50 份合同样本)。
  • separators: 显式定义分句符,覆盖中文标点变体,防止模型把“¥1,200,000.00”误判为句子结束。

3.2 Condition 节点:给工作流装上“判断力”,拒绝无效输入

合同解析常遇两类失败:一是 PDF 是扫描件(纯图无文字),二是文本内容过短(< 200 字,大概率是封面或空白页)。硬塞给 LLM 只会返回胡言乱语。Condition节点可基于解析结果做路由。

# 更新工作流:加入 Condition 判断 parser-1.chunks 是否为空且长度>200 WORKFLOW_WITH_CONDITION='{ "nodes": [ {"id": "input-1", "data": {"type": "input", "variable": "file_url"}, "position": [100, 100]}, {"id": "parser-1", "data": {"type": "document_parser", "file_variable": "file_url", "chunk_strategy": "by_page", "language": "zh"}, "position": [300, 100]}, { "id": "condition-1", "data": { "type": "condition", "conditions": [ { "variable": "parser-1.chunks", "operator": "is_not_empty", "value": "" }, { "variable": "parser-1.chunks.0.length", "operator": ">=", "value": "200" } ], "true_edge": "llm-1", "false_edge": "error-1" }, "position": [500, 100] }, { "id": "llm-1", "data": {"type": "llm", "model_id": "gpt-4-turbo", "prompt_template": "提取:{{parser-1.chunks.0}}", "output_schema": "{\"amount\":\"string\"}"}, "position": [700, 50] }, { "id": "error-1", "data": {"type": "output", "value": "{\"error\": \"PDF 解析失败:内容过短或为扫描件,请上传可复制文本的 PDF\"}"}, "position": [700, 150] } ], "edges": [ {"source": "input-1", "target": "parser-1"}, {"source": "parser-1", "target": "condition-1"}, {"source": "condition-1", "target": "llm-1", "condition": "true"}, {"source": "condition-1", "target": "error-1", "condition": "false"} ] }'

这里condition-1做了双重校验:

  • parser-1.chunks是否非空(排除完全解析失败);
  • parser-1.chunks.0.length是否 ≥ 200(排除封面页、空白页);
  • true_edge和false_edge指定不同下游节点,实现真正的“条件分支”。

3.3 Parallel 节点:并行调用多个模型,榨干响应速度与准确性

单一模型可能漏提字段。例如gpt-3.5-turbo对金额敏感但易错乙方名称,claude-3-haiku擅长名称识别但金额格式混乱。Parallel节点可同时调用两者,再用Merge节点融合结果。

# 工作流片段:并行调用两个模型 { "nodes": [ // ... input, parser, condition 节点省略 ... { "id": "parallel-1", "data": { "type": "parallel", "branches": [ { "id": "branch-gpt", "nodes": [ { "id": "llm-gpt", "data": {"type": "llm", "model_id": "gpt-3.5-turbo", "prompt_template": "专注提取金额和违约金:{{parser-1.chunks.0}}", "output_schema": "{\"amount\":\"string\",\"penalty\":\"string\"}"} } ] }, { "id": "branch-claude", "nodes": [ { "id": "llm-claude", "data": {"type": "llm", "model_id": "claude-3-haiku-20240307", "prompt_template": "专注提取甲乙双方名称:{{parser-1.chunks.0}}", "output_schema": "{\"party_a\":\"string\",\"party_b\":\"string\"}"} } ] } ] }, "position": [500, 100] }, { "id": "merge-1", "data": { "type": "merge", "strategy": "override", // 后执行分支覆盖前分支同名字段 "sources": ["parallel-1.branch-gpt.llm-gpt", "parallel-1.branch-claude.llm-claude"] }, "position": [700, 100] } ], "edges": [ // ... 连接逻辑 ... ] }

Parallel节点的价值在于:

  • 提速:两个 LLM 请求并发发出,总耗时 ≈ max(单次耗时),非累加;
  • 提准:不同模型优势互补,merge-1的override策略确保amount来自 GPT、party_a来自 Claude,各司其职;
  • 注意:sources字段必须用完整节点路径(parallel-1.branch-gpt.llm-gpt),这是 Dify 工作流变量引用的唯一合法语法,错一个字符就报Variable not found。

4. 避坑指南:工作流上线前必须排查的 5 个血泪经验

工作流在调试面板里跑通,不等于能进生产。以下是我在某跨平台系统集成中踩过的坑,每一条都附带现象、根因和可立即执行的修复命令。

4.1 现象:工作流运行日志显示Node execution timeout,但 LLM API 实际已返回

原因:Dify 默认节点超时时间为 60 秒,而某些大模型(如gpt-4-turbo处理 10 页 PDF)可能达 75 秒。超时后 Dify 强制终止,但模型仍在后台生成,造成“请求已发、结果丢失”。
解决:修改工作流节点级超时,而非全局配置。在节点data中添加timeout字段:

"data": { "type": "llm", "model_id": "gpt-4-turbo", "timeout": 120, // 单位:秒,必须为整数 ... }

4.2 现象:Document Parser解析 PDF 后,chunks数组为空,但日志无报错

原因:PDF 文件 URL 未设置Content-Type: application/pdf响应头,Dify 的解析器拒绝处理“类型不明”的二进制流。常见于 Nginx 反向代理未配置 MIME 类型。
解决:检查文件 URL 的响应头。若为代理服务,Nginx 配置需加:

location ~* \.pdf$ { add_header Content-Type application/pdf; # 其他 proxy_pass 配置... }

或改用 Base64 编码上传(绕过 URL 限制):

FILE_BASE64=$(base64 -w 0 test.pdf) curl -X POST "http://localhost:5001/v1/workflows/run" \ -H "Authorization: Bearer $API_KEY" \ -d "{\"inputs\":{\"file_url\":\"data:application/pdf;base64,$FILE_BASE64\"}}"

4.3 现象:Condition节点始终走false_edge,即使parser-1.chunks明明有内容

原因:Condition的variable字段不支持嵌套数组索引(如parser-1.chunks.0),只接受顶层变量或一级属性。parser-1.chunks是数组,直接比较is_not_empty有效,但parser-1.chunks.0.length会解析失败。
解决:用Array Length节点前置计算:

{ "id": "length-1", "data": {"type": "array_length", "array_variable": "parser-1.chunks"}, "position": [450, 100] }, { "id": "condition-1", "data": { "type": "condition", "conditions": [{"variable": "length-1.length", "operator": ">=", "value": "1"}], "true_edge": "llm-1" } }

4.4 现象:output_schema设为{"amount": "number"},但 LLM 返回"1000000"(字符串),Dify 不报错也不转换

原因:Dify 的output_schema仅做 JSON Schema 校验(验证字段存在、类型匹配),不执行类型转换。"number"要求原始输出是数字字面量(1000000),而非字符串。
解决:在提示词中强制要求数字格式,并用output_schema严格约束:

"prompt_template": "返回 JSON,金额字段必须是数字(无引号、无逗号):{... \"amount\": 1000000 ...}", "output_schema": "{\"amount\": \"number\"}"

或接受字符串,在下游用Code节点转换:

{ "id": "code-1", "data": { "type": "code", "code": "return {\"amount\": Number(inputs['llm-1'].amount) || 0};" } }

4.5 现象:工作流在 Web UI 中编辑后保存,API 调用却仍用旧版本

原因:Dify 工作流有draft(草稿)和published(发布)两个状态。Web UI 编辑的是草稿,必须点击“发布”按钮才生效。API 默认调用published版本。
解决:两种方式任选其一:

  • Web UI 中编辑完,务必点击右上角Publish按钮;
  • 或 API 调用时指定version参数:
curl -X POST "http://localhost:5001/v1/workflows/run?version=draft" \ -H "Authorization: Bearer $API_KEY" \ -d '{"inputs":{...}}'

5. 生产就绪技巧:用 Webhook + 自定义节点 + 日志审计打造可追踪、可回滚的工作流体系

工作流跑通只是起点,生产环境要求:谁在何时触发了什么操作、输入输出是否可追溯、出错时能否一键重放。Dify 原生支持 Webhook 和自定义节点,结合简单的日志审计,就能构建企业级可观测性。

5.1 用 Webhook 节点实现全链路事件通知

每个关键节点执行后,向内部监控系统推送事件。例如在llm-1后加 Webhook,上报输入文本哈希、模型耗时、输出字段:

{ "id": "webhook-1", "data": { "type": "webhook", "url": "https://your-monitoring-api.com/dify-event", "method": "POST", "headers": {"Authorization": "Bearer your-monitor-key"}, "body": { "event": "contract_parse_success", "app_id": "{{app.id}}", "workflow_id": "{{workflow.id}}", "input_hash": "{{md5(inputs.input_text)}}", "output_amount": "{{llm-1.amount}}", "latency_ms": "{{llm-1.latency}}" } }, "position": [900, 100] }

{{md5(...)}}和{{llm-1.latency}}是 Dify 内置的表达式函数,无需额外编码。latency字段由 Dify 自动注入,精度到毫秒,是性能分析黄金指标。

5.2 用 Custom Code 节点接入私有规则引擎

当 LLM 输出需经业务规则校验(如“违约金不能超过合同金额的20%”),用Code节点嵌入 Python 逻辑:

{ "id": "code-validate", "data": { "type": "code", "language": "python3", "code": "import json\nresult = json.loads(inputs['llm-1'])\nif result.get('amount') and result.get('penalty'):\n amount_num = float(result['amount'].replace(',', '').replace('¥', ''))\n penalty_num = float(result['penalty'].replace('%', '')) / 100 * amount_num\n if penalty_num > amount_num * 0.2:\n result['validation_error'] = '违约金超标'\nreturn result" } }

此代码:

  • 自动清洗金额字符串(去逗号、去货币符号);
  • 计算违约金数值并与上限比对;
  • 若超标,注入validation_error字段,供下游Condition节点拦截;
  • 所有逻辑在 Dify 容器内执行,无需外调服务,延迟可控。

5.3 日志审计:用 SQL 直查工作流执行记录

Dify 将每次执行存入workflow_runs表。在 SQLite 中执行:

-- 查看最近 10 次合同解析的输入输出与耗时 SELECT id, app_id, inputs, outputs, status, elapsed_time AS latency_ms, created_at FROM workflow_runs WHERE app_id = 'your-app-id' AND created_at > datetime('now', '-7 days') ORDER BY created_at DESC LIMIT 10;

关键字段解读:

  • inputs/outputs:JSON 字符串,可直接json_extract解析;
  • elapsed_time:精确到毫秒的端到端耗时,含解析、LLM、后处理全链路;
  • status:succeeded/failed/stopped,失败时outputs字段含错误堆栈。

提示:生产环境建议将 SQLite 换为 PostgreSQL,并开启log_statement = 'all',配合 pg_stat_statements 扩展做慢查询分析。

我习惯在每个新工作流上线前,用这三条技巧加固:

  1. 加 Webhook,让每一次调用都成为监控系统的数据源;
  2. 关键校验用Code节点,把业务规则锁死在工作流内,避免 API 层补丁越打越多;
  3. 每周五执行一次SELECT * FROM workflow_runs WHERE status = 'failed',人工抽检失败案例,持续优化提示词和output_schema。
    这比写一堆文档更管用——工作流自己会说话。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 3:39:49

Unity编辑器可点击搜索框:放大镜交互实现指南

1. 项目概述&#xff1a;为什么一个“能点的放大镜”值得专门做一篇深度解析&#xff1f;在Unity编辑器里敲下CtrlShiftF&#xff0c;弹出那个熟悉的搜索框——你有没有注意过&#xff0c;右下角那个小小的放大镜图标&#xff0c;它只是个装饰&#xff1f;点击它毫无反应&#…

作者头像 李华
网站建设 2026/10/14 3:38:54

工业级人脸识别签到系统:从检测对齐到状态机去重的端到端实践

简介&#xff1a;本资源是一套完整的基于深度学习的人脸识别签到系统源码&#xff0c;面向人工智能初学者、课程设计与毕业设计学生&#xff0c;解决课堂/企业场景中传统签到效率低、代签难监管等实际问题。项目采用DlibResNet深度特征提取方案&#xff0c;集成用户管理、实时人…

作者头像 李华
网站建设 2026/10/14 3:38:49

TradingView Charting Library v28.5 深度集成指南

简介&#xff1a;charting-library-master-v28.5 是 TradingView 官方图表库 v28.5 版本的完整前端资源包&#xff0c;专为金融量化开发、Web 交易终端定制及高级 K 线/技术指标可视化需求设计&#xff0c;适用于中高级前端工程师与量化系统集成开发者。资源共1247个文件&#…

作者头像 李华
网站建设 2026/10/14 3:38:42

Akamai sensor_data解析与策略编译全流程实战

简介&#xff1a;本资源是一份面向爬虫工程师与逆向开发者的Akamai反爬对抗实战代码包&#xff0c;聚焦于sensor_data 3.0加密参数的生成与验证全流程&#xff0c;解决海外网站&#xff08;如Akamai防护站点&#xff09;中_abck、ak_bmsc等关键指纹参数难以复现的核心难题。压缩…

作者头像 李华
网站建设 2026/10/14 3:37:45

如何用MyBatis Generator插件自动生成Service和Controller

做后端开发的朋友&#xff0c;十有八九都在项目里用过 MyBatis Generator。这东西确实省事&#xff0c;数据库表建好&#xff0c;一条命令跑完&#xff0c;实体类、Mapper 接口和 XML 映射文件全都有了&#xff0c;比手写强太多。但它默认也就生成到 Mapper 这一层&#xff0c;…

作者头像 李华