上一篇文章工单创建了,但存在
ConcurrentHashMap里——服务一重启,所有工单消失。这篇文章把工单落到数据库,加上状态流转(待处理→处理中→已完成),让 Agent 的工单真正「留下来、管起来」。
问题:内存工单的致命缺陷
上一版的 WorkOrderTool 用 ConcurrentHashMap 存工单:
private final ConcurrentHashMap<String, WorkOrder> orders = new ConcurrentHashMap<>();
三个问题:
- 重启丢失——服务重启后所有工单消失
- 不可查询历史——不能查「上月有多少工单」
- 没有状态流转——工单创建后永远是 PENDING,无法标记完成
产线上工单需要的是:持久存储 + 状态流转 + 可审计。
方案:嵌入式 H2 + 工单生命周期
不引入 MySQL/PostgreSQL——用一个嵌入式 H2 数据库,零配置,随应用启停。
工单生命周期:
PENDING(待处理)→ IN_PROGRESS(处理中)→ COMPLETED(已完成)→ CLOSED(已关闭)
↓
CANCELLED(已取消)
只有两个方向:正常流转(→)和取消(↓)。
实现
1. 数据库表
CREATE TABLE work_orders (
id VARCHAR(20) PRIMARY KEY,
device_id VARCHAR(64) NOT NULL,
type VARCHAR(20) NOT NULL, -- 维修/检查/保养
priority VARCHAR(10) NOT NULL, -- HIGH/MEDIUM/LOW
description TEXT,
assignee VARCHAR(64) DEFAULT '值班工程师',
status VARCHAR(20) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP,
closed_at TIMESTAMP
);
2. WorkOrderTool 改造成持久化版本
@Component
public class WorkOrderTool {
private final JdbcTemplate jdbc;
@Tool("创建维修工单。仅当确认设备存在硬件故障需要人工介入时使用。")
public String createWorkOrder(
@P("设备ID") String deviceId,
@P("工单类型:维修/检查/保养") String type,
@P("优先级:HIGH/MEDIUM/LOW") String priority,
@P("故障描述和诊断结论") String description) {
String id = "WO-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
jdbc.update(
"INSERT INTO work_orders (id, device_id, type, priority, description) VALUES (?,?,?,?,?)",
id, deviceId, type, priority, description
);
return findById(id).toString(); // 返回完整工单 JSON
}
@Tool("将工单状态更新为 IN_PROGRESS(开始处理)")
public String startWorkOrder(@P("工单编号") String workOrderId) {
jdbc.update("UPDATE work_orders SET status='IN_PROGRESS', updated_at=NOW() WHERE id=?",
workOrderId);
return "工单 " + workOrderId + " 已开始处理";
}
@Tool("将工单状态更新为 COMPLETED(已完成)")
public String completeWorkOrder(@P("工单编号") String workOrderId) {
jdbc.update(
"UPDATE work_orders SET status='COMPLETED', completed_at=NOW(), updated_at=NOW() WHERE id=?",
workOrderId);
return "工单 " + workOrderId + " 已标记完成";
}
public WorkOrder findById(String id) {
return jdbc.queryForObject(
"SELECT * FROM work_orders WHERE id=?",
new BeanPropertyRowMapper<>(WorkOrder.class), id
);
}
public List<WorkOrder> findByDevice(String deviceId) {
return jdbc.query(
"SELECT * FROM work_orders WHERE device_id=? ORDER BY created_at DESC",
new BeanPropertyRowMapper<>(WorkOrder.class), deviceId
);
}
}
3. 新增两个 Tool 方法注册到 Agent
startWorkOrder 和 completeWorkOrder 作为 @Tool 注册到 DeviceAgent,LLM 可以在对话中直接更新工单状态:
用户:「CNC-001 的维修完成了,关闭工单」
Agent:调用 completeWorkOrder("WO-4BCECB3A") → 工单状态 → COMPLETED
4. 新增 API 端点
// 查询设备的所有历史工单
@GetMapping("/workorder/device/{deviceId}")
public ResponseEntity<List<WorkOrder>> listByDevice(@PathVariable String deviceId)
// 工单统计
@GetMapping("/workorder/stats")
public ResponseEntity<Map<String, Object>> stats()
// 返回:{ total: 15, pending: 3, inProgress: 5, completed: 7 }
状态流转规则
| 当前状态 | 可流转到 | 说明 |
|---|---|---|
| PENDING | IN_PROGRESS | 工程师接单 |
| PENDING | CANCELLED | 误报或无需处理 |
| IN_PROGRESS | COMPLETED | 维修完成 |
| IN_PROGRESS | CANCELLED | 维修无法执行 |
| COMPLETED | CLOSED | 管理员审核关闭 |
状态变更由 updated_at 自动记录,completed_at / closed_at 单独保留时间戳,方便后续统计分析。
端到端效果
# 1. Agent 诊断并创建工单
curl -X POST http://localhost:8080/api/agent/chat \
-H "Content-Type: application/json" \
-d '{"message":"CNC-001 振动异常,诊断并创建工单"}'
# → WO-8F3A2B1C 已创建,状态 PENDING
# 2. 工程师开始处理
curl -X POST http://localhost:8080/api/agent/chat \
-H "Content-Type: application/json" \
-d '{"message":"我接单了 WO-8F3A2B1C,开始维修"}'
# → 工单状态 → IN_PROGRESS
# 3. 维修完成
curl -X POST http://localhost:8080/api/agent/chat \
-H "Content-Type: application/json" \
-d '{"message":"WO-8F3A2B1C 已修好,关闭工单"}'
# → 工单状态 → COMPLETED
# 4. 查询 CNC-001 的历史工单
curl http://localhost:8080/api/agent/workorder/device/CNC-001
# → 3 条工单记录,含状态和时间戳
为什么不用 MySQL/PostgreSQL?
H2 的选择理由:
- 零配置——不需要额外的 Docker 容器,pom.xml 加一个依赖即可
- 嵌入式——随 Java 进程启停,本地开发无额外负担
- 兼容性好——标准 SQL,后续迁移到 MySQL/PostgreSQL 只需改连接串
- 够用——工单量不大(日几十条),H2 完全胜任
如果生产环境已经是 Spring Boot + MySQL,把 H2 换成 MySQL 只需要改 application.yml 中的一行 URL。
代码结构变化
agent/tools/
├── WorkOrderTool.java — 改造:HashMap → JdbcTemplate,新增状态流转方法
agent/model/
└── WorkOrder.java — 新增 updated_at / completed_at / closed_at 字段
controller/
└── AgentController.java — 新增 /workorder/device/{id} 和 /workorder/stats
resources/
├── application.yml — 新增 H2 数据源配置
└── schema.sql — 工单表 DDL(Spring Boot 自动执行)
下一步
工单持久化后,整套系统闭环了——设备数据采集(MQTT→TDEngine)→ Agent 诊断(RAG + CoT)→ 工单生成(WorkOrderTool)→ 工单管理(持久化 + 状态流转)。
下一篇文章从整体回顾这个系统的架构设计,总结「工业 Agent 的六大设计原则」。
本文由 LaoLiang 原创,首发于掘金/知乎/微信公众号。转载请联系作者。