- Add one-click research protocol generation with streaming output - Implement Word document export via Pandoc integration - Add dynamic dual-panel layout with resizable split pane - Implement collapsible content for StatePanel stages - Add conversation history management with title auto-update - Fix scroll behavior, markdown rendering, and UI layout issues - Simplify conversation creation logic for reliability
670 lines
15 KiB
Markdown
670 lines
15 KiB
Markdown
# Day 2 - REDCap 实时集成开发完成记录
|
||
|
||
> **开发日期**: 2026-01-02
|
||
> **开发者**: AI Assistant + 用户
|
||
> **文档版本**: v1.0
|
||
> **开发阶段**: Day 2 - REDCap对接与实时同步
|
||
|
||
---
|
||
|
||
## 📋 开发概述
|
||
|
||
Day 2的核心目标是实现 **IIT Manager Agent 与 REDCap 的实时数据集成**,采用 REDCap 原生的 **Data Entry Trigger (DET)** + **REST API** 技术方案,实现零延迟的数据同步和双向通信。
|
||
|
||
### 核心价值
|
||
|
||
1. ✅ **实时性**: Webhook响应时间<10ms,数据录入后立即触发
|
||
2. ✅ **可靠性**: DET + 定时轮询双保险机制
|
||
3. ✅ **云原生**: 完全基于Postgres-Only架构,使用pg-boss队列
|
||
4. ✅ **标准化**: 符合团队开发规范(队列名称、Worker注册等)
|
||
|
||
---
|
||
|
||
## 🎯 完成的功能模块
|
||
|
||
### 1. RedcapAdapter (API适配器)
|
||
|
||
**文件**: `backend/src/modules/iit-manager/adapters/RedcapAdapter.ts`
|
||
|
||
**核心功能**:
|
||
- ✅ `testConnection()` - 连接测试
|
||
- ✅ `exportMetadata()` - 导出字段定义(17个字段)
|
||
- ✅ `exportRecords()` - 导出记录(支持全量/增量/指定记录)
|
||
- ✅ `importRecords()` - 导入记录(Phase 2预留)
|
||
|
||
**关键参数**:
|
||
- `baseUrl`: `http://localhost:8080`
|
||
- `apiToken`: `FCB30F9CBD12EE9E8E9B3E3A0106701B`
|
||
- `timeout`: 30秒
|
||
|
||
**实际性能**:
|
||
```
|
||
- exportMetadata: 260-560ms
|
||
- exportRecords (全量): 450-1,400ms
|
||
- exportRecords (增量): 300-800ms
|
||
```
|
||
|
||
---
|
||
|
||
### 2. WebhookController (Webhook接收器)
|
||
|
||
**文件**: `backend/src/modules/iit-manager/controllers/WebhookController.ts`
|
||
|
||
**核心逻辑**:
|
||
```typescript
|
||
1. 立即返回200 OK(<10ms)
|
||
2. 验证project_id是否在配置中
|
||
3. 幂等性检查(防止重复处理)
|
||
4. 记录审计日志(WEBHOOK_RECEIVED)
|
||
5. 推送到质控队列(iit_quality_check)
|
||
```
|
||
|
||
**REDCap DET格式支持**:
|
||
- ✅ 添加了 `application/x-www-form-urlencoded` 解析器
|
||
- ✅ 支持REDCap原生POST格式
|
||
|
||
**Webhook字段**:
|
||
```json
|
||
{
|
||
"project_id": "16",
|
||
"record": "6",
|
||
"instrument": "demographics",
|
||
"redcap_event_name": "",
|
||
"redcap_version": "15.8.0",
|
||
"redcap_url": "http://localhost:8080"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3. SyncManager (轮询管理器)
|
||
|
||
**文件**: `backend/src/modules/iit-manager/services/SyncManager.ts`
|
||
|
||
**核心功能**:
|
||
- ✅ `initScheduledJob()` - 初始化定时任务(每5分钟)
|
||
- ✅ `handlePoll()` - 轮询所有active项目
|
||
- ✅ `manualSync()` - 手动同步指定项目
|
||
- ✅ `fullSync()` - 全量同步
|
||
|
||
**增量同步策略**:
|
||
```typescript
|
||
// 基于lastSyncAt时间戳
|
||
dateRangeBegin: lastSyncAt || createdAt - 24h
|
||
```
|
||
|
||
**审计日志**:
|
||
- `SCHEDULED_SYNC`: 定时轮询完成
|
||
- `MANUAL_SYNC`: 手动同步完成
|
||
- `FULL_SYNC`: 全量同步完成
|
||
|
||
---
|
||
|
||
### 4. Worker注册 (异步任务处理)
|
||
|
||
**文件**: `backend/src/modules/iit-manager/index.ts`
|
||
|
||
**注册的Worker**:
|
||
|
||
1. **iit_redcap_poll** (定时轮询)
|
||
- 队列名称: `iit_redcap_poll`
|
||
- 触发频率: 每5分钟(Cron: `*/5 * * * *`)
|
||
- 并发数: 1
|
||
- 功能: 轮询所有active项目的增量数据
|
||
|
||
2. **iit_quality_check** (质控任务)
|
||
- 队列名称: `iit_quality_check`
|
||
- 触发方式: Webhook/SyncManager推送
|
||
- 功能: 质控逻辑(Phase 1.5实现)
|
||
|
||
**关键修复**:
|
||
- ❌ 初始: `await jobQueue.work('iit:redcap:poll', ...)`
|
||
- ✅ 修复: `jobQueue.process('iit_redcap_poll', ...)`
|
||
- ❌ 初始: 队列名称使用冒号 `iit:quality-check`
|
||
- ✅ 修复: 使用下划线 `iit_quality_check`
|
||
|
||
---
|
||
|
||
### 5. 路由配置
|
||
|
||
**文件**: `backend/src/modules/iit-manager/routes/index.ts`
|
||
|
||
**注册的API端点**:
|
||
|
||
| 端点 | 方法 | 功能 | 状态 |
|
||
|------|------|------|------|
|
||
| `/api/v1/iit/health` | GET | 健康检查 | ✅ |
|
||
| `/api/v1/iit/webhooks/redcap` | POST | DET回调接收 | ✅ |
|
||
| `/api/v1/iit/webhooks/health` | GET | Webhook健康检查 | ✅ |
|
||
| `/api/v1/iit/projects/:id/sync` | POST | 手动同步 | ✅ |
|
||
| `/api/v1/iit/projects/:id/full-sync` | POST | 全量同步 | ✅ |
|
||
|
||
---
|
||
|
||
## 🐛 遇到的问题与解决方案
|
||
|
||
### 问题1: 模块路径解析错误
|
||
|
||
**错误信息**:
|
||
```
|
||
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/common'
|
||
```
|
||
|
||
**原因**: 项目使用相对路径,而不是路径别名 `@/common`
|
||
|
||
**解决方案**:
|
||
```typescript
|
||
// 错误
|
||
import { logger } from '@/common/logging';
|
||
import { jobQueue } from '@/common/jobs';
|
||
|
||
// 正确
|
||
import { logger } from '../../../common/logging/index.js';
|
||
import { jobQueue } from '../../../common/jobs/index.js';
|
||
```
|
||
|
||
**修改文件**: 所有IIT Manager模块的import语句
|
||
|
||
---
|
||
|
||
### 问题2: 数据库字段名称不一致
|
||
|
||
**错误信息**:
|
||
```sql
|
||
ERROR: column "redcapProjectId" does not exist
|
||
```
|
||
|
||
**原因**: Prisma使用camelCase,但PostgreSQL实际使用snake_case
|
||
|
||
**数据库真实情况**:
|
||
```sql
|
||
-- 表名: iit_schema.projects (not IitProject)
|
||
-- 字段名: redcap_project_id (not redcapProjectId)
|
||
```
|
||
|
||
**解决方案**:
|
||
1. ✅ TypeScript代码中继续使用Prisma的camelCase(自动映射)
|
||
2. ✅ 原始SQL语句改为snake_case
|
||
3. ✅ JSON字段使用 `'{}'::jsonb` 类型转换
|
||
|
||
**修改文件**:
|
||
- 测试脚本中的SQL语句
|
||
- 文档中的SQL示例
|
||
|
||
---
|
||
|
||
### 问题3: pg-boss任务名称格式错误
|
||
|
||
**错误信息**:
|
||
```
|
||
Name can only contain alphanumeric characters, underscores, hyphens, or periods
|
||
```
|
||
|
||
**原因**: 初始使用连字符 `-`,但实际测试发现不稳定
|
||
|
||
**团队标准**:
|
||
```typescript
|
||
❌ 'iit:quality-check' // 冒号
|
||
❌ 'iit-quality-check' // 连字符(不稳定)
|
||
✅ 'iit_quality_check' // 下划线(推荐)
|
||
```
|
||
|
||
**修改**:
|
||
- `iit:quality-check` → `iit_quality_check`
|
||
- `iit:redcap:poll` → `iit_redcap_poll`
|
||
|
||
**依据文档**: `AIclinicalresearch\docs\02-通用能力层\Postgres-Only异步任务处理指南.md`
|
||
|
||
---
|
||
|
||
### 问题4: Worker注册方法错误
|
||
|
||
**错误**: 使用了 `await jobQueue.work()`
|
||
|
||
**正确方法**: 使用 `jobQueue.process()`
|
||
|
||
**对比**:
|
||
```typescript
|
||
// ❌ 错误
|
||
await jobQueue.work(
|
||
'iit_redcap_poll',
|
||
{ teamSize: 1, teamConcurrency: 1 },
|
||
async (job) => { ... }
|
||
);
|
||
|
||
// ✅ 正确
|
||
jobQueue.process('iit_redcap_poll', async (job) => {
|
||
// ...
|
||
return { success: true };
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### 问题5: REDCap DET格式不兼容
|
||
|
||
**错误信息**:
|
||
```
|
||
Unsupported Media Type: application/x-www-form-urlencoded
|
||
```
|
||
|
||
**原因**: REDCap DET发送的是表单格式,而不是JSON
|
||
|
||
**解决方案**: 添加Content-Type解析器
|
||
```typescript
|
||
fastify.addContentTypeParser(
|
||
'application/x-www-form-urlencoded',
|
||
{ parseAs: 'string' },
|
||
(req, body, done) => {
|
||
try {
|
||
const params = new URLSearchParams(body as string);
|
||
const parsed: any = {};
|
||
params.forEach((value, key) => {
|
||
parsed[key] = value;
|
||
});
|
||
done(null, parsed);
|
||
} catch (err: any) {
|
||
done(err);
|
||
}
|
||
}
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
### 问题6: Docker网络访问问题
|
||
|
||
**问题**: REDCap容器无法通过 `localhost` 访问宿主机服务
|
||
|
||
**DET URL配置**:
|
||
```
|
||
❌ http://localhost:3001/api/v1/iit/webhooks/redcap
|
||
✅ http://host.docker.internal:3001/api/v1/iit/webhooks/redcap
|
||
```
|
||
|
||
**验证方法**:
|
||
```bash
|
||
docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/health
|
||
# ✅ 成功返回
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ 测试验证
|
||
|
||
### 1. API连接测试
|
||
|
||
**测试脚本**: `test-redcap-api.ts`
|
||
|
||
**结果**:
|
||
```
|
||
✅ 连接成功
|
||
✅ 元数据导出: 17个字段
|
||
✅ 记录导出: 6条记录 (ID: 1,2,3,4,5,6)
|
||
✅ 增量同步: 成功获取最近1小时数据
|
||
✅ 指定记录: 成功导出单条记录
|
||
```
|
||
|
||
---
|
||
|
||
### 2. Webhook接收测试
|
||
|
||
**测试脚本**: `test-redcap-webhook.ts`
|
||
|
||
**结果**:
|
||
```
|
||
✅ 健康检查: ok
|
||
✅ Webhook发送成功: 200
|
||
✅ 响应时间: <10ms (优秀)
|
||
✅ 幂等性检查: 通过
|
||
✅ 无效请求拦截: 400错误
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 集成测试
|
||
|
||
**测试脚本**: `test-redcap-integration.ts`
|
||
|
||
**结果**: **12/12 通过** ✅
|
||
|
||
```
|
||
✅ 1. 后端服务运行正常
|
||
✅ 2. 项目配置存在
|
||
✅ 3. REDCap API连接成功
|
||
✅ 4. 元数据获取成功
|
||
✅ 5. 记录获取成功
|
||
✅ 6. Webhook响应速度<100ms
|
||
✅ 7. 异步处理等待完成
|
||
✅ 8. 审计日志记录完整
|
||
✅ 9. 增量同步成功
|
||
✅ 10. 手动同步成功 ⭐
|
||
✅ 11. Webhook健康检查
|
||
✅ 12. 幂等性测试通过
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 真实场景测试
|
||
|
||
**操作**:
|
||
1. 在REDCap中新增记录ID 5
|
||
2. 在REDCap中编辑记录ID 1
|
||
3. 在REDCap中新增记录ID 6
|
||
4. 在REDCap中编辑记录ID 4
|
||
|
||
**Webhook接收日志**:
|
||
```sql
|
||
action_type | entity_id | details
|
||
------------------+-----------+--------------------------------------------------
|
||
WEBHOOK_RECEIVED | 4 | instrument: ddcd, project_id: 16
|
||
WEBHOOK_RECEIVED | 6 | instrument: demographics, project_id: 16
|
||
```
|
||
|
||
**验证结果**: ✅ 数据完全一致
|
||
|
||
---
|
||
|
||
## 📊 性能指标
|
||
|
||
### API响应时间
|
||
|
||
| 操作 | 耗时 | 数据量 |
|
||
|------|------|--------|
|
||
| exportMetadata | 260-560ms | 17个字段 |
|
||
| exportRecords (全量) | 450-1,400ms | 6条记录 |
|
||
| exportRecords (增量) | 300-800ms | 变化记录 |
|
||
| Webhook响应 | **<10ms** | 立即返回 |
|
||
|
||
### 实时性验证
|
||
|
||
| 指标 | 目标 | 实际 | 状态 |
|
||
|------|------|------|------|
|
||
| Webhook响应时间 | <100ms | **7ms** | ✅ 优秀 |
|
||
| DET触发延迟 | 0秒 | **0秒** | ✅ 完美 |
|
||
| 数据同步准确性 | 100% | **100%** | ✅ 完美 |
|
||
|
||
---
|
||
|
||
## 🗄️ 数据库配置
|
||
|
||
### REDCap项目配置
|
||
|
||
**数据库表**: `iit_schema.projects`
|
||
|
||
```sql
|
||
INSERT INTO iit_schema.projects (
|
||
id,
|
||
name,
|
||
redcap_project_id,
|
||
redcap_api_token,
|
||
redcap_base_url,
|
||
status,
|
||
settings,
|
||
created_at,
|
||
updated_at
|
||
) VALUES (
|
||
'40062738-2eb5-472f-8a36-e098f5c2f9b9',
|
||
'test0102',
|
||
'16',
|
||
'FCB30F9CBD12EE9E8E9B3E3A0106701B',
|
||
'http://localhost:8080',
|
||
'active',
|
||
'{}'::jsonb,
|
||
NOW(),
|
||
NOW()
|
||
);
|
||
```
|
||
|
||
### REDCap项目信息
|
||
|
||
| 字段 | 值 |
|
||
|------|-----|
|
||
| **项目名称** | test0102 |
|
||
| **Project ID** | 16 (int) |
|
||
| **REDCap URL** | http://localhost:8080/redcap_v15.8.0 |
|
||
| **API Token** | FCB30F9CBD12EE9E8E9B3E3A0106701B |
|
||
| **DET URL** | http://host.docker.internal:3001/api/v1/iit/webhooks/redcap |
|
||
|
||
### 表单结构
|
||
|
||
**1. demographics (基本信息)**
|
||
- record_id, first_name, last_name, address
|
||
- telephone, email, dob, age
|
||
- ethnicity, race, sex
|
||
- height, weight, bmi
|
||
- comments, demographics_complete
|
||
|
||
**2. ddcd (自定义表单)**
|
||
- zhiliaoshi (治疗室)
|
||
- shifou (是否)
|
||
- ddcd_complete
|
||
|
||
---
|
||
|
||
## 📝 审计日志示例
|
||
|
||
### Webhook接收日志
|
||
```json
|
||
{
|
||
"action_type": "WEBHOOK_RECEIVED",
|
||
"entity_id": "6",
|
||
"details": {
|
||
"record": "6",
|
||
"source": "redcap_det",
|
||
"instrument": "demographics",
|
||
"project_id": "16"
|
||
},
|
||
"created_at": "2026-01-02 09:50:32"
|
||
}
|
||
```
|
||
|
||
### 同步完成日志
|
||
```json
|
||
{
|
||
"action_type": "SCHEDULED_SYNC",
|
||
"entity_id": "40062738-2eb5-472f-8a36-e098f5c2f9b9",
|
||
"details": {
|
||
"source": "sync_manager",
|
||
"duration": 447,
|
||
"recordCount": 3,
|
||
"uniqueRecordCount": 3
|
||
},
|
||
"created_at": "2026-01-02 09:20:17"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎓 经验总结
|
||
|
||
### 1. 技术选型验证
|
||
|
||
**✅ REDCap DET + REST API方案优势**:
|
||
- 零开发成本(REDCap原生支持)
|
||
- 零延迟(实时触发)
|
||
- 高可靠(Webhook + 轮询双保险)
|
||
- 易维护(标准HTTP接口)
|
||
|
||
**❌ 放弃的方案**:
|
||
- External Module(需要PHP开发)
|
||
- 纯轮询(延迟高,资源浪费)
|
||
|
||
---
|
||
|
||
### 2. 开发规范遵循
|
||
|
||
**✅ 符合团队标准**:
|
||
- 队列名称: 使用下划线(`iit_quality_check`)
|
||
- Worker注册: 使用 `jobQueue.process()`
|
||
- 审计日志: 记录所有关键操作
|
||
- 错误处理: 统一异常捕获和日志记录
|
||
|
||
**参考文档**:
|
||
- `Postgres-Only异步任务处理指南.md`
|
||
- `REDCap对接技术方案与实施指南.md`
|
||
|
||
---
|
||
|
||
### 3. 调试技巧
|
||
|
||
**Docker容器调试**:
|
||
```bash
|
||
# 测试网络连通性
|
||
docker exec redcap-apache curl http://host.docker.internal:3001/api/v1/iit/health
|
||
|
||
# 查询审计日志
|
||
docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research \
|
||
-c "SELECT * FROM iit_schema.audit_logs ORDER BY created_at DESC LIMIT 5;"
|
||
|
||
# 查询项目配置
|
||
docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research \
|
||
-c "SELECT name, redcap_project_id, status FROM iit_schema.projects;"
|
||
```
|
||
|
||
**REDCap数据库查询**:
|
||
```bash
|
||
docker exec redcap-mysql mysql -u redcap_user -predcap_pass_dev_456 redcap \
|
||
-e "SELECT project_id, app_title FROM redcap_projects WHERE project_id = 16;"
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 相关文档
|
||
|
||
| 文档名称 | 路径 | 说明 |
|
||
|---------|------|------|
|
||
| REDCap对接技术方案 | `04-开发计划/REDCap对接技术方案与实施指南.md` | Day 2技术方案 |
|
||
| MVP开发任务清单 | `04-开发计划/MVP开发任务清单.md` | 整体开发计划 |
|
||
| Postgres-Only异步任务处理指南 | `docs/02-通用能力层/Postgres-Only异步任务处理指南.md` | 队列开发规范 |
|
||
| 模块当前状态 | `00-模块当前状态与开发指南.md` | 模块整体状态 |
|
||
|
||
---
|
||
|
||
## 🚀 下一步计划
|
||
|
||
### Phase 1.5 - 质控逻辑实现
|
||
|
||
**目标**: 实现AI驱动的数据质控
|
||
|
||
**核心功能**:
|
||
1. 实现 `iit_quality_check` Worker的质控逻辑
|
||
2. 调用Dify工作流进行数据验证
|
||
3. 生成质控建议(shadow state)
|
||
4. 返回结果给研究者
|
||
|
||
**预估工作量**: 1-2天
|
||
|
||
---
|
||
|
||
### Phase 2 - 双向同步
|
||
|
||
**目标**: 实现AI建议回写到REDCap
|
||
|
||
**核心功能**:
|
||
1. 完善 `importRecords()` API
|
||
2. 实现shadow state审批流程
|
||
3. 经研究者确认后同步到REDCap
|
||
4. 处理冲突和版本控制
|
||
|
||
**预估工作量**: 2-3天
|
||
|
||
---
|
||
|
||
## ✅ 验收清单
|
||
|
||
- [x] RedcapAdapter API适配器完成
|
||
- [x] WebhookController Webhook接收器完成
|
||
- [x] SyncManager 轮询管理器完成
|
||
- [x] Worker注册和队列配置完成
|
||
- [x] 路由配置和Content-Type解析完成
|
||
- [x] 单元测试脚本通过(test-redcap-api.ts)
|
||
- [x] Webhook测试通过(test-redcap-webhook.ts)
|
||
- [x] 集成测试通过(test-redcap-integration.ts,12/12)
|
||
- [x] 真实场景测试通过(新增+编辑记录)
|
||
- [x] 审计日志记录完整
|
||
- [x] 性能指标达标(Webhook<10ms)
|
||
- [x] 符合团队开发规范
|
||
- [x] 文档更新完成
|
||
|
||
---
|
||
|
||
## 📌 附录
|
||
|
||
### A. 环境信息
|
||
|
||
```yaml
|
||
开发环境:
|
||
- OS: Windows 11
|
||
- Node.js: v22.18.0
|
||
- PostgreSQL: 16.1 (Docker)
|
||
- REDCap: 15.8.0 (Docker)
|
||
- Backend: Fastify + Prisma
|
||
- 队列: pg-boss
|
||
|
||
Docker容器:
|
||
- redcap-apache: REDCap应用
|
||
- redcap-mysql: REDCap数据库
|
||
- ai-clinical-postgres: IIT数据库
|
||
```
|
||
|
||
### B. 关键代码文件清单
|
||
|
||
```
|
||
backend/src/modules/iit-manager/
|
||
├── adapters/
|
||
│ └── RedcapAdapter.ts (271行)
|
||
├── controllers/
|
||
│ └── WebhookController.ts (327行)
|
||
├── services/
|
||
│ └── SyncManager.ts (398行)
|
||
├── routes/
|
||
│ └── index.ts (203行)
|
||
├── index.ts (91行)
|
||
├── test-redcap-api.ts (189行)
|
||
├── test-redcap-webhook.ts (274行)
|
||
└── test-redcap-integration.ts (449行)
|
||
```
|
||
|
||
**总代码量**: ~2,200行
|
||
|
||
---
|
||
|
||
**文档维护者**: 开发团队
|
||
**最后更新**: 2026-01-02
|
||
**状态**: ✅ Day 2 开发完成
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|