Add call session store with close API and TTL cleanup.

Support immediate in-memory release via POST /api/v1/calls/{callId}/close while keeping scheduled TTL as a fallback for unclosed sessions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xin Wang
2026-07-15 21:43:58 +08:00
parent 31845f7796
commit ed703d6f39
12 changed files with 567 additions and 13 deletions

View File

@@ -51,6 +51,7 @@
- 保存成功后重建 AC 自动机并立即生效
- 提供单页面配置与测试
- JVM 内存保存通话状态
- 提供通话主动关闭接口
- TTL 自动清理过期会话
- 单实例部署
@@ -64,7 +65,6 @@
- Dify 实时确认
- WebSocket 或 SSE
- 通话状态查询接口
- 通话关闭接口
- 独立 `eventId`
- 多实例部署
- 复杂草稿、审核和发布流程
@@ -75,7 +75,7 @@
### 3.1 实时检测接口
只保留一个实时业务接口:
实时检测业务接口:
```http
POST /api/v1/asr-events
@@ -104,18 +104,21 @@ GET /api/v1/calls/{callId}/state
- 本次新增提醒:`newAlerts`
- 当前通话累计结果:`currentResults`
### 3.3 提供通话关闭接口
### 3.3 提供通话关闭接口
不提供
通话结束时,前端应主动关闭会话,立即释放内存中的通话状态
```http
POST /api/v1/calls/{callId}/close
```
同时保留 TTL 定时清理,作为兜底机制,处理前端未调用 close、进程异常退出等遗漏场景。
约束条件:
- 每通电话的 `callId` 必须唯一
- 通话状态通过 TTL 自动清理
- 正常结束路径优先调用 close
- 未关闭或异常遗留的会话由 TTL 自动清理
### 3.4 保留每通电话独立锁
@@ -261,15 +264,18 @@ demo/
│ │ │
│ │ ├── api/
│ │ │ ├── AsrEventController.java
│ │ │ ├── CallController.java
│ │ │ └── RuleAdminController.java
│ │ │
│ │ ├── application/
│ │ │ ├── AsrEventMonitorService.java
│ │ │ ├── CallSessionService.java
│ │ │ └── RuleManagementService.java
│ │ │
│ │ ├── domain/
│ │ │ ├── AsrFinalEventRequest.java
│ │ │ ├── MonitorResponse.java
│ │ │ ├── CloseCallResponse.java
│ │ │ ├── MatchResult.java
│ │ │ ├── AlertResult.java
│ │ │ ├── RuleDocument.java
@@ -758,9 +764,76 @@ try {
---
## 15. 会话 TTL 清理
## 15. 会话关闭与 TTL 清理
由于不提供 close 接口,会话状态依靠 TTL 清理。
会话释放采用 **主动关闭 + TTL 兜底** 双路径:
```text
通话结束
→ 优先POST /api/v1/calls/{callId}/close立即释放
→ 兜底TTL 定时任务清理遗漏会话
```
### 15.1 通话关闭接口
```http
POST /api/v1/calls/{callId}/close
```
响应示例:
```json
{
"callId": "call-1001",
"closed": true,
"existed": true
}
```
| 字段 | 说明 |
|---|---|
| `callId` | 被关闭的通话 ID |
| `closed` | 本次调用是否完成关闭处理(接口幂等,恒为 `true` |
| `existed` | 关闭前会话是否存在;重复关闭或不存在时为 `false` |
处理流程:
```text
获取 callId 独立锁
→ 删除 CallSession
→ 删除对应 ReentrantLock
→ 返回 closed=true
```
语义约定:
- 接口幂等:同一 `callId` 多次 close 不报错
- 通话结束后应尽快调用,避免会话长期占用内存
- 由于 `callId` 必须全局唯一,关闭后不应再复用同一 `callId` 提交 ASR Final
服务层示意:
```java
public CloseCallResponse close(String callId) {
ReentrantLock lock = sessionStore.getLock(callId);
lock.lock();
try {
boolean existed = sessionStore.remove(callId);
sessionStore.removeLock(callId);
return new CloseCallResponse(callId, true, existed);
} finally {
lock.unlock();
}
}
```
### 15.2 TTL 兜底清理
即使提供 close仍保留 TTL用于清理以下遗留会话
- 前端未调用 close
- 页面异常关闭、网络故障导致 close 未送达
- 进程重启前未及时释放的会话单实例内存方案下重启后本就为空TTL 主要覆盖运行期遗漏)
```yaml
monitor:
@@ -784,11 +857,13 @@ public void cleanupExpiredSessions() {
}
```
清理时同删除:
TTL 清理时同删除:
- `CallSession`
- 对应的 `ReentrantLock`
close 与 TTL 互不冲突:已 close 的会话不存在TTL 跳过;未 close 的会话到期后由 TTL 删除。
---
## 16. 规则数据模型
@@ -1306,11 +1381,12 @@ java \
8. `EMS``ems``E M S` 均可命中
9. 相邻 Final 拼接后可命中被切分的品牌
10. 不同通话状态相互隔离
11. TTL 能清理过期会话
12. 保存新规则后 Demo 接口立即使用新版本
13. 保存失败时旧规则继续有效
14. 两个管理页面同时编辑时能够检测版本冲突
15. Actuator 健康检查正常
11. close 能立即释放通话会话,重复 close 幂等
12. TTL 能清理未 close 的过期会话
13. 保存新规则后 Demo 接口立即使用新版本
14. 保存失败时旧规则继续有效
15. 两个管理页面同时编辑时能够检测版本冲突
16. Actuator 健康检查正常
---
@@ -1414,11 +1490,19 @@ Demo 页面立即验证
```text
POST /api/v1/asr-events
POST /api/v1/calls/{callId}/close
GET /api/v1/admin/rules
PUT /api/v1/admin/rules
POST /api/v1/admin/rules/test
```
会话清理:
```text
通话结束 → close 立即释放
遗漏会话 → TTL 定时兜底清理
```
最终采用:
```text