第一种方案(推荐)
在归档节点前添加自定义附加操作就可以(oa前台直接调用外部接口即可)
第二种方案(不推荐,用来扩展思路)数据库轮询版
一、数据库轮询版-整体思路
核心思想(非常重要)
不要直接扫"已结束流程"
而是:
👉 "找出『刚刚结束』、且『还没通知过』的流程实例"
所以一定要有 去重标记。
总体架构
markdown
Spring Boot 定时任务
↓
查询 OA 数据库
↓
找出【厂外调拨流程】中
【审批完成 & 未通知】的 requestId
↓
发送飞书群通知
↓
记录通知日志(防重复)
二、你必须知道的 3 张关键表(e-cology 9)
⚠️ 表名在不同客户可能略有差异,但 下面这几张 90% 都存在
1️⃣ workflow_requestbase(流程实例主表)
这是最重要的。
常用字段:
| 字段 | 含义 |
|---|---|
| requestid | 流程实例 ID |
| workflowid | 流程模板 ID |
| currentstatus | 流程状态 |
| lastoperatedate | 最后操作日期 |
| lastoperatetime | 最后操作时间 |
currentstatus 常见值(经验)
| 值 | 含义 |
|---|---|
| 0 | 运行中 |
| 1 | 正常结束(审批通过) |
| 2 | 强制结束 |
| 3 | 退回 |
👉 你只要 currentstatus = 1
2️⃣ workflow_base(流程模板表)
用来确认流程名(可选)
| 字段 | 含义 |
|---|---|
| id | workflowId |
| workflowname | 流程名称 |
3️⃣ 你自己建的表(强烈推荐)
oa_notify_log(防重复)
sql
create table oa_notify_log (
request_id int primary key,
workflow_id int,
notify_time datetime
);
❗ 这是整个方案是否"能长期用"的关键
三、SQL:找出「刚刚审批完成」的流程
核心 SQL(重点看)
sql
select r.requestid,
r.workflowid,
r.lastoperatedate,
r.lastoperatetime
from workflow_requestbase r
where r.workflowid = 456 -- 厂外调拨流程ID
and r.currentstatus = '1' -- 审批通过
and not exists (
select 1
from oa_notify_log n
where n.request_id = r.requestid
)
📌 解释:
- 只查你关心的流程
- 只要"审批通过"的
- 排除已经通知过的
四、Spring Boot 3 实现(完整可用示例)
1️⃣ 定时任务配置
less
@EnableScheduling
@Configuration
public class ScheduleConfig {
}
2️⃣ 定时扫描任务
typescript
@Component
public class OaWorkflowScanJob {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private FeishuService feishuService;
@Scheduled(cron = "0 */1 * * * ?") // 每 1 分钟扫一次
public void scanFinishedWorkflow() {
String sql = """
select requestid
from workflow_requestbase
where workflowid = ?
and currentstatus = '1'
and not exists (
select 1 from oa_notify_log
where request_id = workflow_requestbase.requestid
)
""";
List<Integer> requestIds =
jdbcTemplate.queryForList(sql, Integer.class, 456);
for (Integer requestId : requestIds) {
try {
// 1. 发送飞书
feishuService.send(buildMsg(requestId));
// 2. 记录已通知
saveNotifyLog(requestId);
} catch (Exception e) {
// 失败不写 log,下次还会再发
e.printStackTrace();
}
}
}
private void saveNotifyLog(Integer requestId) {
jdbcTemplate.update(
"insert into oa_notify_log(request_id, workflow_id, notify_time) values (?, ?, getdate())",
requestId, 456
);
}
private String buildMsg(Integer requestId) {
return "【厂外调拨流程】审批已通过\n流程ID:" + requestId;
}
}
3️⃣ 飞书发送(简版)
typescript
@Service
public class FeishuService {
private static final String WEBHOOK =
"https://open.feishu.cn/open-apis/bot/v2/hook/xxxx";
private final RestTemplate restTemplate = new RestTemplate();
public void send(String text) {
Map<String, Object> body = Map.of(
"msg_type", "text",
"content", Map.of("text", text)
);
restTemplate.postForObject(WEBHOOK, body, String.class);
}
}
五、你这个方案一定要加的 4 个"保险"
✅ 1️⃣ 必须用 oa_notify_log
否则 一定会重复发
✅ 2️⃣ 定时频率不要太高
- 推荐:1~3 分钟
- 不要秒级
✅ 3️⃣ 只认 currentstatus = 1
不要自己猜其他状态
✅ 4️⃣ 飞书失败不要写 log
让下次定时任务补发
六、这个方案的"客观缺点"(我必须告诉你)
| 点 | 说明 |
|---|---|
| 延迟 | 最快 1 分钟 |
| 强制结束要额外判断 | currentstatus = 2 |
| 表结构升级风险 | 有 |
| 并发高时压力大 | 有 |
👉 但在"不能用 Action"的前提下,这是最稳的一种