Dify + Spring Boot 实战:请假申请分支完整实现指南
Dify + Spring Boot 实战:请假申请分支完整实现指南
1. 项目背景与目标
在企业智能助手系列的第二部分,我们继续完善员工自助服务助手,为其添加请假申请功能。用户只需通过自然语言说出请假需求,助手就能自动解析信息、调用后端接口、提交申请并反馈结果。
本文核心内容:
- 使用 LLM 从自然语言中提取结构化信息(JSON)
- 处理模型输出中的推理标签(
<think>)和换行符 - 使用代码执行节点清洗数据并判断信息完整性
- HTTP POST 请求提交请假申请
- 条件分支处理信息缺失场景
2. 整体流程设计
请假申请分支的完整流程如下:
text
用户输入 → 意图识别 → 请假申请分支
↓
提取请假信息(LLM) → 输出带标签的 JSON
↓
代码执行节点(清洗 + 解析 + 缺失判断)
↓
条件分支(missing_fields)
├─ true → 直接回复(反问缺失信息)
└─ false → HTTP POST → 回答 LLM → 结束
3. 后端接口准备(Java Spring Boot)
3.1 数据库表
sql
CREATE DATABASE employee_db;
USE employee_db;
CREATE TABLE leave_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id VARCHAR(20) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
reason VARCHAR(255),
status VARCHAR(20) DEFAULT 'pending'
);
3.2 实体类 LeaveRequest.java
java
package com.example.employee.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "leave_requests")
@Data
public class LeaveRequest {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "employee_id")
private String employeeId;
@Column(name = "start_date")
private LocalDate startDate;
@Column(name = "end_date")
private LocalDate endDate;
private String reason;
private String status;
}
3.3 DTO LeaveRequestDto.java
java
package com.example.employee.dto;
import lombok.Data;
import java.time.LocalDate;
@Data
public class LeaveRequestDto {
private String employeeId;
private LocalDate startDate;
private LocalDate endDate;
private String reason;
}
3.4 Repository LeaveRepository.java
java
package com.example.employee.repository;
import com.example.employee.entity.LeaveRequest;
import org.springframework.data.jpa.repository.JpaRepository;
public interface LeaveRepository extends JpaRepository<LeaveRequest, Long> {
}
3.5 Service LeaveService.java
java
package com.example.employee.service;
import com.example.employee.dto.LeaveRequestDto;
import com.example.employee.entity.LeaveRequest;
import com.example.employee.repository.LeaveRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LeaveService {
@Autowired
private LeaveRepository repository;
public LeaveRequest submitLeave(LeaveRequestDto dto) {
LeaveRequest leave = new LeaveRequest();
leave.setEmployeeId(dto.getEmployeeId());
leave.setStartDate(dto.getStartDate());
leave.setEndDate(dto.getEndDate());
leave.setReason(dto.getReason());
leave.setStatus("pending");
return repository.save(leave);
}
}
3.6 Controller LeaveController.java
java
package com.example.employee.controller;
import com.example.employee.dto.LeaveRequestDto;
import com.example.employee.entity.LeaveRequest;
import com.example.employee.service.LeaveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/leave")
public class LeaveController {
@Autowired
private LeaveService leaveService;
@PostMapping
public LeaveRequest submitLeave(@RequestBody LeaveRequestDto dto) {
return leaveService.submitLeave(dto);
}
}
3.7 部署与测试
-
打包:
mvn clean package -
上传 jar 到服务器
-
启动:
nohup java -jar employee-assistant-backend-1.0-SNAPSHOT.jar > app.log 2>&1 & -
测试接口:
bash
curl -X POST http://你的IP:8081/api/leave \ -H "Content-Type: application/json" \ -d '{"employeeId":"10001","startDate":"2026-04-01","endDate":"2026-04-03","reason":"年假"}'返回示例:
json
{"id":3,"employeeId":"10001","startDate":"2026-04-01","endDate":"2026-04-03","reason":"年假","status":"pending"}
4. Dify Chatflow 请假申请分支配置
4.1 意图识别(复用之前的 LLM)
确保意图识别输出 leave_apply,条件分支已配置。
4.2 提取请假信息(LLM 节点)
节点名称:提取请假信息
系统提示:
text
从用户输入中提取请假信息,输出 JSON 格式,字段如下:
- employeeId: 员工工号(如果没有,填 "MISSING")
- startDate: 开始日期(格式 YYYY-MM-DD,如果没有,填 "MISSING")
- endDate: 结束日期(格式 YYYY-MM-DD,如果没有,填 "MISSING")
- reason: 请假原因(如果没有,填 "MISSING")
示例输出:
{"employeeId":"10001","startDate":"2026-04-01","endDate":"2026-04-03","reason":"年假"}
用户输入:{{sys.query}}
温度:0.1
输出:text
4.3 清洗与解析(代码执行节点 - JavaScript)
由于部分模型(如 DeepSeek R1)会在输出中添加 <think>...</think> 推理标签,需要清洗后提取 JSON。
输入变量:
input→{{提取请假信息.text}}
输出变量:
| 变量名 | 类型 |
|---|---|
leave_data |
Object |
missing_fields |
Boolean |
JavaScript 代码:
javascript
function main(input) {
// 1. 确保输入是字符串
let text = '';
if (typeof input === 'string') {
text = input;
} else if (input && typeof input === 'object') {
text = input.text || JSON.stringify(input);
} else {
text = String(input);
}
// 2. 去除 <think>...</think> 部分
const thinkEnd = text.indexOf('</think>');
if (thinkEnd !== -1) {
text = text.substring(thinkEnd + 8);
}
// 3. 去除首尾空白
text = text.trim();
// 4. 提取 JSON 部分(找到第一个 { 和最后一个 })
const firstBrace = text.indexOf('{');
const lastBrace = text.lastIndexOf('}');
if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
text = text.substring(firstBrace, lastBrace + 1);
}
// 5. 再次清理空白
text = text.trim();
// 6. 尝试解析 JSON
try {
const obj = JSON.parse(text);
// 检查是否有缺失字段
const hasMissing = !obj.employeeId || obj.employeeId === 'MISSING' ||
!obj.startDate || obj.startDate === 'MISSING' ||
!obj.endDate || obj.endDate === 'MISSING' ||
!obj.reason || obj.reason === 'MISSING';
if (hasMissing) {
return { leave_data: null, missing_fields: true };
}
return { leave_data: obj, missing_fields: false };
} catch(e) {
// 解析失败,返回缺失标识
return { leave_data: null, missing_fields: true };
}
}
4.4 条件分支节点
条件:{{代码执行节点.missing_fields}} 等于 true
- 是(信息缺失):进入直接回复节点,内容:“请提供完整的请假信息,包括:员工工号、开始日期(YYYY-MM-DD)、结束日期(YYYY-MM-DD)、请假原因。”
- 否(信息完整):进入 HTTP 请求节点
4.5 HTTP 请求节点(POST)
| 配置项 | 值 |
|---|---|
| URL | http://你的IP:8081/api/leave |
| Method | POST |
| Body Type | JSON |
| Body Content | {{代码执行节点.leave_data}} |
| Headers | Content-Type 自动添加 |
输出变量:body(后端返回的 JSON)
4.6 回答生成(LLM 节点)
系统提示:
text
你是一个企业助手。根据以下请假申请结果,用友好、亲切的语气告知用户。
结果:{{HTTP请求节点.body}}
要求:
- 如果返回结果中包含 id 和 status,说明提交成功,告知用户申请已提交,状态为“待审批”。
- 如果返回错误信息(如 status>=400),则提示失败原因。
- 最后可加一句:“审批通过后您将收到通知。”
字段说明:
- id: 申请编号
- status: pending(待审批)
用户消息:{{sys.query}}(可选)
温度:0.1
4.7 结束节点
将直接回复节点和回答 LLM 节点都连接到结束节点。
5. 关键知识点总结
5.1 处理模型推理标签
部分模型(如 DeepSeek R1)会在输出中添加 <think>...</think> 标签。解决方案:
- 在代码执行节点中截取
</think>之后的内容 - 提取 JSON 部分(定位第一个
{和最后一个})
5.2 信息完整性判断
- 让 LLM 在缺失字段时输出
"MISSING" - 代码执行节点解析后检查字段值
- 输出布尔值
missing_fields供条件分支使用
5.3 避免 JSON 双重转义
- 代码执行节点返回对象(
leave_data: obj) - HTTP 节点选择 JSON 模式,直接引用对象
- 不要将对象手动
JSON.stringify()
5.4 条件分支使用布尔类型
- 布尔类型在条件分支中直接判断
等于 true/false - 避免使用字符串包含判断,更安全可靠
6. 常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
输出仍包含 <think> 标签 |
清洗逻辑不完整 | 截取 </think> 之后,提取 {} 部分 |
leave_data: null |
JSON 解析失败 | 增强清洗逻辑,添加错误日志调试 |
Invalid actual value type: string or array |
条件分支用了对象变量 | 改用布尔变量 missing_fields |
| 请求体出现反斜杠 | 二次序列化 | 代码节点返回对象,HTTP 用 JSON 模式 |
| HTTP 返回 400 | JSON 字段名不匹配 | 确保字段名与后端 DTO 一致 |
7. 测试用例
7.1 完整信息
输入:工号10001,请2026年4月1日到4月3日年假
期望:提交成功,返回申请编号和待审批状态
7.2 缺失工号
输入:请2026年4月1日到4月3日年假
期望:反问“请提供完整的请假信息…”
7.3 缺失日期
输入:工号10001,请年假
期望:反问“请提供完整的请假信息…”
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)